From 45f3216ed86bb3f8c7535011a7e4ee6613917518 Mon Sep 17 00:00:00 2001 From: Reuh Date: Sat, 5 Sep 2015 19:00:36 +0200 Subject: [PATCH 001/101] Updated font and fs lib; updated editor; updated sf2dlib Libs additions: font.load, font:unload, font:getWidth, fs.getDirectory, fs.setDirectory, fs.exists Editor additions: syntaxic coloring and mono font sf2dlib update: you will need the latest version of ctrulib. Also, because of the lib font needs, the sftdlib was modified. --- libs/sf2dlib/libsf2d/source/sf2d.c | 2 +- libs/sf2dlib/libsf2d/source/sf2d_texture.c | 8 +- libs/sftdlib/libsftd/include/sftd.h | 2 + libs/sftdlib/libsftd/source/sftd.c | 63 ++++++++++++- sdcard/3ds/ctruLua/editor/VeraMono.ttf | Bin 0 -> 49224 bytes sdcard/3ds/ctruLua/editor/color.lua | 16 +++- sdcard/3ds/ctruLua/editor/main.lua | 52 +++++++--- sdcard/3ds/ctruLua/editor/syntax.lua | 90 ++++++++++++++++++ sdcard/3ds/ctruLua/main.lua | 10 +- sdcard/3ds/ctruLua/openfile.lua | 3 + source/font.c | 105 ++++++++++++++++++++- source/font.h | 8 ++ source/fs.c | 38 +++++++- source/gfx.c | 13 ++- 14 files changed, 382 insertions(+), 28 deletions(-) create mode 100644 sdcard/3ds/ctruLua/editor/VeraMono.ttf create mode 100644 sdcard/3ds/ctruLua/editor/syntax.lua create mode 100644 source/font.h diff --git a/libs/sf2dlib/libsf2d/source/sf2d.c b/libs/sf2dlib/libsf2d/source/sf2d.c index 0c23c65..6f564fa 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d.c +++ b/libs/sf2dlib/libsf2d/source/sf2d.c @@ -151,7 +151,7 @@ void sf2d_start_frame(gfxScreen_t screen, gfx3dSide_t side) GPU_DepthMap(-1.0f, 0.0f); GPU_SetFaceCulling(GPU_CULL_NONE); GPU_SetStencilTest(false, GPU_ALWAYS, 0x00, 0xFF, 0x00); - GPU_SetStencilOp(GPU_KEEP, GPU_KEEP, GPU_KEEP); + GPU_SetStencilOp(GPU_STENCIL_KEEP, GPU_STENCIL_KEEP, GPU_STENCIL_KEEP); GPU_SetBlendingColor(0,0,0,0); GPU_SetDepthTestAndWriteMask(true, GPU_GEQUAL, GPU_WRITE_ALL); GPUCMD_AddMaskedWrite(GPUREG_0062, 0x1, 0); diff --git a/libs/sf2dlib/libsf2d/source/sf2d_texture.c b/libs/sf2dlib/libsf2d/source/sf2d_texture.c index c9368c7..af387cf 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d_texture.c +++ b/libs/sf2dlib/libsf2d/source/sf2d_texture.c @@ -78,7 +78,13 @@ sf2d_texture *sf2d_create_texture(int width, int height, sf2d_texfmt pixel_forma texture->data_size = data_size; texture->data = data; - memset(texture->data, 0, texture->data_size); + if (place == SF2D_PLACE_VRAM) { + GX_SetMemoryFill(NULL, texture->data, 0x00000000, (u32*)&((u8*)texture->data)[texture->data_size], GX_FILL_TRIGGER | GX_FILL_32BIT_DEPTH, + NULL, 0x00000000, NULL, 0); + gspWaitForPSC0(); + } else { + memset(texture->data, 0, texture->data_size); + } return texture; } diff --git a/libs/sftdlib/libsftd/include/sftd.h b/libs/sftdlib/libsftd/include/sftd.h index d1a75f6..54fe73a 100644 --- a/libs/sftdlib/libsftd/include/sftd.h +++ b/libs/sftdlib/libsftd/include/sftd.h @@ -105,6 +105,8 @@ void sftd_draw_wtext(sftd_font *font, int x, int y, unsigned int color, unsigned */ void sftd_draw_wtextf(sftd_font *font, int x, int y, unsigned int color, unsigned int size, const wchar_t *text, ...); +// (ctruLua addition) Based on sftd_draw_wtext, returns the width of the text drawn. +int sftd_width_wtext(sftd_font *font, unsigned int size, const wchar_t *text); #ifdef __cplusplus } diff --git a/libs/sftdlib/libsftd/source/sftd.c b/libs/sftdlib/libsftd/source/sftd.c index dd9ea02..5b98af4 100644 --- a/libs/sftdlib/libsftd/source/sftd.c +++ b/libs/sftdlib/libsftd/source/sftd.c @@ -228,7 +228,7 @@ void sftd_draw_text(sftd_font *font, int x, int y, unsigned int color, unsigned &rect, &bitmap_left, &bitmap_top, &advance_x, &advance_y, &glyph_size); - const float draw_scale = glyph_size/(float)size; + const float draw_scale = size/(float)glyph_size; sf2d_draw_texture_part_scale_blend(font->tex_atlas->tex, pen_x + bitmap_left * draw_scale, @@ -304,7 +304,7 @@ void sftd_draw_wtext(sftd_font *font, int x, int y, unsigned int color, unsigned &rect, &bitmap_left, &bitmap_top, &advance_x, &advance_y, &glyph_size); - const float draw_scale = (float)size/glyph_size; + const float draw_scale = size/(float)glyph_size; sf2d_draw_texture_part_scale_blend(font->tex_atlas->tex, pen_x + bitmap_left * draw_scale, @@ -332,3 +332,62 @@ void sftd_draw_wtextf(sftd_font *font, int x, int y, unsigned int color, unsigne va_end(args); } +// (ctruLua addition) Based on sftd_draw_wtext, returns the width of the text drawn. +int sftd_width_wtext(sftd_font *font, unsigned int size, const wchar_t *text) +{ + FTC_FaceID face_id = (FTC_FaceID)font; + FT_Face face; + FTC_Manager_LookupFace(ftcmanager, face_id, &face); + + FT_Int charmap_index; + charmap_index = FT_Get_Charmap_Index(face->charmap); + + FT_Glyph glyph; + FT_Bool use_kerning = FT_HAS_KERNING(face); + FT_UInt glyph_index, previous = 0; + int pen_x = 0; + + FTC_ScalerRec scaler; + scaler.face_id = face_id; + scaler.width = size; + scaler.height = size; + scaler.pixel = 1; + + FT_ULong flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL; + + while (*text) { + glyph_index = FTC_CMapCache_Lookup(font->cmapcache, (FTC_FaceID)font, charmap_index, *text); + + if (use_kerning && previous && glyph_index) { + FT_Vector delta; + FT_Get_Kerning(face, previous, glyph_index, FT_KERNING_DEFAULT, &delta); + pen_x += delta.x >> 6; + } + + if (!texture_atlas_exists(font->tex_atlas, glyph_index)) { + FTC_ImageCache_LookupScaler(font->imagecache, &scaler, flags, glyph_index, &glyph, NULL); + + if (!atlas_add_glyph(font->tex_atlas, glyph_index, (FT_BitmapGlyph)glyph, size)) { + continue; + } + } + + bp2d_rectangle rect; + int bitmap_left, bitmap_top; + int advance_x, advance_y; + int glyph_size; + + texture_atlas_get(font->tex_atlas, glyph_index, + &rect, &bitmap_left, &bitmap_top, + &advance_x, &advance_y, &glyph_size); + + const float draw_scale = size/(float)glyph_size; + + pen_x += (advance_x >> 16) * draw_scale; + + previous = glyph_index; + text++; + } + + return pen_x; +} \ No newline at end of file diff --git a/sdcard/3ds/ctruLua/editor/VeraMono.ttf b/sdcard/3ds/ctruLua/editor/VeraMono.ttf new file mode 100644 index 0000000000000000000000000000000000000000..139f0b4311ad2e0369a347b3be6c46e6c2b730d5 GIT binary patch literal 49224 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 literal 0 HcmV?d00001 diff --git a/sdcard/3ds/ctruLua/editor/color.lua b/sdcard/3ds/ctruLua/editor/color.lua index c162160..e29506a 100644 --- a/sdcard/3ds/ctruLua/editor/color.lua +++ b/sdcard/3ds/ctruLua/editor/color.lua @@ -1,6 +1,16 @@ -- Colors based on the Monokai theme return { - background = 0x272822FF, - default = 0xF8F8F2FF, - cursor = 0xFF0000FF + -- General + ["background"] = 0x272822FF, + ["cursor"] = 0xF8F8F0FF, + ["default"] = 0xF8F8F2FF, + + -- Syntax + ["comment"] = 0x75715EFF, + ["string"] = 0xE6DB74FF, + ["constant.numeric"] = 0xAE81FFFF, + ["constant.language"] = 0xAE81FFFF, + ["keyword.control"] = 0xF92672FF, + ["keyword.operator"] = 0xF92672FF, + ["support.function"] = 0x66D9EFFF } \ No newline at end of file diff --git a/sdcard/3ds/ctruLua/editor/main.lua b/sdcard/3ds/ctruLua/editor/main.lua index 1bf70ed..ec14bad 100644 --- a/sdcard/3ds/ctruLua/editor/main.lua +++ b/sdcard/3ds/ctruLua/editor/main.lua @@ -5,27 +5,45 @@ local gfx = require("ctr.gfx") -- Open libs local keyboard = dofile("sdmc:/3ds/ctruLua/keyboard.lua") local openfile = dofile("sdmc:/3ds/ctruLua/openfile.lua") -local color = dofile("sdmc:/3ds/ctruLua/editor/color.lua") +local color = dofile("color.lua") +local syntax = dofile("syntax.lua") + +-- Load data +local font = gfx.font.load("VeraMono.ttf") -- Open file local path, status = openfile("Choose a file to edit", "/3ds/ctruLua/", nil, "any") if not path then return end +local lineEnding local lines = {} if status == "exist" then - for line in io.lines(path) do table.insert(lines, line) end + for line in io.lines(path, "L") do + if not lineEnding then lineEnding = line:match("([\n\r]+)$") end + table.insert(lines, line:match("^(.-)[\n\r]*$")) + end else + lineEnding = "\n" lines = { "" } end +-- Syntax coloring +local coloredLines = syntax(lines, color) + -- Variables local lineHeight = 10 local cursorX, cursorY = 1, 1 local scrollX, scrollY = 0, 0 +-- Helper functions +local function displayedText(text) + return text:gsub("\t", " ") +end + -- Set defaults gfx.set3D(false) gfx.color.setDefault(color.default) gfx.color.setBackground(color.background) +gfx.font.setDefault(font) while ctr.run() do hid.read() @@ -75,10 +93,10 @@ while ctr.run() do until t + 5 < os.time() else for i = 1, #lines, 1 do - file:write(lines[i].."\n") + file:write(lines[i]..lineEnding) gfx.startFrame(gfx.GFX_TOP) gfx.rectangle(0, 0, math.ceil(i/#lines*gfx.TOP_WIDTH), gfx.TOP_HEIGHT, 0, 0xFFFFFFFF) - gfx.color.setDefault(0x000000FF) + gfx.color.setDefault(color.background) gfx.text(gfx.TOP_WIDTH/2, gfx.TOP_HEIGHT/2, math.ceil(i/#lines*100).."%") gfx.color.setDefault(color.default) gfx.endFrame() @@ -122,6 +140,7 @@ while ctr.run() do -- Draw gfx.startFrame(gfx.GFX_TOP) + -- Lines local sI = math.floor(scrollY / lineHeight) if sI < 1 then sI = 1 end @@ -129,21 +148,30 @@ while ctr.run() do if eI > #lines then eI = #lines end for i = sI, eI, 1 do - local line = lines[i] + local x = -scrollX local y = -scrollY+ (i-1)*lineHeight - - if cursorY == i then - gfx.color.setDefault(color.cursor) - gfx.text(-scrollX, y, line:sub(1, (utf8.offset(line, cursorX) or 0)-1):gsub("\t", " ").."|", nil) -- TODO: color doesn't work + + for _,colored in ipairs(coloredLines[i]) do + local str = displayedText(colored[1]) + + gfx.color.setDefault(colored[2]) + gfx.text(x, y, str) gfx.color.setDefault(color.default) + + x = x + font:width(str) end - - gfx.text(-scrollX, y, line:gsub("\t", " "), nil) end + -- Cursor + local curline = lines[cursorY] + gfx.rectangle(-scrollX+ font:width(displayedText(curline:sub(1, (utf8.offset(curline, cursorX) or 0)-1))), + -scrollY+ (cursorY-1)*lineHeight, 1, lineHeight, 0, color.cursor) + gfx.endFrame() gfx.startFrame(gfx.GFX_BOTTOM) + + gfx.text(3, 3, "FPS: "..math.ceil(gfx.getFPS())) keyboard.draw(5, 115) @@ -151,3 +179,5 @@ while ctr.run() do gfx.render() end + +font:unload() \ No newline at end of file diff --git a/sdcard/3ds/ctruLua/editor/syntax.lua b/sdcard/3ds/ctruLua/editor/syntax.lua new file mode 100644 index 0000000..bebe3f5 --- /dev/null +++ b/sdcard/3ds/ctruLua/editor/syntax.lua @@ -0,0 +1,90 @@ +-- Each pattern should return 3 captures : start position, the string to colorize, and the end position. +local syntax = { + { "comment", { "()(%-%-.*)()$" } }, + + --["string"] = { "()(%'.*%f[%\\]%')()", "()(%\".*%f[%\\]%\")()" }, + { "string", { "()(%'[^%']*%')()", "()(%\"[^%\"]*%\")()" } }, + + { "constant.numeric", { + "%f[%d%w%.]()(0x[a-fA-F%d]+)()%f[^%d%w%.]", + "%f[%d%w%.]()([%d% ]+%.[%d% ]+)()%f[^%d%w%.]", + "%f[%d%w%.]()([%d% ]+)()%f[^%d%w%.]" + } + }, + + { "constant.language", { + "%f[%w]()(false)()%f[%W]", "%f[%w]()(nil)()%f[%W]", "%f[%w]()(true)()%f[%W]", "%f[%w]()(_G)()%f[%W]", + "%f[%w]()(_VERSION)()%f[%W]", "%f[%w]()(math.pi)()%f[%W]", "%f[%w]()(math.huge)()%f[%W]", "%f[%w]()(%.%.%.)()%f[%W]" + } + }, + + { "keyword.control", { + "%f[%w]()(break)()%f[%W]", "%f[%w]()(goto)()%f[%W]", "%f[%w]()(do)()%f[%W]", "%f[%w]()(else)()%f[%W]", + "%f[%w]()(for)()%f[%W]", "%f[%w]()(if)()%f[%W]", "%f[%w]()(elseif)()%f[%W]", "%f[%w]()(return)()%f[%W]", + "%f[%w]()(then)()%f[%W]", "%f[%w]()(repeat)()%f[%W]", "%f[%w]()(while)()%f[%W]", "%f[%w]()(until)()%f[%W]", + "%f[%w]()(end)()%f[%W]", "%f[%w]()(function)()%f[%W]", "%f[%w]()(local)()%f[%W]", "%f[%w]()(in)()%f[%W]" + } + }, + + { "keyword.operator", { + "%f[%w]()(and)()%f[%W]", "%f[%w]()(or)()%f[%W]", "%f[%w]()(not)()%f[%W]", + "()(%+)()", "()(%-)()", "()(%%)()", "()(%#)()", "()(%*)()", "()(%/%/?)()", "()(%^)()", "()(%=%=?)()", "()(%~%=?)()", + "()(%.%.)()", "()(%<%=?)()", "()(%>%=?)()", "()(%&)()", "()(%|)()", "()(%<%<)()", "()(%>%>)()", + } + }, + + { "support.function", { + "[^%.%:]()(assert)()[%( %{]", "[^%.%:]()(collectgarbage)()[%( %{]", "[^%.%:]()(dofile)()[%( %{]", + "[^%.%:]()(error)()[%( %{]", "[^%.%:]()(getfenv)()[%( %{]", "[^%.%:]()(getmetatable)()[%( %{]", + "[^%.%:]()(ipairs)()[%( %{]", "[^%.%:]()loadfile)()[%( %{]", "[^%.%:]()(loadstring)()[%( %{]", + "[^%.%:]()(module)()[%( %{]", "[^%.%:]()(next)()[%( %{]", "[^%.%:]()(pairs)()[%( %{]", + "[^%.%:]()(pcall)()[%( %{]", "[^%.%:]()(print)()[%( %{]", "[^%.%:]()(rawequal)()[%( %{]", + "[^%.%:]()(rawget)()[%( %{]", "[^%.%:]()(rawset)()[%( %{]", "[^%.%:]()(require)()[%( %{]", + "[^%.%:]()(select)()[%( %{]", "[^%.%:]()(setfenv)()[%( %{]", "[^%.%:]()(setmetatable)()[%( %{]", + "[^%.%:]()(tonumber)()[%( %{]", "[^%.%:]()(tostring)()[%( %{]", "[^%.%:]()(type)()[%( %{]", + "[^%.%:]()(unpack)()[%( %{]", "[^%.%:]()(xpcall)()[%( %{]" + } + } +} + +return function(lines, color) + local ret = {} + + for _,line in ipairs(lines) do + local colored = { { line, color.default } } + + for _, patterns in ipairs(syntax) do + local name = patterns[1] + + for _, pattern in ipairs(patterns[2]) do + local i = 1 + while i <= #colored do + local oldcolor = colored[i][2] + + if oldcolor == color.default then + local part = colored[i][1] + + local starti, match, endi = part:match(pattern) + if starti then + table.remove(colored, i) + if starti > 1 then + table.insert(colored, i, { part:sub(1, starti-1), oldcolor }) + i = i + 1 + end + table.insert(colored, i, { match, color[name] or color.default }) + if endi <= #part then + table.insert(colored, i+1, { part:sub(endi, -1), oldcolor }) + end + end + end + + i = i + 1 + end + end + end + + table.insert(ret, colored) + end + + return ret +end \ No newline at end of file diff --git a/sdcard/3ds/ctruLua/main.lua b/sdcard/3ds/ctruLua/main.lua index c4cffda..560a7f6 100644 --- a/sdcard/3ds/ctruLua/main.lua +++ b/sdcard/3ds/ctruLua/main.lua @@ -1,4 +1,10 @@ +local fs = require("ctr.fs") + repeat - local file = dofile("sdmc:/3ds/ctruLua/openfile.lua")("Choose a Lua file to execute", "/3ds/ctruLua/", ".lua", "exist") - if file then dofile(file) end + fs.setDirectory("sdmc:/3ds/ctruLua") + local file = dofile("openfile.lua")("Choose a Lua file to execute", "/3ds/ctruLua/", ".lua", "exist") + if file then + fs.setDirectory(file:match("^(.-)[^/]*$")) + dofile(file) + end until not file \ No newline at end of file diff --git a/sdcard/3ds/ctruLua/openfile.lua b/sdcard/3ds/ctruLua/openfile.lua index 82a04a4..c913944 100644 --- a/sdcard/3ds/ctruLua/openfile.lua +++ b/sdcard/3ds/ctruLua/openfile.lua @@ -29,9 +29,11 @@ return function(title, curdir, exts, type) --local was3D = gfx.get3D() TODO: implement this thing in ctruLua local wasDefault = gfx.color.getDefault() local wasBackground = gfx.color.getBackground() + local wasFont = gfx.font.getDefault() gfx.set3D(false) gfx.color.setDefault(0xFFFFFFFF) gfx.color.setBackground(0x000000FF) + gfx.font.setDefault() while ctr.run() do ctr.hid.read() @@ -130,6 +132,7 @@ return function(title, curdir, exts, type) --gfx.set3D(was3D) gfx.color.setDefault(wasDefault) gfx.color.setBackground(wasBackground) + gfx.font.setDefault(wasFont) if ret then return table.unpack(ret) diff --git a/source/font.c b/source/font.c index 8324e03..c61db9e 100644 --- a/source/font.c +++ b/source/font.c @@ -1,27 +1,126 @@ +#include +#include + #include #include "vera_ttf.h" #include #include -sftd_font *font_default; +#include "font.h" +static int font_load(lua_State *L) { + const char *path = luaL_checkstring(L, 1); + + font_userdata *font = lua_newuserdata(L, sizeof(*font)); + luaL_getmetatable(L, "LFont"); + lua_setmetatable(L, -2); + + font->font = sftd_load_font_file(path); + + // SFTD doesn't actually check if the file exist, so we have to do this ourselves. + if (font->font == NULL || access(path, F_OK) != 0) { + lua_pushnil(L); + lua_pushfstring(L, "No valid font file at %s", path); + return 2; + } + + return 1; +} + +static int font_setDefault(lua_State *L) { + if (luaL_testudata(L, 1, "LFont") == NULL) { + font_userdata *font = lua_newuserdata(L, sizeof(*font)); + luaL_getmetatable(L, "LFont"); + lua_setmetatable(L, -2); + + font->font = sftd_load_font_mem(vera_ttf, vera_ttf_size); + } + + lua_setfield(L, LUA_REGISTRYINDEX, "LFontDefault"); + + return 0; +} + +static int font_getDefault(lua_State *L) { + lua_getfield(L, LUA_REGISTRYINDEX, "LFontDefault"); + + return 1; +} + +static int font_object_width(lua_State *L) { + font_userdata *font = luaL_checkudata(L, 1, "LFont"); + if (font->font == NULL) luaL_error(L, "The font object was unloaded"); + + size_t len; + const char *text = luaL_checklstring(L, 2, &len); + + int size = luaL_optinteger(L, 3, 9); + + // Wide caracters support. (wchar = UTF32 on 3DS.) + wchar_t wtext[len]; + len = mbstowcs(wtext, text, len); + *(wtext+len) = 0x0; // text end + + lua_pushinteger(L, sftd_width_wtext(font->font, size, wtext)); + + return 1; +} + +static int font_object_unload(lua_State *L) { + font_userdata *font = luaL_checkudata(L, 1, "LFont"); + if (font->font == NULL) return 0; + + sftd_free_font(font->font); + font->font = NULL; + + return 0; +} + +// Font object methods +static const struct luaL_Reg font_object_methods[] = { + { "width", font_object_width }, + { "unload", font_object_unload }, + { "__gc", font_object_unload }, + { NULL, NULL } +}; + +// Library functions static const struct luaL_Reg font_lib[] = { + { "load", font_load }, + { "setDefault", font_setDefault }, + { "getDefault", font_getDefault }, { NULL, NULL } }; int luaopen_font_lib(lua_State *L) { + luaL_newmetatable(L, "LFont"); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + luaL_setfuncs(L, font_object_methods, 0); + luaL_newlib(L, font_lib); return 1; } void load_font_lib(lua_State *L) { - font_default = sftd_load_font_mem(vera_ttf, vera_ttf_size); // Load default font + // Load default font + font_userdata *font = lua_newuserdata(L, sizeof(*font)); + luaL_getmetatable(L, "LFont"); + lua_setmetatable(L, -2); + font->font = sftd_load_font_mem(vera_ttf, vera_ttf_size); + + lua_setfield(L, LUA_REGISTRYINDEX, "LFontDefault"); + + // Load lib luaL_requiref(L, "ctr.gfx.font", luaopen_font_lib, false); } void unload_font_lib(lua_State *L) { - sftd_free_font(font_default); // Unload current font + lua_getfield(L, LUA_REGISTRYINDEX, "LFontDefault"); + + if (luaL_testudata(L, -1, "LFont") != NULL) + sftd_free_font(((font_userdata *)lua_touserdata(L, -1))->font); // Unload current font } \ No newline at end of file diff --git a/source/font.h b/source/font.h new file mode 100644 index 0000000..888176c --- /dev/null +++ b/source/font.h @@ -0,0 +1,8 @@ +#ifndef FONT_H +#define FONT_H + +typedef struct { + sftd_font *font; +} font_userdata; + +#endif diff --git a/source/fs.c b/source/fs.c index c0b0e80..a568cb7 100644 --- a/source/fs.c +++ b/source/fs.c @@ -1,3 +1,5 @@ +#include + #include <3ds/types.h> #include <3ds/util/utf.h> #include <3ds/services/fs.h> @@ -8,7 +10,7 @@ Handle *fsuHandle; FS_archive sdmcArchive; -int fs_list(lua_State *L) { +static int fs_list(lua_State *L) { const char *path = luaL_checkstring(L, 1); lua_newtable(L); @@ -60,8 +62,40 @@ int fs_list(lua_State *L) { return 1; } +static int fs_exists(lua_State *L) { + const char *path = luaL_checkstring(L, 1); + + lua_pushboolean(L, access(path, F_OK) == 0); + + return 1; +} + +static int fs_getDirectory(lua_State *L) { + char cwd[256]; + + lua_pushstring(L, getcwd(cwd, 256)); + + return 1; +} + +static int fs_setDirectory(lua_State *L) { + const char *path = luaL_checkstring(L, 1); + + int result = chdir(path); + + if (result == 0) + lua_pushboolean(L, true); + else + lua_pushboolean(L, false); + + return 1; +} + static const struct luaL_Reg fs_lib[] = { - { "list", fs_list }, + { "list", fs_list }, + { "exists", fs_exists }, + { "getDirectory", fs_getDirectory }, + { "setDirectory", fs_setDirectory }, { NULL, NULL } }; diff --git a/source/gfx.c b/source/gfx.c index da360f8..4dc4248 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -8,6 +8,8 @@ #include #include +#include "font.h" + bool isGfxInitialised = false; void load_color_lib(lua_State *L); @@ -18,7 +20,6 @@ void load_map_lib(lua_State *L); void unload_font_lib(lua_State *L); u32 color_default; -sftd_font *font_default; static int gfx_startFrame(lua_State *L) { u8 screen = luaL_checkinteger(L, 1); @@ -124,14 +125,20 @@ static int gfx_text(lua_State *L) { int size = luaL_optinteger(L, 4, 9); u32 color = luaL_optinteger(L, 5, color_default); - // todo : font selection + font_userdata *font = luaL_testudata(L, 6, "LFont"); + if (font == NULL) { + lua_getfield(L, LUA_REGISTRYINDEX, "LFontDefault"); + font = luaL_testudata(L, -1, "LFont"); + if (font == NULL) luaL_error(L, "No default font set and no font object passed"); + } + if (font->font == NULL) luaL_error(L, "The font object was unloaded"); // Wide caracters support. (wchar = UTF32 on 3DS.) wchar_t wtext[len]; len = mbstowcs(wtext, text, len); *(wtext+len) = 0x0; // text end - sftd_draw_wtext(font_default, x, y, color, size, wtext); + sftd_draw_wtext(font->font, x, y, color, size, wtext); return 0; } From 3f48e6e66eccd4177db8625ff595144f524524f5 Mon Sep 17 00:00:00 2001 From: Reuh Date: Sat, 5 Sep 2015 19:44:25 +0200 Subject: [PATCH 002/101] Updated sf2dlib to the latest version of ctrulib We should push these changes to the official sf2dlib repo. One day. Maybe. --- libs/sf2dlib/libsf2d/source/sf2d_draw.c | 8 ++++---- libs/sf2dlib/libsf2d/source/sf2d_texture.c | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/libs/sf2dlib/libsf2d/source/sf2d_draw.c b/libs/sf2dlib/libsf2d/source/sf2d_draw.c index 0e1ed37..bf8f896 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d_draw.c +++ b/libs/sf2dlib/libsf2d/source/sf2d_draw.c @@ -44,7 +44,7 @@ void sf2d_draw_line(int x0, int y0, int x1, int y1, u32 color) (u8[]){2} // number of attributes for each buffer ); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 4); + GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_rectangle(int x, int y, int w, int h, u32 color) @@ -89,7 +89,7 @@ void sf2d_draw_rectangle(int x, int y, int w, int h, u32 color) (u8[]){2} // number of attributes for each buffer ); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 4); + GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_rectangle_rotate(int x, int y, int w, int h, u32 color, float rad) @@ -147,7 +147,7 @@ void sf2d_draw_rectangle_rotate(int x, int y, int w, int h, u32 color, float rad (u8[]){2} // number of attributes for each buffer ); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 4); + GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_fill_circle(int x, int y, int radius, u32 color) @@ -208,5 +208,5 @@ void sf2d_draw_fill_circle(int x, int y, int radius, u32 color) (u8[]){2} // number of attributes for each buffer ); - GPU_DrawArray(GPU_TRIANGLE_FAN, num_segments + 2); + GPU_DrawArray(GPU_TRIANGLE_FAN, 0, num_segments + 2); } diff --git a/libs/sf2dlib/libsf2d/source/sf2d_texture.c b/libs/sf2dlib/libsf2d/source/sf2d_texture.c index af387cf..2dc9fce 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d_texture.c +++ b/libs/sf2dlib/libsf2d/source/sf2d_texture.c @@ -231,7 +231,7 @@ static inline void sf2d_draw_texture_generic(const sf2d_texture *texture, int x, (u8[]){2} // number of attributes for each buffer ); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 4); + GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_texture(const sf2d_texture *texture, int x, int y) @@ -300,7 +300,7 @@ static inline void sf2d_draw_texture_rotate_hotspot_generic(const sf2d_texture * (u8[]){2} // number of attributes for each buffer ); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 4); + GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_texture_rotate_hotspot(const sf2d_texture *texture, int x, int y, float rad, float center_x, float center_y) @@ -362,7 +362,7 @@ static inline void sf2d_draw_texture_part_generic(const sf2d_texture *texture, i (u8[]){2} // number of attributes for each buffer ); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 4); + GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_texture_part(const sf2d_texture *texture, int x, int y, int tex_x, int tex_y, int tex_w, int tex_h) @@ -410,7 +410,7 @@ static inline void sf2d_draw_texture_scale_generic(const sf2d_texture *texture, (u8[]){2} // number of attributes for each buffer ); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 4); + GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_texture_scale(const sf2d_texture *texture, int x, int y, float x_scale, float y_scale) @@ -460,7 +460,7 @@ static inline void sf2d_draw_texture_part_scale_generic(const sf2d_texture *text (u8[]){2} // number of attributes for each buffer ); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 4); + GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_texture_part_scale(const sf2d_texture *texture, float x, float y, float tex_x, float tex_y, float tex_w, float tex_h, float x_scale, float y_scale) @@ -520,7 +520,7 @@ static inline void sf2d_draw_texture_part_rotate_scale_generic(const sf2d_textur (u8[]){2} // number of attributes for each buffer ); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 4); + GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_texture_part_rotate_scale(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale) @@ -569,7 +569,7 @@ static inline void sf2d_draw_texture_depth_generic(const sf2d_texture *texture, (u8[]){2} // number of attributes for each buffer ); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 4); + GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); } void sf2d_draw_texture_depth(const sf2d_texture *texture, int x, int y, signed short z) @@ -614,7 +614,7 @@ void sf2d_draw_quad_uv(const sf2d_texture *texture, float left, float top, float (u8[]){2} // number of attributes for each buffer ); - GPU_DrawArray(GPU_TRIANGLE_STRIP, 4); + GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); } // Grabbed from Citra Emulator (citra/src/video_core/utils.h) From 848fe4209664c346e21245637ec2a7fc0885a22b Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Sat, 12 Sep 2015 00:15:51 +0200 Subject: [PATCH 003/101] Finished the QTM library, Added some text in ctr.c The QTM (Headtracking) is not tested on real hardware, and doesn't work in citra. But should work, maybe. Please update your boot.3dsx to use it without errors. --- source/ctr.c | 2 ++ source/qtm.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/source/ctr.c b/source/ctr.c index 0a226f1..0fe153e 100644 --- a/source/ctr.c +++ b/source/ctr.c @@ -13,6 +13,7 @@ void load_ir_lib(lua_State *L); void load_fs_lib(lua_State *L); void load_httpc_lib(lua_State *L); void load_qtm_lib(lua_State *L); +//void load_cam_lib(lua_State *L); void unload_gfx_lib(lua_State *L); void unload_hid_lib(lua_State *L); @@ -48,6 +49,7 @@ struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } { "fs", load_fs_lib, unload_fs_lib }, { "httpc", load_httpc_lib, unload_httpc_lib }, { "qtm", load_qtm_lib, NULL }, +// { "cam", load_cam_lib, NULL }, { NULL, NULL } }; diff --git a/source/qtm.c b/source/qtm.c index 3e0599b..948789e 100644 --- a/source/qtm.c +++ b/source/qtm.c @@ -5,10 +5,20 @@ #include #include -static int qtm_init(lua_State *L) { - qtmInit(); +typedef struct { + qtmHeadtrackingInfo *info; +} qtm_userdata; - return 0; +static int qtm_init(lua_State *L) { + Result ret = qtmInit(); + if (ret) { + lua_pushboolean(L, false); + lua_pushinteger(L, ret); + return 2; + } + + lua_pushboolean(L, true); + return 1; } static int qtm_shutdown(lua_State *L) { @@ -25,18 +35,69 @@ static int qtm_checkInitialized(lua_State *L) { } static int qtm_getHeadtrackingInfo(lua_State *L) { - return 0; + qtm_userdata *data = lua_newuserdata(L, sizeof(*data)); + luaL_getmetatable(L, "LQTM"); + lua_setmetatable(L, -2); + Result ret = qtmGetHeadtrackingInfo(0, data->info); + if (ret) { + lua_pushnil(L); + return 1; + } + return 1; } static int qtm_checkHeadFullyDetected(lua_State *L) { - return 0; + qtm_userdata *info = luaL_checkudata(L, 1, "LQTM"); + lua_pushboolean(L, qtmCheckHeadFullyDetected(info->info)); + return 1; +} + +static int qtm___index(lua_State *L) { + qtm_userdata *info = luaL_checkudata(L, 1, "LQTM"); + lua_Integer index = luaL_checkinteger(L, 2); + index = index - 1; // Lua index begins at 1 + if (index > 3 || index < 0) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + + lua_pushnumber(L, info->info->coords0[index].x); + lua_pushnumber(L, info->info->coords0[index].y); + + return 2; } static int qtm_convertCoordToScreen(lua_State *L) { - return 0; + qtm_userdata *info = luaL_checkudata(L, 1, "LQTM"); + lua_Integer index = luaL_checkinteger(L, 2); + index = index - 1; // Lua index begins at 1 + if (index > 3 || index < 0) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + float screenWidth = luaL_optnumber(L, 3, 400.0f); + float screenHeight = luaL_optnumber(L, 4, 320.0f); + + u32 x, y = 0; + qtmConvertCoordToScreen(&info->info->coords0[index], &screenWidth, &screenHeight, &x, &y); + + lua_pushinteger(L, x); + lua_pushinteger(L, y); + + return 2; } -// module +// object +static const struct luaL_Reg qtm_methods[] = { + {"checkHeadFullyDetected", qtm_checkHeadFullyDetected}, + {"convertCoordToScreen", qtm_convertCoordToScreen }, + {"__index", qtm___index }, + {NULL, NULL} +}; + +// module functions static const struct luaL_Reg qtm_functions[] = { {"init", qtm_init }, {"shutdown", qtm_shutdown }, @@ -48,6 +109,11 @@ static const struct luaL_Reg qtm_functions[] = { }; int luaopen_qtm_lib(lua_State *L) { + luaL_newmetatable(L, "LQTM"); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + luaL_setfuncs(L, qtm_methods, 0); + luaL_newlib(L, qtm_functions); return 1; } From 9d8c499afc09ee9ad34bf6f2b427d6c80b0e6f78 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Sat, 12 Sep 2015 12:05:04 +0200 Subject: [PATCH 004/101] Fixed the QTM __index method --- source/qtm.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/source/qtm.c b/source/qtm.c index 948789e..45ce66b 100644 --- a/source/qtm.c +++ b/source/qtm.c @@ -5,10 +5,14 @@ #include #include +#include + typedef struct { qtmHeadtrackingInfo *info; } qtm_userdata; +static const struct luaL_Reg qtm_methods[]; + static int qtm_init(lua_State *L) { Result ret = qtmInit(); if (ret) { @@ -54,18 +58,30 @@ static int qtm_checkHeadFullyDetected(lua_State *L) { static int qtm___index(lua_State *L) { qtm_userdata *info = luaL_checkudata(L, 1, "LQTM"); - lua_Integer index = luaL_checkinteger(L, 2); - index = index - 1; // Lua index begins at 1 - if (index > 3 || index < 0) { - lua_pushnil(L); - lua_pushnil(L); + if (lua_isinteger(L, 2)) { // index + lua_Integer index = luaL_checkinteger(L, 2); + index = index - 1; // Lua index begins at 1 + if (index > 3 || index < 0) { + lua_pushnil(L); + lua_pushnil(L); + return 2; + } + + lua_pushnumber(L, info->info->coords0[index].x); + lua_pushnumber(L, info->info->coords0[index].y); return 2; + + } else if (lua_isstring(L, 2)) { //method + const char *mname = luaL_checkstring(L, 2); + for (u8 i=0;qtm_methods[i].name;i++) { + if (strcmp(qtm_methods[i].name, mname) == 0) { + lua_pushcfunction(L, qtm_methods[i].func); + return 1; + } + } } - - lua_pushnumber(L, info->info->coords0[index].x); - lua_pushnumber(L, info->info->coords0[index].y); - - return 2; + lua_pushnil(L); + return 1; } static int qtm_convertCoordToScreen(lua_State *L) { From 0773992b68d8334752166977b29623c49f53bec6 Mon Sep 17 00:00:00 2001 From: Reuh Date: Sat, 12 Sep 2015 13:35:21 +0200 Subject: [PATCH 005/101] Fully documented ctr and ctr.gfx in the C source The documentation can be built using LDoc : run make build-doc However, there's still a bunch of things to document. --- .gitignore | 1 + Makefile | 6 + README.md | 2 + doc/config.ld | 26 +++ doc/newonly.png | Bin 0 -> 10519 bytes sdcard/3ds/ctruLua/{ => examples}/example.lua | 0 source/ctr.c | 80 +++++++-- source/gfx.c | 153 +++++++++++++++++- 8 files changed, 253 insertions(+), 15 deletions(-) create mode 100644 doc/config.ld create mode 100644 doc/newonly.png rename sdcard/3ds/ctruLua/{ => examples}/example.lua (100%) diff --git a/.gitignore b/.gitignore index 7f471c4..9208658 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /build/* /ctruLua.* +/doc/html/* \ No newline at end of file diff --git a/Makefile b/Makefile index e10a175..5451cc2 100644 --- a/Makefile +++ b/Makefile @@ -158,6 +158,9 @@ build-all: @echo Building ctruLua... @make build +build-doc: + @cd doc/ && ldoc . && cd .. + #--------------------------------------------------------------------------------- clean: @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf @@ -186,6 +189,9 @@ clean-all: @echo Cleaning ctruLua... @make clean +clean-doc: + @rm -rf doc/html + #--------------------------------------------------------------------------------- else diff --git a/README.md b/README.md index 47f45c6..b115761 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Warning: the 'u' in the repo's name is a 'µ', not a 'u'. * Clone this repository and run the command `make build-all` to build all the dependencies. * If you only made changes to ctrµLua, run `make` to rebuild ctµLua without rebuilding all the dependencies. +To build the documentation, run `make build-doc` (requires [https://github.com/stevedonovan/LDoc](LDoc)). + May not work under Windows. #### Based on ctrulib by smealum: [https://github.com/smealum/ctrulib](https://github.com/smealum/ctrulib) diff --git a/doc/config.ld b/doc/config.ld new file mode 100644 index 0000000..13c1958 --- /dev/null +++ b/doc/config.ld @@ -0,0 +1,26 @@ +-- General options +title = "ctrµLua documentation" +project = "ctrµLua" +description = "ctrµLua: Lua homebrewing for 3DS" +not_luadoc = true + +-- Generation options +dir = "./html/" +style = "!fixed" +format = "markdown" +plain = true + +-- Input files +topics = "../README.md" +file = "../source/" +examples = "../sdcard/3ds/ctruLua/examples/" +manual_url = "file://../libs/lua-5.3.1/doc/manual.html" + +-- Custom tags +custom_tags = { { "newonly", hidden = true } } +custom_display_name_handler = function(item, default_handler) + if item.tags.newonly then + return default_handler(item).." (N3DS only)" + end + return default_handler(item) +end diff --git a/doc/newonly.png b/doc/newonly.png new file mode 100644 index 0000000000000000000000000000000000000000..35ab1694b590bd609a924217c9e7c8e72a264128 GIT binary patch literal 10519 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 #include <3ds/services/apt.h> #include <3ds/os.h> @@ -5,27 +10,84 @@ #include #include +/*** +The `ctr.gfx` module. +@table gfx +@see ctr.gfx +*/ void load_gfx_lib(lua_State *L); -void load_news_lib(lua_State *L); -void load_ptm_lib(lua_State *L); -void load_hid_lib(lua_State *L); -void load_ir_lib(lua_State *L); -void load_fs_lib(lua_State *L); -void load_httpc_lib(lua_State *L); -void load_qtm_lib(lua_State *L); -//void load_cam_lib(lua_State *L); - void unload_gfx_lib(lua_State *L); + +/*** +The `ctr.news` module. +@table news +@see ctr.news +*/ +void load_news_lib(lua_State *L); + +/*** +The `ctr.ptm` module. +@table ptm +@see ctr.ptm +*/ +void load_ptm_lib(lua_State *L); + +/*** +The `ctr.hid` module. +@table hid +@see ctr.hid +*/ +void load_hid_lib(lua_State *L); void unload_hid_lib(lua_State *L); + +/*** +The `ctr.ir` module. +@table ir +@see ctr.ir +*/ +void load_ir_lib(lua_State *L); + +/*** +The `ctr.fs` module. +@table fs +@see ctr.fs +*/ +void load_fs_lib(lua_State *L); void unload_fs_lib(lua_State *L); + +/*** +The `ctr.httpc` module. +@table httpc +@see ctr.httpc +*/ +void load_httpc_lib(lua_State *L); void unload_httpc_lib(lua_State *L); +/*** +The `ctr.qtm` module. +@table qtm +@see ctr.qtm +*/ +void load_qtm_lib(lua_State *L); + +//void load_cam_lib(lua_State *L); + +/*** +Return whether or not the program should continue. +@function run +@treturn boolean `false` if the program should exist or `true` if it can continue +*/ static int ctr_run(lua_State *L) { lua_pushboolean(L, aptMainLoop()); return 1; } +/*** +Return the number of milliseconds since 1st Jan 1900 00:00. +@function time +@treturn number milliseconds +*/ static int ctr_time(lua_State *L) { lua_pushinteger(L, osGetTime()); diff --git a/source/gfx.c b/source/gfx.c index 4dc4248..f070648 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -1,3 +1,8 @@ +/*** +The `gfx` module. +@module ctr.gfx +@usage local gfx = require("ctr.gfx") +*/ #include #include @@ -12,15 +17,43 @@ bool isGfxInitialised = false; +/*** +The `ctr.gfx.color` module. +@table color +@see ctr.gfx.color +*/ void load_color_lib(lua_State *L); -void load_font_lib(lua_State *L); -void load_texture_lib(lua_State *L); -void load_map_lib(lua_State *L); - -void unload_font_lib(lua_State *L); - u32 color_default; +/*** +The `ctr.gfx.font` module. +@table font +@see ctr.gfx.font +*/ +void load_font_lib(lua_State *L); +void unload_font_lib(lua_State *L); + +/*** +The `ctr.gfx.texture` module. +@table texture +@see ctr.gfx.texture +*/ +void load_texture_lib(lua_State *L); + +/*** +The `ctr.gfx.map` module. +@table map +@see ctr.gfx.map +*/ +void load_map_lib(lua_State *L); + +/*** +Start drawing to a screen. +Must be called before any draw operation. +@function startFrame +@tparam number screen the screen to draw to (`GFX_TOP` or `GFX_BOTTOM`) +@tparam[opt=GFX_LEFT] number eye the eye to draw to (`GFX_LEFT` or `GFX_RIGHT`) +*/ static int gfx_startFrame(lua_State *L) { u8 screen = luaL_checkinteger(L, 1); u8 eye = luaL_optinteger(L, 2, GFX_LEFT); @@ -30,18 +63,32 @@ static int gfx_startFrame(lua_State *L) { return 0; } +/*** +End drawing to a screen. +Must be called after your draw operations. +@function endFrame +*/ static int gfx_endFrame(lua_State *L) { sf2d_end_frame(); return 0; } +/*** +Display any drawn pixel. +@function render +*/ static int gfx_render(lua_State *L) { sf2d_swapbuffers(); return 0; } +/*** +Get the current number of frame per second. +@function getFPS +@treturn number number of frame per seconds +*/ static int gfx_getFPS(lua_State *L) { float fps = sf2d_get_fps(); @@ -50,6 +97,11 @@ static int gfx_getFPS(lua_State *L) { return 1; } +/*** +Enable or disable the stereoscopic 3D on the top screen. +@function set3D +@tparam boolean enable true to enable, false to disable +*/ static int gfx_set3D(lua_State *L) { bool enable = lua_toboolean(L, 1); @@ -58,12 +110,26 @@ static int gfx_set3D(lua_State *L) { return 0; } +/*** +Get free VRAM space. +@function vramSpaceFree +@treturn integer free VRAM in bytes +*/ static int gfx_vramSpaceFree(lua_State *L) { lua_pushinteger(L, vramSpaceFree()); return 1; } +/*** +Draw a line on the current screen. +@function line +@tparam integer x1 line's starting point horizontal coordinate, in pixels +@tparam integer y1 line's starting point vertical coordinate, in pixels +@tparam integer x2 line's endpoint horizontal coordinate, in pixels +@tparam integer y2 line's endpoint vertical coordinate, in pixels +@tparam[opt=default color] integer color drawing color +*/ static int gfx_line(lua_State *L) { int x1 = luaL_checkinteger(L, 1); int y1 = luaL_checkinteger(L, 2); @@ -77,6 +143,13 @@ static int gfx_line(lua_State *L) { return 0; } +/*** +Draw a point, a single pixel, on the current screen. +@function point +@tparam integer x point horizontal coordinate, in pixels +@tparam integer y point vertical coordinate, in pixels +@tparam[opt=default color] integer color drawing color +*/ static int gfx_point(lua_State *L) { int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); @@ -88,6 +161,16 @@ static int gfx_point(lua_State *L) { return 0; } +/*** +Draw a rectangle on the current screen. +@function rectangle +@tparam integer x rectangle origin horizontal coordinate, in pixels +@tparam integer y rectangle origin vertical coordinate, in pixels +@tparam integer width rectangle width, in pixels +@tparam integer height rectangle height, in pixels +@tparam[opt=0] number angle rectangle rotation, in radians +@tparam[opt=default color] integer color drawing color +*/ static int gfx_rectangle(lua_State *L) { int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); @@ -105,6 +188,14 @@ static int gfx_rectangle(lua_State *L) { return 0; } +/*** +Draw a circle on the current screen. +@function circle +@tparam integer x circle center horizontal coordinate, in pixels +@tparam integer y circle center vertical coordinate, in pixels +@tparam integer radius circle radius, in pixels +@tparam[opt=default color] integer color drawing color +*/ static int gfx_circle(lua_State *L) { int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); @@ -117,6 +208,16 @@ static int gfx_circle(lua_State *L) { return 0; } +/*** +Draw a text on the current screen. +@function text +@tparam integer x text drawing origin horizontal coordinate, in pixels +@tparam integer y text drawing origin vertical coordinate, in pixels +@tparam string text the text to draw +@tparam[opt=9] integer size drawing size, in pixels +@tparam[opt=default color] integer color drawing color +@tparam[opt=default font] font font to use +*/ static int gfx_text(lua_State *L) { int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); @@ -161,13 +262,53 @@ static const struct luaL_Reg gfx_lib[] = { // Constants struct { char *name; int value; } gfx_constants[] = { + /*** + Constant used to select the top screen. + It is equal to `0`. + @field GFX_TOP + */ { "GFX_TOP", GFX_TOP }, + /*** + Constant used to select the bottom screen. + It is equal to `1`. + @field GFX_BOTTOM + */ { "GFX_BOTTOM", GFX_BOTTOM }, + /*** + Constant used to select the left eye. + It is equal to `0`. + @field GFX_LEFT + */ { "GFX_LEFT", GFX_LEFT }, + /*** + Constant used to select the right eye. + It is equal to `1`. + @field GFX_RIGHT + */ { "GFX_RIGHT", GFX_RIGHT }, + /*** + The top screen height, in pixels. + It is equal to `240`. + @field TOP_HEIGHT + */ { "TOP_HEIGHT", 240 }, + /*** + The top screen width, in pixels. + It is equal to `400`. + @field TOP_WIDTH + */ { "TOP_WIDTH", 400 }, + /*** + The bottom screen height, in pixels. + It is equal to `240`. + @field BOTTOM_HEIGHT + */ { "BOTTOM_HEIGHT", 240 }, + /*** + The bottom screen width, in pixels. + It is equal to `320`. + @field BOTTOM_WIDTH + */ { "BOTTOM_WIDTH", 320 }, { NULL, 0 } }; From 486b142e35aec4f2cdeba3cc2da36155a4c2c695 Mon Sep 17 00:00:00 2001 From: Reuh Date: Sat, 12 Sep 2015 14:00:23 +0200 Subject: [PATCH 006/101] Updated README.md --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b115761..6232241 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,11 @@ Warning: the 'u' in the repo's name is a 'µ', not a 'u'. * Clone this repository and run the command `make build-all` to build all the dependencies. * If you only made changes to ctrµLua, run `make` to rebuild ctµLua without rebuilding all the dependencies. -To build the documentation, run `make build-doc` (requires [https://github.com/stevedonovan/LDoc](LDoc)). - May not work under Windows. +#### Lua API Documentation + +* An online version of the documentation can be found here : http://thomas99.no-ip.org/ctrulua +* To build the documentation, run `make build-doc` (requires [LDoc](https://github.com/stevedonovan/LDoc)). + #### Based on ctrulib by smealum: [https://github.com/smealum/ctrulib](https://github.com/smealum/ctrulib) From e94da040f740742cfccbc6c706d5f982fb53edf9 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Sat, 12 Sep 2015 19:29:11 +0200 Subject: [PATCH 007/101] Added documentation for gfx.map and ctr.qtm modules --- source/map.c | 56 +++++++++++++++++++++++++++++++++++++++++++ source/qtm.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/source/map.c b/source/map.c index 32deeca..04e8552 100644 --- a/source/map.c +++ b/source/map.c @@ -1,3 +1,9 @@ +/*** +The `map` module. +@module ctr.gfx.map +@usage local map = require("ctr.gfx.map") +*/ + #include #include @@ -30,6 +36,16 @@ u16 getTile(map_userdata *map, int x, int y) { } // module functions + +/*** +Load a map from a file. +@function load +@tparam string path path to the .map +@tparam texture tileset containing the tileset +@tparam number tileWidth tile width +@tparam number tileHeight tile height +@treturn map loaded map object +*/ static int map_load(lua_State *L) { const char *mapPath = luaL_checkstring(L, 1); texture_userdata *texture = luaL_checkudata(L, 2, "LTexture"); @@ -91,6 +107,18 @@ static int map_load(lua_State *L) { return 1; } +/*** +Map object +@section Methods +*/ + +/*** +Draw a map. +@function :draw +@tparam number x X position +@tparam number y Y position +@within Methods +*/ static int map_draw(lua_State *L) { map_userdata *map = luaL_checkudata(L, 1, "LMap"); int x = luaL_checkinteger(L, 2); @@ -120,6 +148,11 @@ static int map_draw(lua_State *L) { return 0; } +/*** +Unload a map. +@function :unload +@within Methods +*/ static int map_unload(lua_State *L) { map_userdata *map = luaL_checkudata(L, 1, "LMap"); free(map->data); @@ -127,6 +160,13 @@ static int map_unload(lua_State *L) { return 0; } +/*** +Return the size of a map. +@function :getSize +@treturn number width of the map, in tiles +@treturn number height of the map, in tiles +@within Methods +*/ static int map_getSize(lua_State *L) { map_userdata *map = luaL_checkudata(L, 1, "LMap"); @@ -136,6 +176,14 @@ static int map_getSize(lua_State *L) { return 2; } +/*** +Return the value of a tile. +@function :getTile +@tparam number x X position of the tile +@tparam number y Y position of the tile +@treturn number value of the tile +@within Methods +*/ static int map_getTile(lua_State *L) { map_userdata *map = luaL_checkudata(L, 1, "LMap"); int x = luaL_checkinteger(L, 2); @@ -145,6 +193,14 @@ static int map_getTile(lua_State *L) { return 1; } +/*** +Set the value of a tile. +@function :setTile +@tparam number x X position of the tile +@tparam number y Y position of the tile +@tparam number value New value for the tile +@within Methods +*/ static int map_setTile(lua_State *L) { map_userdata *map = luaL_checkudata(L, 1, "LMap"); int x = luaL_checkinteger(L, 2); diff --git a/source/qtm.c b/source/qtm.c index 45ce66b..758a53a 100644 --- a/source/qtm.c +++ b/source/qtm.c @@ -1,3 +1,9 @@ +/*** +The `qtm` module, for headtracking. New3ds only. +@module ctr.qtm +@usage local qtm = require("ctr.qtm") +@newonly +*/ #include <3ds.h> #include <3ds/types.h> #include <3ds/services/qtm.h> @@ -13,6 +19,10 @@ typedef struct { static const struct luaL_Reg qtm_methods[]; +/*** +Initialize the QTM module. +@function init +*/ static int qtm_init(lua_State *L) { Result ret = qtmInit(); if (ret) { @@ -25,12 +35,21 @@ static int qtm_init(lua_State *L) { return 1; } +/*** +Disable the QTM module. +@function shutdown +*/ static int qtm_shutdown(lua_State *L) { qtmExit(); return 0; } +/*** +Check if the module is initialized. +@function checkInitialized +@treturn boolean `true` if initialized. +*/ static int qtm_checkInitialized(lua_State *L) { bool isInit = qtmCheckInitialized(); @@ -38,6 +57,11 @@ static int qtm_checkInitialized(lua_State *L) { return 1; } +/*** +Return informations about the headtracking +@function getHeadTrackingInfo +@treturn qtmInfos QTM informations +*/ static int qtm_getHeadtrackingInfo(lua_State *L) { qtm_userdata *data = lua_newuserdata(L, sizeof(*data)); luaL_getmetatable(L, "LQTM"); @@ -50,6 +74,12 @@ static int qtm_getHeadtrackingInfo(lua_State *L) { return 1; } +/*** +Check if the head is fully detected +@function checkHeadFullyDetected +@tparam qtmInfos qtmInfos QTM informations +@treturn boolean `true` if fully detected +*/ static int qtm_checkHeadFullyDetected(lua_State *L) { qtm_userdata *info = luaL_checkudata(L, 1, "LQTM"); lua_pushboolean(L, qtmCheckHeadFullyDetected(info->info)); @@ -84,6 +114,16 @@ static int qtm___index(lua_State *L) { return 1; } +/*** +Convert QTM coordinates to screen coordinates +@function convertCoordToScreen +@tparam qtmInfos qtmInfos QTM informations +@tparam number coordinates index +@tparam number [screenWidth] specify a screen width (default `400`) +@tparam number [screenHeight] specify a screen height (default `320`) +@treturn number screen X coordinate +@treturn number screen Y coordinate +*/ static int qtm_convertCoordToScreen(lua_State *L) { qtm_userdata *info = luaL_checkudata(L, 1, "LQTM"); lua_Integer index = luaL_checkinteger(L, 2); @@ -105,6 +145,33 @@ static int qtm_convertCoordToScreen(lua_State *L) { return 2; } +/*** +qtmInfos object +@section Methods +*/ + +/*** +Check if the head is fully detected +@function :checkHeadFullyDetected +@treturn boolean `true` if fully detected +*/ + +/*** +Convert QTM coordinates to screen coordinates +@function :convertCoordToScreen +@tparam number coordinates index +@tparam number [screenWidth] specify a screen width (default `400`) +@tparam number [screenHeight] specify a screen height (default `320`) +@treturn number screen X coordinate +@treturn number screen Y coordinate +*/ + +/*** +When the object is indexed with a number from 1 to 4, it returns a coordinate +of one of the headtracker point. +@tfield number index coordinates, as two numbers (not a table) +*/ + // object static const struct luaL_Reg qtm_methods[] = { {"checkHeadFullyDetected", qtm_checkHeadFullyDetected}, From ab06123f7aed8f6fba82cedc8a4f70a81659800d Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Sat, 12 Sep 2015 19:57:28 +0200 Subject: [PATCH 008/101] Fixed the QTM documentation, Added an usage note about indexing an object with a number --- source/qtm.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/source/qtm.c b/source/qtm.c index 758a53a..2e31cf3 100644 --- a/source/qtm.c +++ b/source/qtm.c @@ -119,8 +119,8 @@ Convert QTM coordinates to screen coordinates @function convertCoordToScreen @tparam qtmInfos qtmInfos QTM informations @tparam number coordinates index -@tparam number [screenWidth] specify a screen width (default `400`) -@tparam number [screenHeight] specify a screen height (default `320`) +@tparam[opt=400] number screenWidth specify a screen width +@tparam[opt=320] number screenHeight specify a screen height @treturn number screen X coordinate @treturn number screen Y coordinate */ @@ -160,16 +160,17 @@ Check if the head is fully detected Convert QTM coordinates to screen coordinates @function :convertCoordToScreen @tparam number coordinates index -@tparam number [screenWidth] specify a screen width (default `400`) -@tparam number [screenHeight] specify a screen height (default `320`) +@tparam[opt=400] number screenWidth specify a screen width +@tparam[opt=320] number screenHeight specify a screen height @treturn number screen X coordinate @treturn number screen Y coordinate */ /*** -When the object is indexed with a number from 1 to 4, it returns a coordinate -of one of the headtracker point. +When the object is indexed with a number from 1 to 4, it returns the coordinates +of one of the headtracker points. @tfield number index coordinates, as two numbers (not a table) +@usage coordX, coordY = qtmInfos[index] */ // object From 070a0d698ea2c44852721c1f0f6d85e2a6b8987c Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Sun, 13 Sep 2015 14:39:12 +0200 Subject: [PATCH 009/101] Added documentation for the ctr.hid, ctr.ptm, ctr.news and ctr.gfx.texture --- source/hid.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++- source/news.c | 21 ++++++++++ source/ptm.c | 42 ++++++++++++++++++- source/texture.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 263 insertions(+), 3 deletions(-) diff --git a/source/hid.c b/source/hid.c index 371f6e5..6d54b20 100644 --- a/source/hid.c +++ b/source/hid.c @@ -1,9 +1,55 @@ +/*** +The `hid` module. +The circle pad pro is supported, it's keys replace de "3ds only" keys +@module ctr.hid +@usage local hid = require("ctr.hid") +*/ #include <3ds/types.h> #include <3ds/services/hid.h> #include #include +/*** +Keys list +@table keys +@field a A +@field b B +@field select Select +@field start Start +@field dRight D-Pad right +@field dLeft D-Pad Left +@field dUp D-Pad Up +@field dDown D-Pad Down +@field r R trigger +@field l L trigger +@field x X +@field y Y +@field zl ZL trigger (new3ds only) +@field zr ZR trigger (new3ds only) +@field touch +@field cstickRight C-Stick right (new3ds only) +@field cstickLeft C-Stick left (new3ds only) +@field cstickUp C-Stick up (new3ds only) +@field cstickDown C-Stick down (new3ds only) +@field cpadRight Circle pad right +@field cpadLeft Circle pad left +@field cpadUp Circle pad up +@field cpadDown Circle pad down +@field up Generic up +@field down Generic down +@field left Generic left +@field right Generic right +*/ + +/*** +Keys states +@table states +@field down keys which have been just pressed +@field held keys which are held down +@field up keys whick are been just released +*/ + // Key list based on hid.h from the ctrulib by smealum struct { PAD_KEY key; char *name; } hid_keys_name[] = { { KEY_A , "a" }, @@ -38,12 +84,28 @@ struct { PAD_KEY key; char *name; } hid_keys_name[] = { { 0, NULL } }; +/*** +Refresh the HID state. +@function read +*/ static int hid_read(lua_State *L) { hidScanInput(); return 0; } +/*** +Return the keys states as `state.key` in a table. +@function keys +@treturn table keys states +@usage +-- Just an example +hid.read() +local keys = hid.keys() +if keys.held.a then + -- do stuff +end +*/ static int hid_keys(lua_State *L) { u32 kDown = hidKeysDown(); u32 kHeld = hidKeysHeld(); @@ -79,6 +141,13 @@ static int hid_keys(lua_State *L) { return 1; } +/*** +Return the touch position on the touch screen. +`0,0` is the top-left corner. +@function touch +@treturn number X position +@treturn number Y position +*/ static int hid_touch(lua_State *L) { touchPosition pos; hidTouchRead(&pos); @@ -89,6 +158,13 @@ static int hid_touch(lua_State *L) { return 2; } +/*** +Return the circle pad position. +`0,0` is the center position. Warning: the circle pad doesn't always go back to `0,0`. +@function circle +@treturn number X position +@treturn number Y position +*/ static int hid_circle(lua_State *L) { circlePosition pos; hidCircleRead(&pos); @@ -99,6 +175,13 @@ static int hid_circle(lua_State *L) { return 2; } +/*** +Return the accelerometer vector +@function accel +@treturn number X acceleration +@treturn number Y acceleration +@treturn number Z acceleration +*/ static int hid_accel(lua_State *L) { accelVector pos; hidAccelRead(&pos); @@ -110,6 +193,13 @@ static int hid_accel(lua_State *L) { return 3; } +/*** +Return the gyroscope rate. +@function gyro +@treturn number roll +@treturn number pitch +@treturn number yaw +*/ static int hid_gyro(lua_State *L) { angularRate pos; hidGyroRead(&pos); @@ -121,6 +211,11 @@ static int hid_gyro(lua_State *L) { return 3; } +/*** +Return the sound volume. +@function volume +@treturn number volume (`0` to `63`) +*/ static int hid_volume(lua_State *L) { u8 volume = 0; HIDUSER_GetSoundVolume(&volume); @@ -130,6 +225,11 @@ static int hid_volume(lua_State *L) { return 1; } +/*** +Return the 3D cursor position. +@function pos3d +@treturn number 3d cursor position (`0` to `1`) +*/ static int hid_3d(lua_State *L) { float slider = (*(float*)0x1FF81080); @@ -165,4 +265,4 @@ void load_hid_lib(lua_State *L) { void unload_hid_lib(lua_State *L) { HIDUSER_DisableAccelerometer(); HIDUSER_DisableGyroscope(); -} \ No newline at end of file +} diff --git a/source/news.c b/source/news.c index 6f333f0..f55449c 100644 --- a/source/news.c +++ b/source/news.c @@ -1,3 +1,8 @@ +/*** +The `news` module. +@module ctr.news +@usage local news = require("ctr.news") +*/ #include <3ds/types.h> #include <3ds/util/utf.h> #include <3ds/services/news.h> @@ -5,12 +10,24 @@ #include #include +/*** +Initialize the news module. +@function init +*/ static int news_init(lua_State *L) { newsInit(); return 0; } +/*** +Send a notification to the user. WIP, do not use !!! +@function notification +@tparam string title title of the notification +@tparam string message message of the notification +@tparam string imageData data from a JPEG image (content of a file) or raw data +@tparam[OPT=false] boolean jpeg set to `true` if the data is from a JPEG file +*/ static int news_notification(lua_State *L) { const char *title = luaL_checkstring(L, 1); const char *message = luaL_checkstring(L, 2); @@ -33,6 +50,10 @@ static int news_notification(lua_State *L) { return 0; } +/*** +Disable the news module. +@function shutdown +*/ static int news_shutdown(lua_State *L) { newsExit(); diff --git a/source/ptm.c b/source/ptm.c index 886b30c..6182635 100644 --- a/source/ptm.c +++ b/source/ptm.c @@ -1,3 +1,8 @@ +/*** +The `ptm` module. +@module ctr.ptm +@usage local ptm = require("ctr.ptm") +*/ #include <3ds/types.h> #include <3ds/services/ptm.h> @@ -6,18 +11,31 @@ static Handle *ptmHandle; +/*** +Initialize the PTM module. +@function init +*/ static int ptm_init(lua_State *L) { ptmInit(); return 0; } +/*** +Disable the PTM module. +@function shutdown +*/ static int ptm_shutdown(lua_State *L) { ptmExit(); return 0; } +/*** +Return the shell state. Don't care about this. +@function getShellState +@treturn number shell state +*/ static int ptm_getShellState(lua_State *L) { u8 out = 0; PTMU_GetShellState(ptmHandle, &out); @@ -27,6 +45,11 @@ static int ptm_getShellState(lua_State *L) { return 1; } +/*** +Return the battery level. +@function getBatteryLevel +@treturn number battery level (`5`: fully charged; `0`: empty) +*/ static int ptm_getBatteryLevel(lua_State *L) { u8 out = 0; PTMU_GetBatteryLevel(ptmHandle, &out); @@ -36,24 +59,39 @@ static int ptm_getBatteryLevel(lua_State *L) { return 1; } +/*** +Return whether or not the battery is charging. +@function getBatteryChargeState +@treturn boolean `true` if the battery is charging +*/ static int ptm_getBatteryChargeState(lua_State *L) { u8 out = 0; PTMU_GetBatteryChargeState(ptmHandle, &out); - lua_pushinteger(L, out); + lua_pushboolean(L, out); return 1; } +/*** +Return whether or not the pedometer is counting. +@function getPedometerState +@treturn boolean `true` if the pedometer is counting +*/ static int ptm_getPedometerState(lua_State *L) { u8 out = 0; PTMU_GetPedometerState(ptmHandle, &out); - lua_pushinteger(L, out); + lua_pushboolean(L, out); return 1; } +/*** +Return the total steps taken with the system. +@function getTotalStepCount +@treturn number step count +*/ static int ptm_getTotalStepCount(lua_State *L) { u32 steps = 0; PTMU_GetTotalStepCount(ptmHandle, &steps); diff --git a/source/texture.c b/source/texture.c index 2581445..dc27549 100644 --- a/source/texture.c +++ b/source/texture.c @@ -1,3 +1,8 @@ +/*** +The `gfx.texture` module. +@module ctr.gfx.texture +@usage local texture = require("ctr.gfx.texture") +*/ #include #include @@ -14,6 +19,15 @@ u8 getType(const char *name) { } // module functions + +/*** +Load a texture from a file. Supported formats: PNG, JPEG, BMP. +@function load +@tparam string path path to the image file +@tparam[opt=PLACE_RAM] number place where to put the loaded texture +@tparam[opt=auto] number type type of the image +@treturn texture the loaded texture object +*/ static int texture_load(lua_State *L) { const char *path = luaL_checkstring(L, 1); u8 place = luaL_optinteger(L, 2, SF2D_PLACE_RAM); //place in ram by default @@ -51,6 +65,18 @@ static int texture_load(lua_State *L) { return 1; } +/*** +Texture object +@section Methods +*/ + +/*** +Draw a texture. +@function :draw +@tparam number x X position +@tparam number y Y position +@tparam[opt=0.0] number rad rotation of the texture (in radians) +*/ static int texture_draw(lua_State *L) { texture_userdata *texture = luaL_checkudata(L, 1, "LTexture"); int x = luaL_checkinteger(L, 2); @@ -66,6 +92,17 @@ static int texture_draw(lua_State *L) { return 0; } +/*** +Draw a part of the texture +@function :drawPart +@tparam number x X position +@tparam number y Y position +@tparam number sx X position of the beginning of the part +@tparam number sy Y position of the beginning of the part +@tparam number w width of the part +@tparam number h height of the part +@tparam[opt=0.0] number rad rotation of the part (in radians) +*/ static int texture_drawPart(lua_State *L) { texture_userdata *texture = luaL_checkudata(L, 1, "LTexture"); int x = luaL_checkinteger(L, 2); @@ -81,6 +118,12 @@ static int texture_drawPart(lua_State *L) { return 0; } +/*** +Return the size of the texture. +@function :getSize +@treturn number width of the texture +@treturn number height of the texture +*/ static int texture_getSize(lua_State *L) { texture_userdata *texture = luaL_checkudata(L, 1, "LTexture"); @@ -90,6 +133,10 @@ static int texture_getSize(lua_State *L) { return 2; } +/*** +Unload a texture. +@function :unload +*/ static int texture_unload(lua_State *L) { texture_userdata *texture = luaL_checkudata(L, 1, "LTexture"); @@ -101,6 +148,12 @@ static int texture_unload(lua_State *L) { return 0; } +/*** +Rescale the texture. The default scale is `1.0`. +@function :scale +@tparam number scaleX new scale of the width +@tparam number scaleY new scale of the height +*/ static int texture_scale(lua_State *L) { texture_userdata *texture = luaL_checkudata(L, 1, "LTexture"); float sx = luaL_checknumber(L, 2); @@ -112,6 +165,13 @@ static int texture_scale(lua_State *L) { return 0; } +/*** +Return the color of a pixel. +@function :getPixel +@tparam number x X position of the pixel +@tparam number y Y position of the pixel +@treturn number color of the pixel. +*/ static int texture_getPixel(lua_State *L) { texture_userdata *texture = luaL_checkudata(L, 1, "LTexture"); int x = luaL_checkinteger(L, 2); @@ -122,6 +182,13 @@ static int texture_getPixel(lua_State *L) { return 1; } +/*** +Set the color of a pixel. +@function :setPixel +@tparam number x X position of the pixel +@tparam number y Y position of the pixel +@tparam number color New color of the pixel +*/ static int texture_setPixel(lua_State *L) { texture_userdata *texture = luaL_checkudata(L, 1, "LTexture"); int x = luaL_checkinteger(L, 2); @@ -133,6 +200,11 @@ static int texture_setPixel(lua_State *L) { return 0; } +/*** +Set the blend color of the texture. +@function :setBlendColor +@tparam number color new blend color +*/ static int texture_setBlendColor(lua_State *L) { texture_userdata *texture = luaL_checkudata(L, 1, "LTexture"); u32 color = luaL_checkinteger(L, 2); @@ -162,13 +234,42 @@ static const struct luaL_Reg texture_functions[] = { {NULL, NULL} }; +/*** +Fields +@section Fields +*/ + // constants struct { char *name; int value; } texture_constants[] = { + /*** + Constant used to select the RAM. + @field PLACE_RAM + */ {"PLACE_RAM", SF2D_PLACE_RAM }, + /*** + Constant used to select the VRAM. + @field PLACE_VRAM + */ {"PLACE_VRAM", SF2D_PLACE_VRAM}, + /*** + Constant used to select a temporary RAM pool. Don't use it. + @field PLACE_TEMP + */ {"PLACE_TEMP", SF2D_PLACE_TEMP}, + /*** + Constant used to select the PNG type. + @field TYPE_PNG + */ {"TYPE_PNG", 0 }, + /*** + Constant used to select the JPEG type. + @field TYPE_JPEG + */ {"TYPE_JPEG", 1 }, + /*** + Constant used to select the BMP type. + @field TYPE_BMP + */ {"TYPE_BMP", 2 }, {NULL, 0} }; From 22bcc3e2d5614326f507b05f1d9c3a75b7a10a40 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Mon, 14 Sep 2015 22:17:30 +0200 Subject: [PATCH 010/101] =?UTF-8?q?Added=20the=20Tiago=20Dionizio's=20"lzl?= =?UTF-8?q?ib",=20and=20modified=20it=20to=20be=20usable=20with=20ctr?= =?UTF-8?q?=C2=B5Lua.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accessible with require("ctr.fs.lzlib"). I just added the file "lzlib.c" in the source/ directory, because it's much simpler to build. https://github.com/LuaDist/lzlib for details and documentation. --- source/fs.c | 17 +- source/ir.c | 62 ++- source/lzlib.c | 997 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1072 insertions(+), 4 deletions(-) create mode 100644 source/lzlib.c diff --git a/source/fs.c b/source/fs.c index a568cb7..940ef58 100644 --- a/source/fs.c +++ b/source/fs.c @@ -10,6 +10,8 @@ Handle *fsuHandle; FS_archive sdmcArchive; +void load_lzlib(lua_State *L); + static int fs_list(lua_State *L) { const char *path = luaL_checkstring(L, 1); @@ -99,9 +101,20 @@ static const struct luaL_Reg fs_lib[] = { { NULL, NULL } }; +// submodules +struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } fs_libs[] = { + {"zip", load_lzlib, NULL}, + {NULL, NULL} +}; + int luaopen_fs_lib(lua_State *L) { luaL_newlib(L, fs_lib); - + + for (int i = 0; fs_libs[i].name; i++) { + fs_libs[i].load(L); + lua_setfield(L, -2, fs_libs[i].name); + } + return 1; } @@ -121,4 +134,4 @@ void unload_fs_lib(lua_State *L) { FSUSER_CloseArchive(fsuHandle, &sdmcArchive); fsExit(); -} \ No newline at end of file +} diff --git a/source/ir.c b/source/ir.c index 14a1ec7..40cd490 100644 --- a/source/ir.c +++ b/source/ir.c @@ -1,3 +1,8 @@ +/*** +The `ir` module. +@module ctr.ir +@usage local ir = require("ctr.ir") +*/ #include <3ds/types.h> #include <3ds/services/ir.h> #include <3ds/linear.h> @@ -8,8 +13,35 @@ u32 bufferSize = 0; u32 *buffer; +/*** +Bitrate codes list (this is not a part of the module, just a reference) +@table bitrates +@field 3 115200 +@field 4 96000 +@field 5 72000 +@field 6 48000 (default) +@field 7 36000 +@field 8 24000 +@field 9 18000 +@field 10 12000 +@field 11 9600 +@field 12 6000 +@field 13 3000 +@field 14 57600 +@field 15 38400 +@field 16 19200 +@field 17 7200 +@field 18 4800 +*/ + +/*** +Initialize the IR module. +@function init +@tparam[opt=6] number bitrate bitrate of the IR module (more informations below) +@tparam[opt=2048] number buffer size of the buffer, in bytes (max 2048) +*/ static int ir_init(lua_State *L) { - u8 bitrate = luaL_checkinteger(L, 1); + u8 bitrate = luaL_optinteger(L, 1, 6); bufferSize = luaL_optinteger(L, 2, 2048); //default: 2Kio buffer = linearAlloc(bufferSize); @@ -25,12 +57,22 @@ static int ir_init(lua_State *L) { return 1; } +/*** +Disable the IR module. +@function shutdown +*/ static int ir_shutdown(lua_State *L) { IRU_Shutdown(); return 0; } +/*** +Send some data over the IR module. +@function send +@tparam string data just some data +@tparam[opt=false] boolean wait set to `true` to wait until the data is sent. +*/ static int ir_send(lua_State *L) { u8 *data = (u8*)luaL_checkstring(L, 1); u32 wait = lua_toboolean(L, 2); @@ -40,8 +82,14 @@ static int ir_send(lua_State *L) { return 0; } +/*** +Receive some data from the IR module. +@function receive +@tparam[opt=buffer size] number size bytes to receive +@tparam[opt=false] boolean wait wait until the data is received +*/ static int ir_receive(lua_State *L) { - u32 size = luaL_optinteger(L, 1, bufferSize); + u32 size = luaL_optinteger(L, 1, 0x800); u32 wait = lua_toboolean(L, 2); u8 *data = 0; u32 *transfercount = 0; @@ -53,6 +101,11 @@ static int ir_receive(lua_State *L) { return 1; } +/*** +Set the bitrate of the communication. +@function setBitRate +@tparam number bitrate new bitrate for the communication +*/ static int ir_setBitRate(lua_State *L) { u8 bitrate = luaL_checkinteger(L, 1); @@ -61,6 +114,11 @@ static int ir_setBitRate(lua_State *L) { return 0; } +/*** +Return the actual bitrate of the communication. +@function getBitRate +@treturn number actual bitrate +*/ static int ir_getBitRate(lua_State *L) { u8 bitrate = 0; diff --git a/source/lzlib.c b/source/lzlib.c new file mode 100644 index 0000000..b73dee1 --- /dev/null +++ b/source/lzlib.c @@ -0,0 +1,997 @@ +/************************************************************************ +* Author : Tiago Dionizio * +* Library : lzlib - Lua 5 interface to access zlib library functions * +* * +* 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. * +************************************************************************/ + +/* +Library modified in order to work with ctrµLua. +The outdated Lua API functions will be replaced if needed. +*/ + +#include +#include + +#include <3ds/types.h> + +#include "lua.h" +#include "lauxlib.h" + +#include "zlib.h" + +/* +** ========================================================================= +** compile time options wich determine available functionality +** ========================================================================= +*/ + +/* TODO + +- also call flush on table/userdata when flush function is detected +- remove io_cb check inflate_block if condition +- only set eos when ZSTREAM_END is reached +- check for stream errors to close stream when really needed + +*/ + + +/* +** ========================================================================= +** zlib stream metamethods +** ========================================================================= +*/ +#define ZSTREAMMETA "zlib:zstream" + +#define LZ_ANY -1 +#define LZ_NONE 0 +#define LZ_DEFLATE 1 +#define LZ_INFLATE 2 + +#if 0 + #define LZ_BUFFER_SIZE LUAL_BUFFERSIZE +#else + #define LZ_BUFFER_SIZE 8192 +#endif + +typedef struct { + /* zlib structures */ + z_stream zstream; + /* stream state. LZ_DEFLATE | LZ_INFLATE */ + int state; + int error; + int peek; + int eos; + /* user callback source for reading/writing */ + int io_cb; + /* input buffer */ + int i_buffer_ref; + size_t i_buffer_pos; + size_t i_buffer_len; + const char *i_buffer; + /* output buffer */ + size_t o_buffer_len; + size_t o_buffer_max; + char o_buffer[LZ_BUFFER_SIZE]; + /* dictionary */ + const Bytef *dictionary; + size_t dictionary_len; +} lz_stream; + + +/* forward declarations */ +static int lzstream_docompress(lua_State *L, lz_stream *s, int from, int to, int flush); + + +static lz_stream *lzstream_new(lua_State *L, int src) { + lz_stream *s = (lz_stream*)lua_newuserdata(L, sizeof(lz_stream)); + + luaL_getmetatable(L, ZSTREAMMETA); + lua_setmetatable(L, -2); /* set metatable */ + + s->state = LZ_NONE; + s->error = Z_OK; + s->eos = 0; + s->io_cb = LUA_REFNIL; + + s->i_buffer = NULL; + s->i_buffer_ref = LUA_REFNIL; + s->i_buffer_pos = 0; + s->i_buffer_len = 0; + + s->peek = 0; + s->o_buffer_len = 0; + s->o_buffer_max = sizeof(s->o_buffer) / sizeof(s->o_buffer[0]); + + s->zstream.zalloc = Z_NULL; + s->zstream.zfree = Z_NULL; + + /* prepare source */ + if (lua_isstring(L, src)) { + lua_pushvalue(L, src); + s->i_buffer_ref = luaL_ref(L, LUA_REGISTRYINDEX); + s->i_buffer = lua_tolstring(L, src, &s->i_buffer_len); + } else { + /* table | function | userdata */ + lua_pushvalue(L, src); + s->io_cb = luaL_ref(L, LUA_REGISTRYINDEX); + } + return s; +} + +static void lzstream_cleanup(lua_State *L, lz_stream *s) { + if (s && s->state != LZ_NONE) { + if (s->state == LZ_INFLATE) { + inflateEnd(&s->zstream); + } + if (s->state == LZ_DEFLATE) { + deflateEnd(&s->zstream); + } + + luaL_unref(L, LUA_REGISTRYINDEX, s->io_cb); + luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref); + s->state = LZ_NONE; + } +} + +/* ====================================================================== */ + +static lz_stream *lzstream_get(lua_State *L, int index) { + lz_stream *s = (lz_stream*)luaL_checkudata(L, index, ZSTREAMMETA); + if (s == NULL) luaL_argerror(L, index, "bad zlib stream"); + return s; +} + +static lz_stream *lzstream_check(lua_State *L, int index, int state) { + lz_stream *s = lzstream_get(L, index); + if ((state != LZ_ANY && s->state != state) || s->state == LZ_NONE) { + luaL_argerror(L, index, "attempt to use invalid zlib stream"); + } + return s; +} + +/* ====================================================================== */ + +static int lzstream_tostring(lua_State *L) { + lz_stream *s = (lz_stream*)luaL_checkudata(L, 1, ZSTREAMMETA); + if (s == NULL) luaL_argerror(L, 1, "bad zlib stream"); + + if (s->state == LZ_NONE) { + lua_pushstring(L, "zlib stream (closed)"); + } else if (s->state == LZ_DEFLATE) { + lua_pushfstring(L, "zlib deflate stream (%p)", (void*)s); + } else if (s->state == LZ_INFLATE) { + lua_pushfstring(L, "zlib inflate stream (%p)", (void*)s); + } else { + lua_pushfstring(L, "%p", (void*)s); + } + + return 1; +} + +/* ====================================================================== */ + +static int lzstream_gc(lua_State *L) { + lz_stream *s = lzstream_get(L, 1); + lzstream_cleanup(L, s); + return 0; +} + +/* ====================================================================== */ + +static int lzstream_close(lua_State *L) { + lz_stream *s = lzstream_get(L, 1); + + if (s->state == LZ_DEFLATE) { + lua_settop(L, 0); + lua_pushliteral(L, ""); + return lzstream_docompress(L, s, 1, 1, Z_FINISH); + } + + lzstream_cleanup(L, s); + lua_pushboolean(L, 1); + return 1; +} + +/* ====================================================================== */ + +static int lzstream_adler(lua_State *L) { + lz_stream *s = lzstream_check(L, 1, LZ_ANY); + lua_pushnumber(L, s->zstream.adler); + return 1; +} + +/* ====================================================================== */ + +/* + zlib.deflate( + sink: function | { write: function [, close: function, flush: function] }, + compression level, [Z_DEFAILT_COMPRESSION] + method, [Z_DEFLATED] + windowBits, [15] + memLevel, [8] + strategy, [Z_DEFAULT_STRATEGY] + dictionary: [""] + ) +*/ +static int lzlib_deflate(lua_State *L) { + int level, method, windowBits, memLevel, strategy; + lz_stream *s; + const char *dictionary; + size_t dictionary_len; + + if (lua_istable(L, 1) || lua_isuserdata(L, 1)) { + /* is there a :write function? */ + lua_getfield(L, 1, "write"); + if (!lua_isfunction(L, -1)) { + luaL_argerror(L, 1, "output parameter does not provide :write function"); + } + lua_pop(L, 1); + } + else if (!lua_isfunction(L, 1)) { + luaL_argerror(L, 1, "output parameter must be a function, table or userdata value"); + } + + level = (int) luaL_optinteger(L, 2, Z_DEFAULT_COMPRESSION); + method = (int) luaL_optinteger(L, 3, Z_DEFLATED); + windowBits = (int) luaL_optinteger(L, 4, 15); + memLevel = (int) luaL_optinteger(L, 5, 8); + strategy = (int) luaL_optinteger(L, 6, Z_DEFAULT_STRATEGY); + dictionary = luaL_optlstring(L, 7, NULL, &dictionary_len); + + s = lzstream_new(L, 1); + + if (deflateInit2(&s->zstream, level, method, windowBits, memLevel, strategy) != Z_OK) { + lua_pushliteral(L, "call to deflateInit2 failed"); + lua_error(L); + } + + if (dictionary) { + if (deflateSetDictionary(&s->zstream, (const Bytef *) dictionary, dictionary_len) != Z_OK) { + lua_pushliteral(L, "call to deflateSetDictionnary failed"); + lua_error(L); + } + } + + s->state = LZ_DEFLATE; + return 1; +} + +/* + zlib.inflate( + source: string | function | { read: function, close: function }, + windowBits: number, [15] + dictionary: [""] + ) +*/ +static int lzlib_inflate(lua_State *L) +{ + int windowBits; + lz_stream *s; + int have_peek = 0; + const char *dictionary; + size_t dictionary_len; + + if (lua_istable(L, 1) || lua_isuserdata(L, 1)) { + /* is there a :read function? */ + lua_getfield(L, 1, "read"); + if (!lua_isfunction(L, -1)) { + luaL_argerror(L, 1, "input parameter does not provide :read function"); + } + lua_pop(L, 1); + /* check for peek function */ + lua_getfield(L, 1, "peek"); + have_peek = lua_isfunction(L, -1); + lua_pop(L, 1); + } + else if (!lua_isstring(L, 1) && !lua_isfunction(L, 1)) { + luaL_argerror(L, 1, "input parameter must be a string, function, table or userdata value"); + } + + windowBits = (int) luaL_optinteger(L, 2, 15); + dictionary = luaL_optlstring(L, 3, NULL, &dictionary_len); + + s = lzstream_new(L, 1); + + if (windowBits > 0 && windowBits < 16) { + windowBits |= 32; + } + + if (inflateInit2(&s->zstream, windowBits) != Z_OK) { + lua_pushliteral(L, "call to inflateInit2 failed"); + lua_error(L); + } + + if (dictionary) { + s->dictionary = (const Bytef *) dictionary; + s->dictionary_len = dictionary_len; + } + + s->peek = have_peek; + s->state = LZ_INFLATE; + return 1; +} + +/* ====================================================================== */ + +static int lz_pushresult (lua_State *L, lz_stream *s) { + if (s->error == Z_OK) { + lua_pushboolean(L, 1); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, zError(s->error)); + lua_pushinteger(L, s->error); + return 3; + } +} + +/* + Get block to process: + - top of stack gets +*/ +static const char* lzstream_fetch_block(lua_State *L, lz_stream *s, int hint) { + if (s->i_buffer_pos >= s->i_buffer_len) { + luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref); + s->i_buffer_ref = LUA_NOREF; + s->i_buffer = NULL; + + lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb); + if (!lua_isnil(L, -1)) { + if (lua_isfunction(L, -1)) { + lua_pushinteger(L, hint); + lua_call(L, 1, 1); + } else { + lua_getfield(L, -1, (s->peek ? "peek" : "read")); + lua_insert(L, -2); + lua_pushinteger(L, hint); + lua_call(L, 2, 1); + } + + if (lua_isstring(L, -1)) { + s->i_buffer_pos = 0; + s->i_buffer = lua_tolstring(L, -1, &s->i_buffer_len); + if (s->i_buffer_len > 0) { + s->i_buffer_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } else { + lua_pop(L, 1); + } + } else if (lua_isnil(L, -1)) { + lua_pop(L, 1); + } else { + lua_pushliteral(L, "deflate callback must return string or nil"); + lua_error(L); + } + } else { + lua_pop(L, 1); + } + } + + return s->i_buffer; +} + +static int lzstream_inflate_block(lua_State *L, lz_stream *s) { + if (lzstream_fetch_block(L, s, LZ_BUFFER_SIZE) || !s->eos) { + int r; + + if (s->i_buffer_len == s->i_buffer_pos) { + s->zstream.next_in = NULL; + s->zstream.avail_in = 0; + } else { + s->zstream.next_in = (unsigned char*)(s->i_buffer + s->i_buffer_pos); + s->zstream.avail_in = s->i_buffer_len - s->i_buffer_pos; + } + + s->zstream.next_out = (unsigned char*)s->o_buffer + s->o_buffer_len; + s->zstream.avail_out = s->o_buffer_max - s->o_buffer_len; + + /* munch some more */ + r = inflate(&s->zstream, Z_SYNC_FLUSH); + + if (r == Z_NEED_DICT) { + if (s->dictionary == NULL) { + lua_pushliteral(L, "no inflate dictionary provided"); + lua_error(L); + } + + if (inflateSetDictionary(&s->zstream, s->dictionary, s->dictionary_len) != Z_OK) { + lua_pushliteral(L, "call to inflateSetDictionnary failed"); + lua_error(L); + } + + r = inflate(&s->zstream, Z_SYNC_FLUSH); + } + + if (r != Z_OK && r != Z_STREAM_END && r != Z_BUF_ERROR) { + lzstream_cleanup(L, s); + s->error = r; + #if 1 + lua_pushfstring(L, "failed to decompress [%d]", r); + lua_error(L); + #endif + } + + if (r == Z_STREAM_END) { + luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref); + s->i_buffer_ref = LUA_NOREF; + s->i_buffer = NULL; + + s->eos = 1; + } + + /* number of processed bytes */ + if (s->peek) { + size_t processed = s->i_buffer_len - s->i_buffer_pos - s->zstream.avail_in; + + lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb); + lua_getfield(L, -1, "read"); + lua_insert(L, -2); + lua_pushinteger(L, processed); + lua_call(L, 2, 0); + } + + s->i_buffer_pos = s->i_buffer_len - s->zstream.avail_in; + s->o_buffer_len = s->o_buffer_max - s->zstream.avail_out; + } + + return s->o_buffer_len; +} + +/* +** Remove n bytes from the output buffer. +*/ +static void lzstream_remove(lz_stream *s, size_t n) { + memmove(s->o_buffer, s->o_buffer + n, s->o_buffer_len - n); + s->o_buffer_len -= n; +} + +/* +** Copy at most n bytes to buffer b and remove them from the +** output stream buffer. +*/ +static int lzstream_flush_buffer(lua_State *L, lz_stream *s, size_t n, luaL_Buffer *b) { + /* check output */ + if (n > s->o_buffer_len) { + n = s->o_buffer_len; + } + + if (n > 0) { + lua_pushlstring(L, s->o_buffer, n); + luaL_addvalue(b); + + lzstream_remove(s, n); + } + + return n; +} + +/* + z:read( + {number | '*l' | '*a'}* + ) +*/ +static int lz_test_eof(lua_State *L, lz_stream *s) { + lua_pushlstring(L, NULL, 0); + if (s->o_buffer_len > 0) { + return 1; + } else if (s->eos) { + return 0; + } else { + return lzstream_inflate_block(L, s); + } +} + +static int lz_read_line(lua_State *L, lz_stream *s) { + luaL_Buffer b; + size_t l = 0, n; + + luaL_buffinit(L, &b); + + if (s->o_buffer_len > 0 || !s->eos) do { + char *p = s->o_buffer; + size_t len = s->o_buffer_len; + + /* find newline in output buffer */ + for (n = 0; n < len; ++n, ++p) { + if (*p == '\n' || *p == '\r') { + int eat_nl = *p == '\r'; + luaL_addlstring(&b, s->o_buffer, n); + lzstream_remove(s, n+1); + l += n; + + if (eat_nl && lzstream_inflate_block(L, s)) { + if (s->o_buffer_len > 0 && *s->o_buffer == '\n') { + lzstream_remove(s, 1); + } + } + + luaL_pushresult(&b); + return 1; + } + } + + if (len > 0) { + luaL_addlstring(&b, s->o_buffer, len); + lzstream_remove(s, len); + l += len; + } + } while (lzstream_inflate_block(L, s)); + + luaL_pushresult(&b); + return l > 0 || !s->eos || s->o_buffer_len > 0; +} + + +static int lz_read_chars(lua_State *L, lz_stream *s, size_t n) { + size_t len; + luaL_Buffer b; + luaL_buffinit(L, &b); + + if (s->o_buffer_len > 0 || !s->eos) do { + size_t rlen = lzstream_flush_buffer(L, s, n, &b); + n -= rlen; + } while (n > 0 && lzstream_inflate_block(L, s)); + + luaL_pushresult(&b); + lua_tolstring(L, -1, &len); + return n == 0 || len > 0; +} + +static int lzstream_decompress(lua_State *L) { + lz_stream *s = lzstream_check(L, 1, LZ_INFLATE); + int nargs = lua_gettop(L) - 1; + int success; + int n; + if (nargs == 0) { /* no arguments? */ + success = lz_read_line(L, s); + n = 3; /* to return 1 result */ + } + else { /* ensure stack space for all results and for auxlib's buffer */ + luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); + success = 1; + for (n = 2; nargs-- && success; n++) { + if (lua_type(L, n) == LUA_TNUMBER) { + size_t l = (size_t)lua_tointeger(L, n); + success = (l == 0) ? lz_test_eof(L, s) : lz_read_chars(L, s, l); + } + else { + const char *p = lua_tostring(L, n); + luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); + switch (p[1]) { + case 'l': /* line */ + success = lz_read_line(L, s); + break; + case 'a': /* file */ + lz_read_chars(L, s, ~((size_t)0)); /* read MAX_SIZE_T chars */ + success = 1; /* always success */ + break; + default: + return luaL_argerror(L, n, "invalid format"); + } + } + } + } + if (s->error != Z_OK) { + return lz_pushresult(L, s); + } + if (!success) { + lua_pop(L, 1); /* remove last result */ + lua_pushnil(L); /* push nil instead */ + } + return n - 2; +} + + +static int lzstream_readline(lua_State *L) { + lz_stream *s; + int sucess; + + s = lzstream_check(L, lua_upvalueindex(1), LZ_INFLATE); + sucess = lz_read_line(L, s); + + if (s->error != Z_OK) { + return lz_pushresult(L, s); + } + + if (sucess) { + return 1; + } else { + /* EOF */ + return 0; + } +} + +static int lzstream_lines(lua_State *L) { + lzstream_check(L, 1, LZ_INFLATE); + lua_settop(L, 1); + lua_pushcclosure(L, lzstream_readline, 1); + return 1; +} + +/* ====================================================================== */ + +static int lzstream_docompress(lua_State *L, lz_stream *s, int from, int to, int flush) { + int r, arg; + int self = 0; + size_t b_size = s->o_buffer_max; + unsigned char *b = (unsigned char *)s->o_buffer; + + /* number of processed bytes */ + lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb); + if (!lua_isfunction(L, -1)) { + self = 1; + lua_getfield(L, -1, "write"); + } + + for (arg = from; arg <= to; arg++) { + s->zstream.next_in = (unsigned char*)luaL_checklstring(L, arg, (size_t*)&s->zstream.avail_in); + + do { + s->zstream.next_out = b; + s->zstream.avail_out = b_size; + + /* bake some more */ + r = deflate(&s->zstream, flush); + if (r != Z_OK && r != Z_STREAM_END && r != Z_BUF_ERROR) { + lzstream_cleanup(L, s); + lua_pushboolean(L, 0); + lua_pushfstring(L, "failed to compress [%d]", r); + return 2; + } + + if (s->zstream.avail_out != b_size) { + /* write output */ + lua_pushvalue(L, -1); /* function */ + if (self) lua_pushvalue(L, -3); /* self */ + lua_pushlstring(L, (char*)b, b_size - s->zstream.avail_out); /* data */ + lua_call(L, (self ? 2 : 1), 0); + } + + if (r == Z_STREAM_END) { + lzstream_cleanup(L, s); + break; + } + + /* process all input */ + } while (s->zstream.avail_in > 0 || s->zstream.avail_out == 0); + } + + lua_pushboolean(L, 1); + return 1; +} + +static int lzstream_compress(lua_State *L) { + lz_stream *s = lzstream_check(L, 1, LZ_DEFLATE); + return lzstream_docompress(L, s, 2, lua_gettop(L), Z_NO_FLUSH); +} + + +/* ====================================================================== */ + +static int lzstream_flush(lua_State *L) { + static int flush_values[] = { Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH }; + static const char *const flush_opts[] = { "sync", "full", "finish" }; + + lz_stream *s = lzstream_check(L, 1, LZ_DEFLATE); + int flush = luaL_checkoption(L, 2, flush_opts[0], flush_opts); + + lua_settop(L, 0); + lua_pushliteral(L, ""); + return lzstream_docompress(L, s, 1, 1, flush_values[flush]); +} + +/* +** ========================================================================= +** zlib functions +** ========================================================================= +*/ + +static int lzlib_version(lua_State *L) +{ + lua_pushstring(L, zlibVersion()); + return 1; +} + +/* ====================================================================== */ +static int lzlib_adler32(lua_State *L) +{ + if (lua_gettop(L) == 0) + { + /* adler32 initial value */ + lua_pushnumber(L, adler32(0L, Z_NULL, 0)); + } + else + { + /* update adler32 checksum */ + size_t len; + int adler = (int) luaL_checkinteger(L, 1); + const unsigned char* buf = (unsigned char*)luaL_checklstring(L, 2, &len); + + lua_pushnumber(L, adler32(adler, buf, len)); + } + return 1; +} + +/* ====================================================================== */ +static int lzlib_crc32(lua_State *L) +{ + if (lua_gettop(L) == 0) + { + /* crc32 initial value */ + lua_pushnumber(L, crc32(0L, Z_NULL, 0)); + } + else + { + /* update crc32 checksum */ + size_t len; + int crc = (int) luaL_checkinteger(L, 1); + const unsigned char* buf = (unsigned char*)luaL_checklstring(L, 2, &len); + + lua_pushnumber(L, crc32(crc, buf, len)); + } + return 1; +} + +/* ====================================================================== */ + + +static int lzlib_compress(lua_State *L) { + size_t avail_in; + const char *next_in = luaL_checklstring(L, 1, &avail_in); + int level = (int) luaL_optinteger(L, 2, Z_DEFAULT_COMPRESSION); + int method = (int) luaL_optinteger(L, 3, Z_DEFLATED); + int windowBits = (int) luaL_optinteger(L, 4, 15); + int memLevel = (int) luaL_optinteger(L, 5, 8); + int strategy = (int) luaL_optinteger(L, 6, Z_DEFAULT_STRATEGY); + + int ret; + luaL_Buffer b; + z_stream zs; + + luaL_buffinit(L, &b); + + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + + zs.next_out = Z_NULL; + zs.avail_out = 0; + zs.next_in = Z_NULL; + zs.avail_in = 0; + + ret = deflateInit2(&zs, level, method, windowBits, memLevel, strategy); + + if (ret != Z_OK) + { + lua_pushnil(L); + lua_pushnumber(L, ret); + return 2; + } + + zs.next_in = (unsigned char*)next_in; + zs.avail_in = avail_in; + + for(;;) + { + zs.next_out = (unsigned char*)luaL_prepbuffer(&b); + zs.avail_out = LUAL_BUFFERSIZE; + + /* munch some more */ + ret = deflate(&zs, Z_FINISH); + + /* push gathered data */ + luaL_addsize(&b, LUAL_BUFFERSIZE - zs.avail_out); + + /* done processing? */ + if (ret == Z_STREAM_END) + break; + + /* error condition? */ + if (ret != Z_OK) + break; + } + + /* cleanup */ + deflateEnd(&zs); + + luaL_pushresult(&b); + lua_pushnumber(L, ret); + return 2; +} + +/* ====================================================================== */ + +static int lzlib_decompress(lua_State *L) +{ + size_t avail_in; + const char *next_in = luaL_checklstring(L, 1, &avail_in); + int windowBits = (int) luaL_optinteger(L, 2, 15); + + int ret; + luaL_Buffer b; + z_stream zs; + + luaL_buffinit(L, &b); + + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + + zs.next_out = Z_NULL; + zs.avail_out = 0; + zs.next_in = Z_NULL; + zs.avail_in = 0; + + ret = inflateInit2(&zs, windowBits); + + if (ret != Z_OK) { + lua_pushliteral(L, "failed to initialize zstream structures"); + lua_error(L); + } + + zs.next_in = (unsigned char*)next_in; + zs.avail_in = avail_in; + + for (;;) { + zs.next_out = (unsigned char*)luaL_prepbuffer(&b); + zs.avail_out = LUAL_BUFFERSIZE; + + /* bake some more */ + ret = inflate(&zs, Z_FINISH); + + /* push gathered data */ + luaL_addsize(&b, LUAL_BUFFERSIZE - zs.avail_out); + + /* done processing? */ + if (ret == Z_STREAM_END) + break; + + if (ret != Z_OK && ret != Z_BUF_ERROR) { + /* cleanup */ + inflateEnd(&zs); + + lua_pushliteral(L, "failed to process zlib stream"); + lua_error(L); + } + } + + /* cleanup */ + inflateEnd(&zs); + + luaL_pushresult(&b); + return 1; +} + + +/* +** ========================================================================= +** Register functions +** ========================================================================= +*/ + +#if (LUA_VERSION_NUM >= 502) + +#define luaL_register(L,n,f) luaL_setfuncs(L,f,0) + +#endif + +LUALIB_API int luaopen_zlib(lua_State *L) +{ + const luaL_Reg lzstream_meta[] = + { + {"write", lzstream_compress }, + {"read", lzstream_decompress }, + {"lines", lzstream_lines }, + {"flush", lzstream_flush }, + {"close", lzstream_close }, + + {"adler", lzstream_adler }, + + {"__tostring", lzstream_tostring }, + {"__gc", lzstream_gc }, + {NULL, NULL} + }; + + const luaL_Reg zlib[] = + { + {"version", lzlib_version }, + {"adler32", lzlib_adler32 }, + {"crc32", lzlib_crc32 }, + + {"deflate", lzlib_deflate }, + {"inflate", lzlib_inflate }, + + {"compress", lzlib_compress }, + {"decompress", lzlib_decompress }, + + {NULL, NULL} + }; + + /* ====================================================================== */ + + /* create new metatable for zlib compression structures */ + luaL_newmetatable(L, ZSTREAMMETA); + lua_pushliteral(L, "__index"); + lua_pushvalue(L, -2); /* push metatable */ + lua_rawset(L, -3); /* metatable.__index = metatable */ + + /* + ** Stack: metatable + */ + luaL_register(L, NULL, lzstream_meta); + + lua_pop(L, 1); /* remove metatable from stack */ + + /* + ** Stack: + */ + lua_newtable(L); + + lua_pushliteral (L, "_COPYRIGHT"); + lua_pushliteral (L, "Copyright (C) 2003-2010 Tiago Dionizio"); + lua_settable (L, -3); + lua_pushliteral (L, "_DESCRIPTION"); + lua_pushliteral (L, "Lua 5 interface to access zlib library functions"); + lua_settable (L, -3); + lua_pushliteral (L, "_VERSION"); + lua_pushliteral (L, "lzlib 0.4-work3"); + lua_settable (L, -3); + +#define PUSH_LITERAL(name) \ + lua_pushliteral (L, #name); \ + lua_pushinteger (L, Z_##name); \ + lua_settable (L, -3); + +#define PUSH_NUMBER(name, value) \ + lua_pushliteral (L, #name); \ + lua_pushinteger (L, value); \ + lua_settable (L, -3); + + PUSH_LITERAL(NO_COMPRESSION) + PUSH_LITERAL(BEST_SPEED) + PUSH_LITERAL(BEST_COMPRESSION) + PUSH_LITERAL(DEFAULT_COMPRESSION) + + PUSH_LITERAL(FILTERED) + PUSH_LITERAL(HUFFMAN_ONLY) + PUSH_LITERAL(RLE) + PUSH_LITERAL(FIXED) + PUSH_LITERAL(DEFAULT_STRATEGY) + + PUSH_NUMBER(MINIMUM_MEMLEVEL, 1) + PUSH_NUMBER(MAXIMUM_MEMLEVEL, 9) + PUSH_NUMBER(DEFAULT_MEMLEVEL, 8) + + PUSH_NUMBER(DEFAULT_WINDOWBITS, 15) + PUSH_NUMBER(MINIMUM_WINDOWBITS, 8) + PUSH_NUMBER(MAXIMUM_WINDOWBITS, 15) + + PUSH_NUMBER(GZIP_WINDOWBITS, 16) + PUSH_NUMBER(RAW_WINDOWBITS, -1) + + luaL_register(L, NULL, zlib); + + /* + ** Stack: zlib table + */ + return 1; +} + +void load_lzlib(lua_State *L) { + luaL_requiref(L, "ctr.fs.lzlib", luaopen_zlib, false); +} From b47634f6992e98fb693f1c37cac1d168603f6891 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Tue, 15 Sep 2015 23:10:15 +0200 Subject: [PATCH 011/101] Fixed the documentation error with lzlib, Added gfx.get3D(), Enhanced the ir library errors returns. --- sdcard/3ds/ctruLua/openfile.lua | 4 +-- source/gfx.c | 16 ++++++++++-- source/ir.c | 45 ++++++++++++++++++++++++++++----- source/lzlib.c | 6 +++++ 4 files changed, 60 insertions(+), 11 deletions(-) diff --git a/sdcard/3ds/ctruLua/openfile.lua b/sdcard/3ds/ctruLua/openfile.lua index c913944..32a924e 100644 --- a/sdcard/3ds/ctruLua/openfile.lua +++ b/sdcard/3ds/ctruLua/openfile.lua @@ -26,7 +26,7 @@ return function(title, curdir, exts, type) local ret = nil -- Remember and set defaults - --local was3D = gfx.get3D() TODO: implement this thing in ctruLua + local was3D = gfx.get3D() local wasDefault = gfx.color.getDefault() local wasBackground = gfx.color.getBackground() local wasFont = gfx.font.getDefault() @@ -129,7 +129,7 @@ return function(title, curdir, exts, type) end -- Reset defaults - --gfx.set3D(was3D) + gfx.set3D(was3D) gfx.color.setDefault(wasDefault) gfx.color.setBackground(wasBackground) gfx.font.setDefault(wasFont) diff --git a/source/gfx.c b/source/gfx.c index f070648..be1310a 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -16,6 +16,7 @@ The `gfx` module. #include "font.h" bool isGfxInitialised = false; +bool is3DEnabled = false; //TODO: add a function for this in the ctrulib/sf2dlib. /*** The `ctr.gfx.color` module. @@ -103,13 +104,23 @@ Enable or disable the stereoscopic 3D on the top screen. @tparam boolean enable true to enable, false to disable */ static int gfx_set3D(lua_State *L) { - bool enable = lua_toboolean(L, 1); + is3DEnabled = lua_toboolean(L, 1); - sf2d_set_3D(enable); + sf2d_set_3D(is3DEnabled); return 0; } +/*** +Check whether or not the stereoscopic 3D is enabled. +@function get3D +@treturn boolean true if enabled, false if disabled +*/ +static int gfx_get3D(lua_State *L) { + lua_pushboolean(L, is3DEnabled); + return 1; +} + /*** Get free VRAM space. @function vramSpaceFree @@ -251,6 +262,7 @@ static const struct luaL_Reg gfx_lib[] = { { "render", gfx_render }, { "getFPS", gfx_getFPS }, { "set3D", gfx_set3D }, + { "get3D", gfx_get3D }, { "vramSpaceFree", gfx_vramSpaceFree }, { "line", gfx_line }, { "point", gfx_point }, diff --git a/source/ir.c b/source/ir.c index 40cd490..c58333c 100644 --- a/source/ir.c +++ b/source/ir.c @@ -43,6 +43,11 @@ Initialize the IR module. static int ir_init(lua_State *L) { u8 bitrate = luaL_optinteger(L, 1, 6); bufferSize = luaL_optinteger(L, 2, 2048); //default: 2Kio + if (bufferSize > 2048) { + lua_pushboolean(L, false); + lua_pushstring(L, "the buffer can't be larger than 2048 bytes."); + return 2; + } buffer = linearAlloc(bufferSize); Result ret = IRU_Initialize(buffer, bufferSize); @@ -62,9 +67,15 @@ Disable the IR module. @function shutdown */ static int ir_shutdown(lua_State *L) { - IRU_Shutdown(); + Result ret = IRU_Shutdown(); + if (ret) { + lua_pushboolean(L, false); + lua_pushinteger(L, ret); + return 2; + } - return 0; + lua_pushboolean(L, true); + return 1; } /*** @@ -77,7 +88,12 @@ static int ir_send(lua_State *L) { u8 *data = (u8*)luaL_checkstring(L, 1); u32 wait = lua_toboolean(L, 2); - IRU_SendData(data, sizeof(data), wait); + Result ret = IRU_SendData(data, sizeof(data), wait); + if (ret) { + lua_pushboolean(L, false); + lua_pushinteger(L, ret); + return 2; + } return 0; } @@ -89,12 +105,17 @@ Receive some data from the IR module. @tparam[opt=false] boolean wait wait until the data is received */ static int ir_receive(lua_State *L) { - u32 size = luaL_optinteger(L, 1, 0x800); + u32 size = luaL_optinteger(L, 1, bufferSize); u32 wait = lua_toboolean(L, 2); u8 *data = 0; u32 *transfercount = 0; - IRU_RecvData(data, size, 0x00, transfercount, wait); + Result ret = IRU_RecvData(data, size, 0x00, transfercount, wait); + if (ret) { + lua_pushboolean(L, false); + lua_pushinteger(L, ret); + return 2; + } lua_pushstring(L, (const char *)data); @@ -109,7 +130,12 @@ Set the bitrate of the communication. static int ir_setBitRate(lua_State *L) { u8 bitrate = luaL_checkinteger(L, 1); - IRU_SetBitRate(bitrate); + Result ret = IRU_SetBitRate(bitrate); + if (ret) { + lua_pushboolean(L, false); + lua_pushinteger(L, ret); + return 2; + } return 0; } @@ -122,7 +148,12 @@ Return the actual bitrate of the communication. static int ir_getBitRate(lua_State *L) { u8 bitrate = 0; - IRU_GetBitRate(&bitrate); + Result ret = IRU_GetBitRate(&bitrate); + if (ret) { + lua_pushboolean(L, false); + lua_pushinteger(L, ret); + return 2; + } lua_pushinteger(L, bitrate); diff --git a/source/lzlib.c b/source/lzlib.c index b73dee1..d045b1c 100644 --- a/source/lzlib.c +++ b/source/lzlib.c @@ -1,3 +1,9 @@ +/*** +The `fs.lzlib` module. See https://github.com/LuaDist/lzlib for informations and documentation. +@module ctr.fs.lzlib +@usage local lzlib = require("ctr.fs.lzlib") +*/ + /************************************************************************ * Author : Tiago Dionizio * * Library : lzlib - Lua 5 interface to access zlib library functions * From a3ab8253659f46e0e8ec9a97955a320428a11a39 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Sun, 20 Sep 2015 18:23:44 +0200 Subject: [PATCH 012/101] Documented the HTTPC library, and maybe fixed it (couldn't test, sorry) --- source/httpc.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/source/httpc.c b/source/httpc.c index 8296cc2..756c069 100644 --- a/source/httpc.c +++ b/source/httpc.c @@ -1,3 +1,8 @@ +/*** +The `httpc` module. +@module ctr.httpc +@usage local httpc = require("ctr.httpc") +*/ #include #include @@ -8,6 +13,11 @@ #include #include +/*** +Create a HTTP Context. +@function context +@treturn context a new http context +*/ static int httpc_context(lua_State *L) { httpcContext context; Result ret = httpcOpenContext(&context, "http://google.com/", 0); // Initialization only. @@ -23,6 +33,16 @@ static int httpc_context(lua_State *L) { return 1; } +/*** +context object +@section Methods +*/ + +/*** +Open an url in the context. +@function :open +@tparam string url the url to open +*/ static int httpc_open(lua_State *L) { httpcContext *context = lua_touserdata(L, 1); char *url = (char*)luaL_checkstring(L, 2); @@ -38,6 +58,10 @@ static int httpc_open(lua_State *L) { return 1; } +/*** +Begin a request to get the content at the URL. +@function :beginRequest +*/ static int httpc_beginRequest(lua_State *L) { httpcContext *context = lua_touserdata(L, 1); Result ret = 0; @@ -52,6 +76,11 @@ static int httpc_beginRequest(lua_State *L) { return 1; } +/*** +Return the status code returned by the request. +@function :getStatusCode +@treturn number the status code +*/ static int httpc_getStatusCode(lua_State *L) { httpcContext *context = lua_touserdata(L, 1); u32 statusCode = 0; @@ -66,6 +95,11 @@ static int httpc_getStatusCode(lua_State *L) { return 1; } +/*** +Return the amount of data to download. +@function :getDownloadSize +@treturn number size in (bytes) +*/ static int httpc_getDownloadSize(lua_State *L) { httpcContext *context = lua_touserdata(L, 1); u32 contentSize = 0; @@ -76,6 +110,11 @@ static int httpc_getDownloadSize(lua_State *L) { return 1; } +/*** +Download and return the data of the context. +@function :downloadData +@treturn string data +*/ static int httpc_downloadData(lua_State *L) { httpcContext *context = lua_touserdata(L, 1); u32 status = 0; @@ -98,11 +137,15 @@ static int httpc_downloadData(lua_State *L) { } lua_pushstring(L, (char*)buff); - free(buff); + //free(buff); lua_pushinteger(L, size); // only for test purposes. return 2; } +/*** +Close the context. +@function :close +*/ static int httpc_close(lua_State *L) { httpcContext *context = lua_touserdata(L, 1); From 5d0e2a7d81d73d0cc75102a6af7a9710d0472fb6 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Mon, 28 Sep 2015 22:37:03 +0200 Subject: [PATCH 013/101] Documented the font library, Improved the news library (still unusable) --- source/font.c | 39 ++++++++++++++++++++++++++++++++++++++- source/news.c | 18 ++++++++++++------ 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/source/font.c b/source/font.c index c61db9e..4b45da1 100644 --- a/source/font.c +++ b/source/font.c @@ -1,3 +1,9 @@ +/*** +The `font` module +@module ctr.gfx.font +@usage local font = require("ctr.gfx.font") +*/ + #include #include @@ -9,6 +15,12 @@ #include "font.h" +/*** +Load a TTF font. +@function load +@tparam string path path to the file +@treturn font the loaded font. +*/ static int font_load(lua_State *L) { const char *path = luaL_checkstring(L, 1); @@ -28,6 +40,11 @@ static int font_load(lua_State *L) { return 1; } +/*** +Set a font as the default one. +@function setDefault +@tparam font font the font to set as the default one. +*/ static int font_setDefault(lua_State *L) { if (luaL_testudata(L, 1, "LFont") == NULL) { font_userdata *font = lua_newuserdata(L, sizeof(*font)); @@ -42,12 +59,28 @@ static int font_setDefault(lua_State *L) { return 0; } +/*** +Return the default font. +@function getDefault +@treturn font default font +*/ static int font_getDefault(lua_State *L) { lua_getfield(L, LUA_REGISTRYINDEX, "LFontDefault"); return 1; } +/*** +font object +@section Methods +*/ + +/*** +Return the width of a string with a font. +@function :object_width +@tparam string text the text to test +@treturn number the width of the text (in pixels) +*/ static int font_object_width(lua_State *L) { font_userdata *font = luaL_checkudata(L, 1, "LFont"); if (font->font == NULL) luaL_error(L, "The font object was unloaded"); @@ -67,6 +100,10 @@ static int font_object_width(lua_State *L) { return 1; } +/*** +Unload a font. +@function :unload +*/ static int font_object_unload(lua_State *L) { font_userdata *font = luaL_checkudata(L, 1, "LFont"); if (font->font == NULL) return 0; @@ -123,4 +160,4 @@ void unload_font_lib(lua_State *L) { if (luaL_testudata(L, -1, "LFont") != NULL) sftd_free_font(((font_userdata *)lua_touserdata(L, -1))->font); // Unload current font -} \ No newline at end of file +} diff --git a/source/news.c b/source/news.c index f55449c..18932d6 100644 --- a/source/news.c +++ b/source/news.c @@ -10,6 +10,8 @@ The `news` module. #include #include +#include + /*** Initialize the news module. @function init @@ -31,19 +33,23 @@ Send a notification to the user. WIP, do not use !!! static int news_notification(lua_State *L) { const char *title = luaL_checkstring(L, 1); const char *message = luaL_checkstring(L, 2); - const void *imageData = luaL_checkstring(L, 3); + const void *imageData = luaL_optstring(L, 3, NULL); bool jpeg = false; if (lua_isboolean(L, 4)) jpeg = lua_toboolean(L, 4); - lua_len(L, 3); - u32 imageDataLength = luaL_checkinteger(L, -1); - + + u32 imageDataLength = 0; + if (imageData) { + lua_len(L, 3); + luaL_checkinteger(L, -1); + } + const u16* cTitle = 0; const u16* cMessage = 0; u32 titleLength, messageLength; - titleLength = (u32) utf8_to_utf16((uint16_t*)cTitle, (uint8_t*)title, sizeof(title)); - messageLength = (u32) utf8_to_utf16((uint16_t*)cMessage, (uint8_t*)message, sizeof(message)); + titleLength = (u32) utf8_to_utf16((uint16_t*)cTitle, (uint8_t*)title, strlen(title)); + messageLength = (u32) utf8_to_utf16((uint16_t*)cMessage, (uint8_t*)message, strlen(message)); NEWSU_AddNotification(cTitle, titleLength, cMessage, messageLength, imageData, imageDataLength, jpeg); From 29ed61131d361ff2f0eec5432925d643d892ab12 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Mon, 28 Sep 2015 22:52:19 +0200 Subject: [PATCH 014/101] Oops, fixed an error in the gfx.font documentation --- source/font.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/font.c b/source/font.c index 4b45da1..be75d45 100644 --- a/source/font.c +++ b/source/font.c @@ -77,7 +77,7 @@ font object /*** Return the width of a string with a font. -@function :object_width +@function :width @tparam string text the text to test @treturn number the width of the text (in pixels) */ From b5036ecff87196b574736f7e294503536bfef46d Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Fri, 2 Oct 2015 08:38:35 +0200 Subject: [PATCH 015/101] Added "sdmc:/3ds/ctruLua/libs/" to the libraries path You can now require("sprite") ! --- libs/lua-5.3.1/src/luaconf.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libs/lua-5.3.1/src/luaconf.h b/libs/lua-5.3.1/src/luaconf.h index 74a9252..fb77880 100644 --- a/libs/lua-5.3.1/src/luaconf.h +++ b/libs/lua-5.3.1/src/luaconf.h @@ -188,12 +188,14 @@ #else /* }{ */ -#define LUA_ROOT "/usr/local/" -#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR "/" -#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR "/" +#define LUA_ROOT "sdmc:/" +#define LUA_LDIR LUA_ROOT "3ds/ctruLua/libs/" +#define LUA_CDIR LUA_ROOT "3ds/ctruLua/libs/" #define LUA_PATH_DEFAULT \ - LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ - LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \ + LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ + LUA_LDIR LUA_VDIR"?.lua;" LUA_LDIR LUA_VDIR"?/init.lua;" \ +/* LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \ + LUA_CDIR LUA_VDIR"?.lua;" LUA_CDIR LUA_VDIR"?/init.lua;" \ */ "./?.lua;" "./?/init.lua" #define LUA_CPATH_DEFAULT \ LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so" From fb0bd5fc8cc6c2296119af9f5cc6972f46288035 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Fri, 2 Oct 2015 08:43:54 +0200 Subject: [PATCH 016/101] How could I do that ... --- libs/lua-5.3.1/src/luaconf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/lua-5.3.1/src/luaconf.h b/libs/lua-5.3.1/src/luaconf.h index fb77880..9d075c6 100644 --- a/libs/lua-5.3.1/src/luaconf.h +++ b/libs/lua-5.3.1/src/luaconf.h @@ -195,7 +195,7 @@ LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ LUA_LDIR LUA_VDIR"?.lua;" LUA_LDIR LUA_VDIR"?/init.lua;" \ /* LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \ - LUA_CDIR LUA_VDIR"?.lua;" LUA_CDIR LUA_VDIR"?/init.lua;" \ */ + LUA_CDIR LUA_VDIR"?.lua;" LUA_CDIR LUA_VDIR"?/init.lua;" */ \ "./?.lua;" "./?/init.lua" #define LUA_CPATH_DEFAULT \ LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so" From 48afdc07b5c24d662b707abea89e965a859bee0b Mon Sep 17 00:00:00 2001 From: Ruairidh Carmichael Date: Fri, 2 Oct 2015 14:20:53 +0100 Subject: [PATCH 017/101] Multiple image types --- source/texture.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/source/texture.c b/source/texture.c index dc27549..8d37187 100644 --- a/source/texture.c +++ b/source/texture.c @@ -13,9 +13,19 @@ The `gfx.texture` module. #include "texture.h" -u8 getType(const char *name) { - // NYI, always return the PNG type, because PNG is the best type. - return 0; +int getType(const char *name) { + const char *dot = strrchr(name, '.'); + if(!dot || dot == name) dot = ""; + const char *ext = dot + 1; + if (strncmp(ext, "png", 3) == 0) { + return 0; + } else if (strncmp(ext, "jpeg", 4) == 0 || strncmp(ext, "jpg", 3) == 0) { + return 1; + } else if (strncmp(ext, "bmp", 3) == 0) { + return 2; + } else { + return 4; + } } // module functions @@ -45,7 +55,7 @@ static int texture_load(lua_State *L) { } else if (type==1) { //JPEG texture->texture = sfil_load_JPEG_file(path, place); } else if (type==2) { //BMP - texture->texture = sfil_load_BMP_file(path, place); + texture->texture = sfil_load_BMP_file(path, place); //appears to be broken right now. } else { lua_pushnil(L); lua_pushstring(L, "Bad type"); From 406e17d321f818e328a754f254aa6c215391c6db Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Fri, 2 Oct 2015 18:17:51 +0200 Subject: [PATCH 018/101] Fixed the missing "#include " --- source/texture.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/texture.c b/source/texture.c index 8d37187..a9da11f 100644 --- a/source/texture.c +++ b/source/texture.c @@ -10,6 +10,7 @@ The `gfx.texture` module. #include #include +#include #include "texture.h" From 4aad32d5abdc000f466b2140c402fe455037f84a Mon Sep 17 00:00:00 2001 From: Reuh Date: Sun, 4 Oct 2015 12:08:25 +0200 Subject: [PATCH 019/101] Moved lzlib to the libs directory (cleaner) --- Makefile | 2 +- libs/lzlib/.travis.yml | 51 ++ libs/lzlib/CHANGES | 42 + libs/lzlib/CMakeLists.txt | 19 + libs/lzlib/Makefile | 62 ++ libs/lzlib/README | 112 +++ libs/lzlib/README.lgzip | 108 +++ libs/lzlib/cmake/FindLua.cmake | 118 +++ libs/lzlib/cmake/dist.cmake | 321 ++++++++ libs/lzlib/cmake/lua.cmake | 293 +++++++ libs/lzlib/dist.info | 15 + libs/lzlib/gzip.lua | 83 ++ libs/lzlib/lakefile | 58 ++ libs/lzlib/lzlib.c | 986 ++++++++++++++++++++++ libs/lzlib/rockspec/INSTALL | 23 + libs/lzlib/rockspec/lzlib-git-1.rockspec | 37 + libs/lzlib/test_gzip.lua | 101 +++ libs/lzlib/test_prologue.lua | 2 + libs/lzlib/test_zlib2.lua | 93 +++ libs/lzlib/test_zlib3.lua | 29 + libs/lzlib/test_zlib_dict.lua | 34 + libs/lzlib/zlib.def | 2 + source/lzlib.c | 993 +---------------------- 23 files changed, 2593 insertions(+), 991 deletions(-) create mode 100644 libs/lzlib/.travis.yml create mode 100644 libs/lzlib/CHANGES create mode 100644 libs/lzlib/CMakeLists.txt create mode 100644 libs/lzlib/Makefile create mode 100644 libs/lzlib/README create mode 100644 libs/lzlib/README.lgzip create mode 100644 libs/lzlib/cmake/FindLua.cmake create mode 100644 libs/lzlib/cmake/dist.cmake create mode 100644 libs/lzlib/cmake/lua.cmake create mode 100644 libs/lzlib/dist.info create mode 100644 libs/lzlib/gzip.lua create mode 100644 libs/lzlib/lakefile create mode 100644 libs/lzlib/lzlib.c create mode 100644 libs/lzlib/rockspec/INSTALL create mode 100644 libs/lzlib/rockspec/lzlib-git-1.rockspec create mode 100644 libs/lzlib/test_gzip.lua create mode 100644 libs/lzlib/test_prologue.lua create mode 100644 libs/lzlib/test_zlib2.lua create mode 100644 libs/lzlib/test_zlib3.lua create mode 100644 libs/lzlib/test_zlib_dict.lua create mode 100644 libs/lzlib/zlib.def diff --git a/Makefile b/Makefile index 5451cc2..606550f 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ TARGET := ctruLua BUILD := build SOURCES := source libs/lua-5.3.1/src DATA := data -INCLUDES := include libs/lua-5.3.1/src +INCLUDES := include libs/lua-5.3.1/src libs/lzlib APP_TITLE := ctruLua APP_DESCRIPTION := Lua for the 3DS. Yes, it works. diff --git a/libs/lzlib/.travis.yml b/libs/lzlib/.travis.yml new file mode 100644 index 0000000..57493eb --- /dev/null +++ b/libs/lzlib/.travis.yml @@ -0,0 +1,51 @@ +# +# LuaDist Travis-CI Hook +# + +# We assume C build environments +language: C + +# Try using multiple Lua Implementations +env: + - TOOL="gcc" # Use native compiler (GCC usually) + - TOOL="clang" # Use clang + - TOOL="i686-w64-mingw32" # 32bit MinGW + - TOOL="x86_64-w64-mingw32" # 64bit MinGW + - TOOL="arm-linux-gnueabihf" # ARM hard-float (hf), linux + +# Crosscompile builds may fail +matrix: + allow_failures: + - env: TOOL="i686-w64-mingw32" + - env: TOOL="x86_64-w64-mingw32" + - env: TOOL="arm-linux-gnueabihf" + +# Install dependencies +install: + - git clone git://github.com/LuaDist/Tools.git ~/_tools + - ~/_tools/travis/travis install + +# Bootstap +before_script: + - ~/_tools/travis/travis bootstrap + +# Build the module +script: + - ~/_tools/travis/travis build + +# Execute additional tests or commands +after_script: + - ~/_tools/travis/travis test + +# Only watch the master branch +branches: + only: + - master + +# Notify the LuaDist Dev group if needed +notifications: + recipients: + - luadist-dev@googlegroups.com + email: + on_success: change + on_failure: always diff --git a/libs/lzlib/CHANGES b/libs/lzlib/CHANGES new file mode 100644 index 0000000..3281068 --- /dev/null +++ b/libs/lzlib/CHANGES @@ -0,0 +1,42 @@ +0.4-work3 2010-11-19 +==================== +Compatibility with lua 5.2. + + +0.4-work2 2008-11-07 +==================== +Fix: In the stream interface, the write callback was always being called with +two parameters even if the callback only needed one. +Thanks to Ignacio Burgueo for reporting the problem and providing a patch. + + +0.4-work1 2008-04-08 +==================== +Unstable version of streaming interface. Many things need to me tested +properly, and code needs to be polished. + +This adds an interface to zlib deflate/inflate streams similar to the io +file objects. + +Check README file for more information. + +gzip module is now a pure lua module. + +Warning: minimal tests provided... need to change that. + + +0.3 2008-01-28 +============== +This release makes the zlib/gzip bindings compatible with the latest +version of Lua (5.1) and now uses the package system to load the library. +It also fixes some incompatibilities that showed up with the changes +made to the Lua auxiliary library. + +0.2 2004-07-22 +============== + + +0.1 2003-12-09 +============== + +First released version. diff --git a/libs/lzlib/CMakeLists.txt b/libs/lzlib/CMakeLists.txt new file mode 100644 index 0000000..64272db --- /dev/null +++ b/libs/lzlib/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2007-2013 LuaDist. +# Created by Peter Drahoš +# Redistribution and use of this file is allowed according to the terms of the MIT license. +# For details see the COPYRIGHT file distributed with LuaDist. +# Please note that the package source code is licensed under its own license. + +project ( lzlib C ) +cmake_minimum_required ( VERSION 2.8 ) +include ( cmake/dist.cmake ) +include ( lua ) + +find_library ( Z_LIBRARY NAMES zlib zlibd z ) +# Find zlib + +install_lua_module ( zlib lzlib.c zlib.def LINK ${Z_LIBRARY} ) +install_lua_module ( gzip gzip.lua ) + +install_data ( README README.lgzip ) +install_test ( test_gzip.lua test_zlib_dict.lua test_zlib2.lua test_zlib3.lua ) diff --git a/libs/lzlib/Makefile b/libs/lzlib/Makefile new file mode 100644 index 0000000..87ed87d --- /dev/null +++ b/libs/lzlib/Makefile @@ -0,0 +1,62 @@ +# $Id: Makefile,v 1.8 2004/07/22 19:10:47 tngd Exp $ +# makefile for zlib library for Lua + +# dist location +DISTDIR=dist +TMP=/tmp + +# change these to reflect your Lua installation +LUA= $(HOME)/local/lua-5.2 +LUAINC= $(LUA)/include +LUALIB= $(LUA)/lib +LUABIN= $(LUA)/bin + +ZLIB=../zlib-1.2.3 + +# no need to change anything below here +CFLAGS= $(INCS) $(DEFS) $(WARN) -O0 -fPIC +WARN= -g -Werror -Wall -pedantic #-ansi +INCS= -I$(LUAINC) -I$(ZLIB) +LIBS= -L$(ZLIB) -lz -L$(LUALIB) -L$(LUABIN) #-llua51 + +MYLIB=lzlib + +ZLIB_NAME = zlib +GZIP_NAME = gzip + +T_ZLIB= $(ZLIB_NAME).so +T_GZIP= $(GZIP_NAME).so + +VER=0.4-work3 +TARFILE = $(DISTDIR)/$(MYLIB)-$(VER).tar.gz +TARFILES = Makefile README README.lgzip CHANGES \ + lzlib.c gzip.lua \ + test_zlib2.lua \ + test_zlib3.lua \ + test_gzip.lua \ + test_prologue.lua + +all: $(T_ZLIB) # $(T_GZIP) + +test: $(T_ZLIB) # $(T_GZIP) + $(LUABIN)/lua -lluarc test_prologue.lua + $(LUABIN)/lua -lluarc test_gzip.lua + $(LUABIN)/lua -lluarc test_zlib2.lua + $(LUABIN)/lua -lluarc test_zlib3.lua + +$(T_ZLIB): lzlib.o + $(CC) -o $@ -shared $< $(LIBS) + +$(T_GZIP): lgzip.o + $(CC) -o $@ -shared $< $(LIBS) + +clean: + rm -f *.o *.so core core.* a.out + +dist: $(TARFILE) + +$(TARFILE): $(TARFILES) + @ln -sf `pwd` $(TMP)/$(MYLIB)-$(VER) + tar -zcvf $(TARFILE) -C $(TMP) $(addprefix $(MYLIB)-$(VER)/,$(TARFILES)) + @rm -f $(TMP)/$(MYLIB)-$(VER) + @# @lsum $(TARFILE) $(DISTDIR)/md5sums.txt diff --git a/libs/lzlib/README b/libs/lzlib/README new file mode 100644 index 0000000..69a537a --- /dev/null +++ b/libs/lzlib/README @@ -0,0 +1,112 @@ +************************************************************************* +* Author : Tiago Dionizio * +* Library : lzlib - Lua 5.1 interface to access zlib library functions* +* * +* 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. * +************************************************************************* + +To use this library you need zlib library. +You can get it from http://www.gzip.org/zlib/ + + +Loading the library: + + If you built the library as a loadable package + [local] zlib = require 'zlib' + + If you compiled the package statically into your application, call + the function "luaopen_zlib(L)". It will create a table with the zlib + functions and leave it on the stack. + +-- zlib functions -- + +zlib.version() + returns zlib version + +zlib.adler32([int adler32, string buffer]) + Without any parameters, returns the inicial adler32 value. + + Call to update the adler32 value, adler is the current value, buffer is passed + to adler32 zlib function and the updated value is returned. + +zlib.crc32([int crc32, string buffer]) + Same as zlib.adler32. + +zlib.compress(string buffer [, int level] [, int method] [, int windowBits] [, int memLevel] [, int strategy]) + Return a string containing the compressed buffer according to the given parameters. + +zlib.decompress(string buffer [, int windowBits]) + Return the decompressed stream after processing the given buffer. + + +zlib.deflate( + sink: function | { write: function [, close: function, flush: function ] }, + compression level, [Z_DEFAILT_COMPRESSION] + method, [Z_DEFLATED] + windowBits, [15] + memLevel, [8] + strategy, [Z_DEFAULT_STRATEGY] + dictionary, [""] +) + Return a deflate stream. + + stream:read((number | '*l' | '*a')*) + Return a value for each given parameter. Returns a line when + no format is specified. + + stream:lines() + Return iterator that returns a line each time it is called, or nil + on end of file. + + stream:close() + Close the stream. + +zlib.inflate( + source: string | function | { read: function, close: function }, + windowBits: number, [15] + dictionary, [""] +) + Return an inflate stream. + + +deflate and inflate streams can be used almost like a normal file: + + stream:write(...) + Write each parameter into the sream. + + stream:read([option [, ...]]) + Read from the stream, each parameter corresponds to + a return value. + + With no arguments, it reads a line. + Parameters are interpreted as follows: + number - reads the specified number of bytes + 'a' - reads the remaining bytes + 'l' - reads a line + + stream:lines(...) + Returns an iterator that returns a new line each time + it is called. + + stream:flush(['sync' | 'full' | 'finish']) + Flush output for deflate streams. + + stream:close() + Close the stream. diff --git a/libs/lzlib/README.lgzip b/libs/lzlib/README.lgzip new file mode 100644 index 0000000..ff68b3c --- /dev/null +++ b/libs/lzlib/README.lgzip @@ -0,0 +1,108 @@ +************************************************************************* +* Author : Tiago Dionizio * +* Library : lgzip - a gzip file access binding for Lua 5 * +* based on liolib.c from Lua 5.0 library * +* * +* 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. * +************************************************************************* + +To use this library you need zlib library. +You can get it from http://www.gzip.org/zlib/ + + +GZIP file handling on top of the zlib interface. + +TODO: + - detect plain text files + - proper testing + - improve implementation? + - check gzopen flags for consistency (ex: strategy flag) + +Loading the library: + + [local] gzip = require 'gzip' + +gzip.open(filename [, mode]) + + Opens a file name using "gzopen". Behaviour is identical to the description + given in the zlib library. If mode is not given a default mode "r" will be + used. Mode is the same as interpreted by gzopen function, ie, it can + include special modes such as characters 1 to 9 that will be treated as the + compression level when opening a file for writing. + + It returns a new file handle, or, in case of errors, nil plus an error + message + +gzip.lines(filename) + + Same behaviour as io.lines in the io standard library provided by lua + with the aditional feature of working with gzip files. If a normal text + file is read it will read it normaly. + +gzip.close(file) + + Same as file:close, use file:close instead. + +file:flush() + + This function takes no parameters and flushes all output to working file. + The same as calling 'gzflush(file, Z_FINISH)' so writing to the file will + most likely not work as expected. This is subject to change in the future + if there is a strong reason for it to happen. + +file:read(format1, ...) + Reads the file file, according to the given formats, which specify what + to read. For each format, the function returns a string with the characters + read, or nil if it cannot read data with the specified format. When called + without formats, it uses a default format that reads the entire next line + (see below). + + The available formats are + + "*a" reads the whole file, starting at the current position. On end of + file, it returns the empty string. + "*l" reads the next line (skipping the end of line), returning nil on + end of file. This is the default format. + number reads a string with up to that number of characters, returning + nil on end of file. If number is zero, it reads nothing and + returns an empty string, or nil on end of file. + + Unlike io.read, the "*n" format will not be available. + + +file:lines() + + Returns an iterator function that, each time it is called, returns a new + line from the file. Therefore, the construction + + for line in file:lines() do ... end + + will iterate over all lines of the file. + +file:write(value1, ...) + + Writes the value of each of its arguments to the filehandle file. The + arguments must be strings or numbers. To write other values, use tostring + or string.format before write + +file:close() + + Closes the file. + diff --git a/libs/lzlib/cmake/FindLua.cmake b/libs/lzlib/cmake/FindLua.cmake new file mode 100644 index 0000000..7fb7ca3 --- /dev/null +++ b/libs/lzlib/cmake/FindLua.cmake @@ -0,0 +1,118 @@ +# Locate Lua library +# This module defines +# LUA_EXECUTABLE, if found +# LUA_FOUND, if false, do not try to link to Lua +# LUA_LIBRARIES +# LUA_INCLUDE_DIR, where to find lua.h +# LUA_VERSION_STRING, the version of Lua found (since CMake 2.8.8) +# +# Note that the expected include convention is +# #include "lua.h" +# and not +# #include +# This is because, the lua location is not standardized and may exist +# in locations other than lua/ + +#============================================================================= +# Copyright 2007-2009 Kitware, Inc. +# Modified to support Lua 5.2 by LuaDist 2012 +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) +# +# The required version of Lua can be specified using the +# standard syntax, e.g. FIND_PACKAGE(Lua 5.1) +# Otherwise the module will search for any available Lua implementation + +# Always search for non-versioned lua first (recommended) +SET(_POSSIBLE_LUA_INCLUDE include include/lua) +SET(_POSSIBLE_LUA_EXECUTABLE lua) +SET(_POSSIBLE_LUA_LIBRARY lua) + +# Determine possible naming suffixes (there is no standard for this) +IF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) + SET(_POSSIBLE_SUFFIXES "${Lua_FIND_VERSION_MAJOR}${Lua_FIND_VERSION_MINOR}" "${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}" "-${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}") +ELSE(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) + SET(_POSSIBLE_SUFFIXES "52" "5.2" "-5.2" "51" "5.1" "-5.1") +ENDIF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) + +# Set up possible search names and locations +FOREACH(_SUFFIX ${_POSSIBLE_SUFFIXES}) + LIST(APPEND _POSSIBLE_LUA_INCLUDE "include/lua${_SUFFIX}") + LIST(APPEND _POSSIBLE_LUA_EXECUTABLE "lua${_SUFFIX}") + LIST(APPEND _POSSIBLE_LUA_LIBRARY "lua${_SUFFIX}") +ENDFOREACH(_SUFFIX) + +# Find the lua executable +FIND_PROGRAM(LUA_EXECUTABLE + NAMES ${_POSSIBLE_LUA_EXECUTABLE} +) + +# Find the lua header +FIND_PATH(LUA_INCLUDE_DIR lua.h + HINTS + $ENV{LUA_DIR} + PATH_SUFFIXES ${_POSSIBLE_LUA_INCLUDE} + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt +) + +# Find the lua library +FIND_LIBRARY(LUA_LIBRARY + NAMES ${_POSSIBLE_LUA_LIBRARY} + HINTS + $ENV{LUA_DIR} + PATH_SUFFIXES lib64 lib + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /sw + /opt/local + /opt/csw + /opt +) + +IF(LUA_LIBRARY) + # include the math library for Unix + IF(UNIX AND NOT APPLE) + FIND_LIBRARY(LUA_MATH_LIBRARY m) + SET( LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}" CACHE STRING "Lua Libraries") + # For Windows and Mac, don't need to explicitly include the math library + ELSE(UNIX AND NOT APPLE) + SET( LUA_LIBRARIES "${LUA_LIBRARY}" CACHE STRING "Lua Libraries") + ENDIF(UNIX AND NOT APPLE) +ENDIF(LUA_LIBRARY) + +# Determine Lua version +IF(LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h") + FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_str REGEX "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua .+\"") + + STRING(REGEX REPLACE "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([^\"]+)\".*" "\\1" LUA_VERSION_STRING "${lua_version_str}") + UNSET(lua_version_str) +ENDIF() + +INCLUDE(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if +# all listed variables are TRUE +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua + REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR + VERSION_VAR LUA_VERSION_STRING) + +MARK_AS_ADVANCED(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY LUA_EXECUTABLE) + diff --git a/libs/lzlib/cmake/dist.cmake b/libs/lzlib/cmake/dist.cmake new file mode 100644 index 0000000..310ef94 --- /dev/null +++ b/libs/lzlib/cmake/dist.cmake @@ -0,0 +1,321 @@ +# LuaDist CMake utility library. +# Provides sane project defaults and macros common to LuaDist CMake builds. +# +# Copyright (C) 2007-2012 LuaDist. +# by David Manura, Peter Drahoš +# Redistribution and use of this file is allowed according to the terms of the MIT license. +# For details see the COPYRIGHT file distributed with LuaDist. +# Please note that the package source code is licensed under its own license. + +## Extract information from dist.info +if ( NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/dist.info ) + message ( FATAL_ERROR + "Missing dist.info file (${CMAKE_CURRENT_SOURCE_DIR}/dist.info)." ) +endif () +file ( READ ${CMAKE_CURRENT_SOURCE_DIR}/dist.info DIST_INFO ) +if ( "${DIST_INFO}" STREQUAL "" ) + message ( FATAL_ERROR "Failed to load dist.info." ) +endif () +# Reads field `name` from dist.info string `DIST_INFO` into variable `var`. +macro ( _parse_dist_field name var ) + string ( REGEX REPLACE ".*${name}[ \t]?=[ \t]?[\"']([^\"']+)[\"'].*" "\\1" + ${var} "${DIST_INFO}" ) + if ( ${var} STREQUAL DIST_INFO ) + message ( FATAL_ERROR "Failed to extract \"${var}\" from dist.info" ) + endif () +endmacro () +# +_parse_dist_field ( name DIST_NAME ) +_parse_dist_field ( version DIST_VERSION ) +_parse_dist_field ( license DIST_LICENSE ) +_parse_dist_field ( author DIST_AUTHOR ) +_parse_dist_field ( maintainer DIST_MAINTAINER ) +_parse_dist_field ( url DIST_URL ) +_parse_dist_field ( desc DIST_DESC ) +message ( "DIST_NAME: ${DIST_NAME}") +message ( "DIST_VERSION: ${DIST_VERSION}") +message ( "DIST_LICENSE: ${DIST_LICENSE}") +message ( "DIST_AUTHOR: ${DIST_AUTHOR}") +message ( "DIST_MAINTAINER: ${DIST_MAINTAINER}") +message ( "DIST_URL: ${DIST_URL}") +message ( "DIST_DESC: ${DIST_DESC}") +string ( REGEX REPLACE ".*depends[ \t]?=[ \t]?[\"']([^\"']+)[\"'].*" "\\1" + DIST_DEPENDS ${DIST_INFO} ) +if ( DIST_DEPENDS STREQUAL DIST_INFO ) + set ( DIST_DEPENDS "" ) +endif () +message ( "DIST_DEPENDS: ${DIST_DEPENDS}") +## 2DO: Parse DIST_DEPENDS and try to install Dependencies with automatically using externalproject_add + + +## INSTALL DEFAULTS (Relative to CMAKE_INSTALL_PREFIX) +# Primary paths +set ( INSTALL_BIN bin CACHE PATH "Where to install binaries to." ) +set ( INSTALL_LIB lib CACHE PATH "Where to install libraries to." ) +set ( INSTALL_INC include CACHE PATH "Where to install headers to." ) +set ( INSTALL_ETC etc CACHE PATH "Where to store configuration files" ) +set ( INSTALL_SHARE share CACHE PATH "Directory for shared data." ) + +# Secondary paths +option ( INSTALL_VERSION + "Install runtime libraries and executables with version information." OFF) +set ( INSTALL_DATA ${INSTALL_SHARE}/${DIST_NAME} CACHE PATH + "Directory the package can store documentation, tests or other data in.") +set ( INSTALL_DOC ${INSTALL_DATA}/doc CACHE PATH + "Recommended directory to install documentation into.") +set ( INSTALL_EXAMPLE ${INSTALL_DATA}/example CACHE PATH + "Recommended directory to install examples into.") +set ( INSTALL_TEST ${INSTALL_DATA}/test CACHE PATH + "Recommended directory to install tests into.") +set ( INSTALL_FOO ${INSTALL_DATA}/etc CACHE PATH + "Where to install additional files") + +# Tweaks and other defaults +# Setting CMAKE to use loose block and search for find modules in source directory +set ( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true ) +set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH} ) +option ( BUILD_SHARED_LIBS "Build shared libraries" ON ) + +# In MSVC, prevent warnings that can occur when using standard libraries. +if ( MSVC ) + add_definitions ( -D_CRT_SECURE_NO_WARNINGS ) +endif () + +# RPath and relative linking +option ( USE_RPATH "Use relative linking." ON) +if ( USE_RPATH ) + string ( REGEX REPLACE "[^!/]+" ".." UP_DIR ${INSTALL_BIN} ) + set ( CMAKE_SKIP_BUILD_RPATH FALSE CACHE STRING "" FORCE ) + set ( CMAKE_BUILD_WITH_INSTALL_RPATH FALSE CACHE STRING "" FORCE ) + set ( CMAKE_INSTALL_RPATH $ORIGIN/${UP_DIR}/${INSTALL_LIB} + CACHE STRING "" FORCE ) + set ( CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE CACHE STRING "" FORCE ) + set ( CMAKE_INSTALL_NAME_DIR @executable_path/${UP_DIR}/${INSTALL_LIB} + CACHE STRING "" FORCE ) +endif () + +## MACROS +# Parser macro +macro ( parse_arguments prefix arg_names option_names) + set ( DEFAULT_ARGS ) + foreach ( arg_name ${arg_names} ) + set ( ${prefix}_${arg_name} ) + endforeach () + foreach ( option ${option_names} ) + set ( ${prefix}_${option} FALSE ) + endforeach () + + set ( current_arg_name DEFAULT_ARGS ) + set ( current_arg_list ) + foreach ( arg ${ARGN} ) + set ( larg_names ${arg_names} ) + list ( FIND larg_names "${arg}" is_arg_name ) + if ( is_arg_name GREATER -1 ) + set ( ${prefix}_${current_arg_name} ${current_arg_list} ) + set ( current_arg_name ${arg} ) + set ( current_arg_list ) + else () + set ( loption_names ${option_names} ) + list ( FIND loption_names "${arg}" is_option ) + if ( is_option GREATER -1 ) + set ( ${prefix}_${arg} TRUE ) + else () + set ( current_arg_list ${current_arg_list} ${arg} ) + endif () + endif () + endforeach () + set ( ${prefix}_${current_arg_name} ${current_arg_list} ) +endmacro () + + +# install_executable ( executable_targets ) +# Installs any executables generated using "add_executable". +# USE: install_executable ( lua ) +# NOTE: subdirectories are NOT supported +set ( CPACK_COMPONENT_RUNTIME_DISPLAY_NAME "${DIST_NAME} Runtime" ) +set ( CPACK_COMPONENT_RUNTIME_DESCRIPTION + "Executables and runtime libraries. Installed into ${INSTALL_BIN}." ) +macro ( install_executable ) + foreach ( _file ${ARGN} ) + if ( INSTALL_VERSION ) + set_target_properties ( ${_file} PROPERTIES VERSION ${DIST_VERSION} + SOVERSION ${DIST_VERSION} ) + endif () + install ( TARGETS ${_file} RUNTIME DESTINATION ${INSTALL_BIN} + COMPONENT Runtime ) + endforeach() +endmacro () + +# install_library ( library_targets ) +# Installs any libraries generated using "add_library" into apropriate places. +# USE: install_library ( libexpat ) +# NOTE: subdirectories are NOT supported +set ( CPACK_COMPONENT_LIBRARY_DISPLAY_NAME "${DIST_NAME} Development Libraries" ) +set ( CPACK_COMPONENT_LIBRARY_DESCRIPTION + "Static and import libraries needed for development. Installed into ${INSTALL_LIB} or ${INSTALL_BIN}." ) +macro ( install_library ) + foreach ( _file ${ARGN} ) + if ( INSTALL_VERSION ) + set_target_properties ( ${_file} PROPERTIES VERSION ${DIST_VERSION} + SOVERSION ${DIST_VERSION} ) + endif () + install ( TARGETS ${_file} + RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT Runtime + LIBRARY DESTINATION ${INSTALL_LIB} COMPONENT Runtime + ARCHIVE DESTINATION ${INSTALL_LIB} COMPONENT Library ) + endforeach() +endmacro () + +# helper function for various install_* functions, for PATTERN/REGEX args. +macro ( _complete_install_args ) + if ( NOT("${_ARG_PATTERN}" STREQUAL "") ) + set ( _ARG_PATTERN PATTERN ${_ARG_PATTERN} ) + endif () + if ( NOT("${_ARG_REGEX}" STREQUAL "") ) + set ( _ARG_REGEX REGEX ${_ARG_REGEX} ) + endif () +endmacro () + +# install_header ( files/directories [INTO destination] ) +# Install a directories or files into header destination. +# USE: install_header ( lua.h luaconf.h ) or install_header ( GL ) +# USE: install_header ( mylib.h INTO mylib ) +# For directories, supports optional PATTERN/REGEX arguments like install(). +set ( CPACK_COMPONENT_HEADER_DISPLAY_NAME "${DIST_NAME} Development Headers" ) +set ( CPACK_COMPONENT_HEADER_DESCRIPTION + "Headers needed for development. Installed into ${INSTALL_INC}." ) +macro ( install_header ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_INC}/${_ARG_INTO} + COMPONENT Header ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_INC}/${_ARG_INTO} + COMPONENT Header ) + endif () + endforeach() +endmacro () + +# install_data ( files/directories [INTO destination] ) +# This installs additional data files or directories. +# USE: install_data ( extra data.dat ) +# USE: install_data ( image1.png image2.png INTO images ) +# For directories, supports optional PATTERN/REGEX arguments like install(). +set ( CPACK_COMPONENT_DATA_DISPLAY_NAME "${DIST_NAME} Data" ) +set ( CPACK_COMPONENT_DATA_DESCRIPTION + "Application data. Installed into ${INSTALL_DATA}." ) +macro ( install_data ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} + DESTINATION ${INSTALL_DATA}/${_ARG_INTO} + COMPONENT Data ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_DATA}/${_ARG_INTO} + COMPONENT Data ) + endif () + endforeach() +endmacro () + +# INSTALL_DOC ( files/directories [INTO destination] ) +# This installs documentation content +# USE: install_doc ( doc/ doc.pdf ) +# USE: install_doc ( index.html INTO html ) +# For directories, supports optional PATTERN/REGEX arguments like install(). +set ( CPACK_COMPONENT_DOCUMENTATION_DISPLAY_NAME "${DIST_NAME} Documentation" ) +set ( CPACK_COMPONENT_DOCUMENTATION_DESCRIPTION + "Application documentation. Installed into ${INSTALL_DOC}." ) +macro ( install_doc ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_DOC}/${_ARG_INTO} + COMPONENT Documentation ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_DOC}/${_ARG_INTO} + COMPONENT Documentation ) + endif () + endforeach() +endmacro () + +# install_example ( files/directories [INTO destination] ) +# This installs additional examples +# USE: install_example ( examples/ exampleA ) +# USE: install_example ( super_example super_data INTO super) +# For directories, supports optional PATTERN/REGEX argument like install(). +set ( CPACK_COMPONENT_EXAMPLE_DISPLAY_NAME "${DIST_NAME} Examples" ) +set ( CPACK_COMPONENT_EXAMPLE_DESCRIPTION + "Examples and their associated data. Installed into ${INSTALL_EXAMPLE}." ) +macro ( install_example ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_EXAMPLE}/${_ARG_INTO} + COMPONENT Example ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_EXAMPLE}/${_ARG_INTO} + COMPONENT Example ) + endif () + endforeach() +endmacro () + +# install_test ( files/directories [INTO destination] ) +# This installs tests and test files, DOES NOT EXECUTE TESTS +# USE: install_test ( my_test data.sql ) +# USE: install_test ( feature_x_test INTO x ) +# For directories, supports optional PATTERN/REGEX argument like install(). +set ( CPACK_COMPONENT_TEST_DISPLAY_NAME "${DIST_NAME} Tests" ) +set ( CPACK_COMPONENT_TEST_DESCRIPTION + "Tests and associated data. Installed into ${INSTALL_TEST}." ) +macro ( install_test ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_TEST}/${_ARG_INTO} + COMPONENT Test ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_TEST}/${_ARG_INTO} + COMPONENT Test ) + endif () + endforeach() +endmacro () + +# install_foo ( files/directories [INTO destination] ) +# This installs optional or otherwise unneeded content +# USE: install_foo ( etc/ example.doc ) +# USE: install_foo ( icon.png logo.png INTO icons) +# For directories, supports optional PATTERN/REGEX argument like install(). +set ( CPACK_COMPONENT_OTHER_DISPLAY_NAME "${DIST_NAME} Unspecified Content" ) +set ( CPACK_COMPONENT_OTHER_DESCRIPTION + "Other unspecified content. Installed into ${INSTALL_FOO}." ) +macro ( install_foo ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_FOO}/${_ARG_INTO} + COMPONENT Other ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_FOO}/${_ARG_INTO} + COMPONENT Other ) + endif () + endforeach() +endmacro () + +## CTest defaults + +## CPack defaults +set ( CPACK_GENERATOR "ZIP" ) +set ( CPACK_STRIP_FILES TRUE ) +set ( CPACK_PACKAGE_NAME "${DIST_NAME}" ) +set ( CPACK_PACKAGE_VERSION "${DIST_VERSION}") +set ( CPACK_PACKAGE_VENDOR "LuaDist" ) +set ( CPACK_COMPONENTS_ALL Runtime Library Header Data Documentation Example Other ) +include ( CPack ) diff --git a/libs/lzlib/cmake/lua.cmake b/libs/lzlib/cmake/lua.cmake new file mode 100644 index 0000000..80bbc5f --- /dev/null +++ b/libs/lzlib/cmake/lua.cmake @@ -0,0 +1,293 @@ +# LuaDist CMake utility library for Lua. +# +# Copyright (C) 2007-2012 LuaDist. +# by David Manura, Peter Drahos +# Redistribution and use of this file is allowed according to the terms of the MIT license. +# For details see the COPYRIGHT file distributed with LuaDist. +# Please note that the package source code is licensed under its own license. + +set ( INSTALL_LMOD ${INSTALL_LIB}/lua + CACHE PATH "Directory to install Lua modules." ) +set ( INSTALL_CMOD ${INSTALL_LIB}/lua + CACHE PATH "Directory to install Lua binary modules." ) + +option ( SKIP_LUA_WRAPPER + "Do not build and install Lua executable wrappers." OFF) + +# List of (Lua module name, file path) pairs. +# Used internally by add_lua_test. Built by add_lua_module. +set ( _lua_modules ) + +# utility function: appends path `path` to path `basepath`, properly +# handling cases when `path` may be relative or absolute. +macro ( _append_path basepath path result ) + if ( IS_ABSOLUTE "${path}" ) + set ( ${result} "${path}" ) + else () + set ( ${result} "${basepath}/${path}" ) + endif () +endmacro () + +# install_lua_executable ( target source ) +# Automatically generate a binary if srlua package is available +# The application or its source will be placed into /bin +# If the application source did not have .lua suffix then it will be added +# USE: lua_executable ( sputnik src/sputnik.lua ) +macro ( install_lua_executable _name _source ) + get_filename_component ( _source_name ${_source} NAME_WE ) + # Find srlua and glue + find_program( SRLUA_EXECUTABLE NAMES srlua ) + find_program( GLUE_EXECUTABLE NAMES glue ) + # Executable output + set ( _exe ${CMAKE_CURRENT_BINARY_DIR}/${_name}${CMAKE_EXECUTABLE_SUFFIX} ) + if ( NOT SKIP_LUA_WRAPPER AND SRLUA_EXECUTABLE AND GLUE_EXECUTABLE ) + # Generate binary gluing the lua code to srlua, this is a robuust approach for most systems + add_custom_command( + OUTPUT ${_exe} + COMMAND ${GLUE_EXECUTABLE} + ARGS ${SRLUA_EXECUTABLE} ${_source} ${_exe} + DEPENDS ${_source} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + VERBATIM + ) + # Make sure we have a target associated with the binary + add_custom_target(${_name} ALL + DEPENDS ${_exe} + ) + # Install with run permissions + install ( PROGRAMS ${_exe} DESTINATION ${INSTALL_BIN} COMPONENT Runtime) + # Also install source as optional resurce + install ( FILES ${_source} DESTINATION ${INSTALL_FOO} COMPONENT Other ) + else() + # Install into bin as is but without the lua suffix, we assume the executable uses UNIX shebang/hash-bang magic + install ( PROGRAMS ${_source} DESTINATION ${INSTALL_BIN} + RENAME ${_source_name} + COMPONENT Runtime + ) + endif() +endmacro () + +macro ( _lua_module_helper is_install _name ) + parse_arguments ( _MODULE "LINK;ALL_IN_ONE" "" ${ARGN} ) + # _target is CMake-compatible target name for module (e.g. socket_core). + # _module is relative path of target (e.g. socket/core), + # without extension (e.g. .lua/.so/.dll). + # _MODULE_SRC is list of module source files (e.g. .lua and .c files). + # _MODULE_NAMES is list of module names (e.g. socket.core). + if ( _MODULE_ALL_IN_ONE ) + string ( REGEX REPLACE "\\..*" "" _target "${_name}" ) + string ( REGEX REPLACE "\\..*" "" _module "${_name}" ) + set ( _target "${_target}_all_in_one") + set ( _MODULE_SRC ${_MODULE_ALL_IN_ONE} ) + set ( _MODULE_NAMES ${_name} ${_MODULE_DEFAULT_ARGS} ) + else () + string ( REPLACE "." "_" _target "${_name}" ) + string ( REPLACE "." "/" _module "${_name}" ) + set ( _MODULE_SRC ${_MODULE_DEFAULT_ARGS} ) + set ( _MODULE_NAMES ${_name} ) + endif () + if ( NOT _MODULE_SRC ) + message ( FATAL_ERROR "no module sources specified" ) + endif () + list ( GET _MODULE_SRC 0 _first_source ) + + get_filename_component ( _ext ${_first_source} EXT ) + if ( _ext STREQUAL ".lua" ) # Lua source module + list ( LENGTH _MODULE_SRC _len ) + if ( _len GREATER 1 ) + message ( FATAL_ERROR "more than one source file specified" ) + endif () + + set ( _module "${_module}.lua" ) + + get_filename_component ( _module_dir ${_module} PATH ) + get_filename_component ( _module_filename ${_module} NAME ) + _append_path ( "${CMAKE_CURRENT_SOURCE_DIR}" "${_first_source}" _module_path ) + list ( APPEND _lua_modules "${_name}" "${_module_path}" ) + + if ( ${is_install} ) + install ( FILES ${_first_source} DESTINATION ${INSTALL_LMOD}/${_module_dir} + RENAME ${_module_filename} + COMPONENT Runtime + ) + endif () + else () # Lua C binary module + enable_language ( C ) + find_package ( Lua REQUIRED ) + include_directories ( ${LUA_INCLUDE_DIR} ) + + set ( _module "${_module}${CMAKE_SHARED_MODULE_SUFFIX}" ) + + get_filename_component ( _module_dir ${_module} PATH ) + get_filename_component ( _module_filenamebase ${_module} NAME_WE ) + foreach ( _thisname ${_MODULE_NAMES} ) + list ( APPEND _lua_modules "${_thisname}" + "${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_CFG_INTDIR}/${_module}" ) + endforeach () + + add_library( ${_target} MODULE ${_MODULE_SRC}) + target_link_libraries ( ${_target} ${LUA_LIBRARY} ${_MODULE_LINK} ) + set_target_properties ( ${_target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY + "${_module_dir}" PREFIX "" OUTPUT_NAME "${_module_filenamebase}" ) + if ( ${is_install} ) + install ( TARGETS ${_target} DESTINATION ${INSTALL_CMOD}/${_module_dir} COMPONENT Runtime) + endif () + endif () +endmacro () + +# add_lua_module +# Builds a Lua source module into a destination locatable by Lua +# require syntax. +# Binary modules are also supported where this function takes sources and +# libraries to compile separated by LINK keyword. +# USE: add_lua_module ( socket.http src/http.lua ) +# USE2: add_lua_module ( mime.core src/mime.c ) +# USE3: add_lua_module ( socket.core ${SRC_SOCKET} LINK ${LIB_SOCKET} ) +# USE4: add_lua_module ( ssl.context ssl.core ALL_IN_ONE src/context.c src/ssl.c ) +# This form builds an "all-in-one" module (e.g. ssl.so or ssl.dll containing +# both modules ssl.context and ssl.core). The CMake target name will be +# ssl_all_in_one. +# Also sets variable _module_path (relative path where module typically +# would be installed). +macro ( add_lua_module ) + _lua_module_helper ( 0 ${ARGN} ) +endmacro () + + +# install_lua_module +# This is the same as `add_lua_module` but also installs the module. +# USE: install_lua_module ( socket.http src/http.lua ) +# USE2: install_lua_module ( mime.core src/mime.c ) +# USE3: install_lua_module ( socket.core ${SRC_SOCKET} LINK ${LIB_SOCKET} ) +macro ( install_lua_module ) + _lua_module_helper ( 1 ${ARGN} ) +endmacro () + +# Builds string representing Lua table mapping Lua modules names to file +# paths. Used internally. +macro ( _make_module_table _outvar ) + set ( ${_outvar} ) + list ( LENGTH _lua_modules _n ) + if ( ${_n} GREATER 0 ) # avoids cmake complaint + foreach ( _i RANGE 1 ${_n} 2 ) + list ( GET _lua_modules ${_i} _path ) + math ( EXPR _ii ${_i}-1 ) + list ( GET _lua_modules ${_ii} _name ) + set ( ${_outvar} "${_table} ['${_name}'] = '${_path}'\;\n") + endforeach () + endif () + set ( ${_outvar} +"local modules = { +${_table}}" ) +endmacro () + +# add_lua_test ( _testfile [ WORKING_DIRECTORY _working_dir ] ) +# Runs Lua script `_testfile` under CTest tester. +# Optional named argument `WORKING_DIRECTORY` is current working directory to +# run test under (defaults to ${CMAKE_CURRENT_BINARY_DIR}). +# Both paths, if relative, are relative to ${CMAKE_CURRENT_SOURCE_DIR}. +# Any modules previously defined with install_lua_module are automatically +# preloaded (via package.preload) prior to running the test script. +# Under LuaDist, set test=true in config.lua to enable testing. +# USE: add_lua_test ( test/test1.lua [args...] [WORKING_DIRECTORY dir]) +macro ( add_lua_test _testfile ) + if ( NOT SKIP_TESTING ) + parse_arguments ( _ARG "WORKING_DIRECTORY" "" ${ARGN} ) + include ( CTest ) + find_program ( LUA NAMES lua lua.bat ) + get_filename_component ( TESTFILEABS ${_testfile} ABSOLUTE ) + get_filename_component ( TESTFILENAME ${_testfile} NAME ) + get_filename_component ( TESTFILEBASE ${_testfile} NAME_WE ) + + # Write wrapper script. + # Note: One simple way to allow the script to find modules is + # to just put them in package.preload. + set ( TESTWRAPPER ${CMAKE_CURRENT_BINARY_DIR}/${TESTFILENAME} ) + _make_module_table ( _table ) + set ( TESTWRAPPERSOURCE +"local CMAKE_CFG_INTDIR = ... or '.' +${_table} +local function preload_modules(modules) + for name, path in pairs(modules) do + if path:match'%.lua' then + package.preload[name] = assert(loadfile(path)) + else + local name = name:gsub('.*%-', '') -- remove any hyphen prefix + local symbol = 'luaopen_' .. name:gsub('%.', '_') + --improve: generalize to support all-in-one loader? + local path = path:gsub('%$%{CMAKE_CFG_INTDIR%}', CMAKE_CFG_INTDIR) + package.preload[name] = assert(package.loadlib(path, symbol)) + end + end +end +preload_modules(modules) +arg[0] = '${TESTFILEABS}' +table.remove(arg, 1) +return assert(loadfile '${TESTFILEABS}')(unpack(arg)) +" ) + if ( _ARG_WORKING_DIRECTORY ) + get_filename_component ( + TESTCURRENTDIRABS ${_ARG_WORKING_DIRECTORY} ABSOLUTE ) + # note: CMake 2.6 (unlike 2.8) lacks WORKING_DIRECTORY parameter. + set ( _pre ${CMAKE_COMMAND} -E chdir "${TESTCURRENTDIRABS}" ) + endif () + file ( WRITE ${TESTWRAPPER} ${TESTWRAPPERSOURCE}) + add_test ( NAME ${TESTFILEBASE} COMMAND ${_pre} ${LUA} + ${TESTWRAPPER} "${CMAKE_CFG_INTDIR}" + ${_ARG_DEFAULT_ARGS} ) + endif () + # see also http://gdcm.svn.sourceforge.net/viewvc/gdcm/Sandbox/CMakeModules/UsePythonTest.cmake + # Note: ${CMAKE_CFG_INTDIR} is a command-line argument to allow proper + # expansion by the native build tool. +endmacro () + + +# Converts Lua source file `_source` to binary string embedded in C source +# file `_target`. Optionally compiles Lua source to byte code (not available +# under LuaJIT2, which doesn't have a bytecode loader). Additionally, Lua +# versions of bin2c [1] and luac [2] may be passed respectively as additional +# arguments. +# +# [1] http://lua-users.org/wiki/BinToCee +# [2] http://lua-users.org/wiki/LuaCompilerInLua +function ( add_lua_bin2c _target _source ) + find_program ( LUA NAMES lua lua.bat ) + execute_process ( COMMAND ${LUA} -e "string.dump(function()end)" + RESULT_VARIABLE _LUA_DUMP_RESULT ERROR_QUIET ) + if ( NOT ${_LUA_DUMP_RESULT} ) + SET ( HAVE_LUA_DUMP true ) + endif () + message ( "-- string.dump=${HAVE_LUA_DUMP}" ) + + if ( ARGV2 ) + get_filename_component ( BIN2C ${ARGV2} ABSOLUTE ) + set ( BIN2C ${LUA} ${BIN2C} ) + else () + find_program ( BIN2C NAMES bin2c bin2c.bat ) + endif () + if ( HAVE_LUA_DUMP ) + if ( ARGV3 ) + get_filename_component ( LUAC ${ARGV3} ABSOLUTE ) + set ( LUAC ${LUA} ${LUAC} ) + else () + find_program ( LUAC NAMES luac luac.bat ) + endif () + endif ( HAVE_LUA_DUMP ) + message ( "-- bin2c=${BIN2C}" ) + message ( "-- luac=${LUAC}" ) + + get_filename_component ( SOURCEABS ${_source} ABSOLUTE ) + if ( HAVE_LUA_DUMP ) + get_filename_component ( SOURCEBASE ${_source} NAME_WE ) + add_custom_command ( + OUTPUT ${_target} DEPENDS ${_source} + COMMAND ${LUAC} -o ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo + ${SOURCEABS} + COMMAND ${BIN2C} ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo + ">${_target}" ) + else () + add_custom_command ( + OUTPUT ${_target} DEPENDS ${SOURCEABS} + COMMAND ${BIN2C} ${_source} ">${_target}" ) + endif () +endfunction() diff --git a/libs/lzlib/dist.info b/libs/lzlib/dist.info new file mode 100644 index 0000000..e54e7a1 --- /dev/null +++ b/libs/lzlib/dist.info @@ -0,0 +1,15 @@ +--- This file is part of LuaDist project + +name = "lzlib" +version = "0.4.2" + +desc = "Lua bindings to the ZLib compression library" +author = "Tiago Dionizio" +url = "http://luaforge.net/projects/lzlib/" +license = "MIT/X11" +maintainer = "Peter Drahoš" + +depends = { + "lua >= 5.1, < 5.4", + "zlib >=1.2.3" +} diff --git a/libs/lzlib/gzip.lua b/libs/lzlib/gzip.lua new file mode 100644 index 0000000..77ee173 --- /dev/null +++ b/libs/lzlib/gzip.lua @@ -0,0 +1,83 @@ + +--[===================================================================[ +TODO: + - add a nice header with license and stuff.. + - detect plain text files + - proper testing + - improve implementation? + - check gzopen flags for consistency (ex: strategy flag) +--]===================================================================] + +local io = require 'io' +local zlib = require 'zlib' + +local error, assert, setmetatable, tostring = error, assert, setmetatable, tostring + +local _M = {} + +function _M.open(filename, mode) + mode = mode or 'r' + local r = mode:find('r', 1, true) and true + local w = mode:find('w', 1, true) and true + local level = -1 + + local lstart, lend = mode:find('%d') + if (lstart and lend) then + level = mode:sub(lstart, lend) + end + + if (not (r or w)) then + error('file open mode must specify read or write operation') + end + + local f, z + + local mt = { + __index = { + read = function(self, ...) + return z:read(...) + end, + write = function(self, ...) + return z:write(...) + end, + seek = function(self, ...) + error 'seek not supported on gzip files' + end, + lines = function(self, ...) + return z:lines(...) + end, + flush = function(self, ...) + return z:flush(...) and f:flush() + end, + close = function(self, ...) + return z:close() and f:close() + end, + }, + __tostring = function(self) + return 'gzip object (' .. mode .. ') [' .. tostring(z) .. '] [' .. tostring(f) .. ']' + end, + } + + if r then + f = assert(io.open(filename, 'rb')) + z = assert(zlib.inflate(f)) + else + f = assert(io.open(filename, 'wb')) + z = assert(zlib.deflate(f, level, nil, 15 + 16)) + end + + return setmetatable({}, mt) +end + +function _M.lines(filename) + local gz = _M.open(filename, 'r') + return function() + local line = gz and gz:read() + if line == nil then + gz:close() + end + return line + end +end + +return _M \ No newline at end of file diff --git a/libs/lzlib/lakefile b/libs/lzlib/lakefile new file mode 100644 index 0000000..c322f41 --- /dev/null +++ b/libs/lzlib/lakefile @@ -0,0 +1,58 @@ +PROJECT = 'zlib' + +IF=choose +J=path.join + +if LUA_VER == '5.2' then + LUA_NEED = 'lua52' + LUA_DIR = ENV.LUA_DIR_5_2 or ENV.LUA_DIR + LUA_RUNNER = 'lua52' +else + LUA_NEED = 'lua' + LUA_DIR = ENV.LUA_DIR + LUA_RUNNER = 'lua' +end + +DYNAMIC = DYNAMIC or false + +ZLIB_NEED = IF(DYNAMIC, 'zlib-static-md', 'zlib-static-mt') + +INSTALL_DIR = INSTALL_DIR or J(LUA_DIR,'libs',PROJECT) + +build = c.shared{PROJECT, + -- base = 'src', + src = '*.c', + needs = { LUA_NEED, ZLIB_NEED }, + dynamic = DYNAMIC, + strip = true, + defines = IF(MSVC, "STRUCT_INT=__int64"), + libflags = IF(MSVC, "/EXPORT:luaopen_" .. PROJECT), +} + +target('build', build) + +install = target('install', { + file.group{odir=J(INSTALL_DIR, 'share');src = build }; + file.group{odir=J(INSTALL_DIR, 'share');src = 'gzip.lua' }; + file.group{odir=J(INSTALL_DIR, 'test'); src = 'test_*.lua'}; +}) + +local function run(file, cwd) + print() + print("run " .. file) + if not TESTING then + if cwd then lake.chdir(cwd) end + os.execute( LUA_RUNNER .. ' ' .. file ) + if cwd then lake.chdir("<") end + print() + end +end + +target('test', install, function() + local test_dir = J(INSTALL_DIR,'test') + run(J(test_dir,'test_zlib3.lua'), test_dir) + run(J(test_dir,'test_zlib2.lua'), test_dir) + run(J(test_dir,'test_prologue.lua'), test_dir) + run(J(test_dir,'test_gzip.lua'), test_dir) +end) + diff --git a/libs/lzlib/lzlib.c b/libs/lzlib/lzlib.c new file mode 100644 index 0000000..1890b9b --- /dev/null +++ b/libs/lzlib/lzlib.c @@ -0,0 +1,986 @@ +/************************************************************************ +* Author : Tiago Dionizio * +* Library : lzlib - Lua 5 interface to access zlib library functions * +* * +* 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. * +************************************************************************/ + +#include +#include + +#include "lua.h" +#include "lauxlib.h" + +#include "zlib.h" + +/* +** ========================================================================= +** compile time options wich determine available functionality +** ========================================================================= +*/ + +/* TODO + +- also call flush on table/userdata when flush function is detected +- remove io_cb check inflate_block if condition +- only set eos when ZSTREAM_END is reached +- check for stream errors to close stream when really needed + +*/ + + +/* +** ========================================================================= +** zlib stream metamethods +** ========================================================================= +*/ +#define ZSTREAMMETA "zlib:zstream" + +#define LZ_ANY -1 +#define LZ_NONE 0 +#define LZ_DEFLATE 1 +#define LZ_INFLATE 2 + +#if 0 + #define LZ_BUFFER_SIZE LUAL_BUFFERSIZE +#else + #define LZ_BUFFER_SIZE 8192 +#endif + +typedef struct { + /* zlib structures */ + z_stream zstream; + /* stream state. LZ_DEFLATE | LZ_INFLATE */ + int state; + int error; + int peek; + int eos; + /* user callback source for reading/writing */ + int io_cb; + /* input buffer */ + int i_buffer_ref; + size_t i_buffer_pos; + size_t i_buffer_len; + const char *i_buffer; + /* output buffer */ + size_t o_buffer_len; + size_t o_buffer_max; + char o_buffer[LZ_BUFFER_SIZE]; + /* dictionary */ + const Bytef *dictionary; + size_t dictionary_len; +} lz_stream; + + +/* forward declarations */ +static int lzstream_docompress(lua_State *L, lz_stream *s, int from, int to, int flush); + + +static lz_stream *lzstream_new(lua_State *L, int src) { + lz_stream *s = (lz_stream*)lua_newuserdata(L, sizeof(lz_stream)); + + luaL_getmetatable(L, ZSTREAMMETA); + lua_setmetatable(L, -2); /* set metatable */ + + s->state = LZ_NONE; + s->error = Z_OK; + s->eos = 0; + s->io_cb = LUA_REFNIL; + + s->i_buffer = NULL; + s->i_buffer_ref = LUA_REFNIL; + s->i_buffer_pos = 0; + s->i_buffer_len = 0; + + s->peek = 0; + s->o_buffer_len = 0; + s->o_buffer_max = sizeof(s->o_buffer) / sizeof(s->o_buffer[0]); + + s->zstream.zalloc = Z_NULL; + s->zstream.zfree = Z_NULL; + + /* prepare source */ + if (lua_isstring(L, src)) { + lua_pushvalue(L, src); + s->i_buffer_ref = luaL_ref(L, LUA_REGISTRYINDEX); + s->i_buffer = lua_tolstring(L, src, &s->i_buffer_len); + } else { + /* table | function | userdata */ + lua_pushvalue(L, src); + s->io_cb = luaL_ref(L, LUA_REGISTRYINDEX); + } + return s; +} + +static void lzstream_cleanup(lua_State *L, lz_stream *s) { + if (s && s->state != LZ_NONE) { + if (s->state == LZ_INFLATE) { + inflateEnd(&s->zstream); + } + if (s->state == LZ_DEFLATE) { + deflateEnd(&s->zstream); + } + + luaL_unref(L, LUA_REGISTRYINDEX, s->io_cb); + luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref); + s->state = LZ_NONE; + } +} + +/* ====================================================================== */ + +static lz_stream *lzstream_get(lua_State *L, int index) { + lz_stream *s = (lz_stream*)luaL_checkudata(L, index, ZSTREAMMETA); + if (s == NULL) luaL_argerror(L, index, "bad zlib stream"); + return s; +} + +static lz_stream *lzstream_check(lua_State *L, int index, int state) { + lz_stream *s = lzstream_get(L, index); + if ((state != LZ_ANY && s->state != state) || s->state == LZ_NONE) { + luaL_argerror(L, index, "attempt to use invalid zlib stream"); + } + return s; +} + +/* ====================================================================== */ + +static int lzstream_tostring(lua_State *L) { + lz_stream *s = (lz_stream*)luaL_checkudata(L, 1, ZSTREAMMETA); + if (s == NULL) luaL_argerror(L, 1, "bad zlib stream"); + + if (s->state == LZ_NONE) { + lua_pushstring(L, "zlib stream (closed)"); + } else if (s->state == LZ_DEFLATE) { + lua_pushfstring(L, "zlib deflate stream (%p)", (void*)s); + } else if (s->state == LZ_INFLATE) { + lua_pushfstring(L, "zlib inflate stream (%p)", (void*)s); + } else { + lua_pushfstring(L, "%p", (void*)s); + } + + return 1; +} + +/* ====================================================================== */ + +static int lzstream_gc(lua_State *L) { + lz_stream *s = lzstream_get(L, 1); + lzstream_cleanup(L, s); + return 0; +} + +/* ====================================================================== */ + +static int lzstream_close(lua_State *L) { + lz_stream *s = lzstream_get(L, 1); + + if (s->state == LZ_DEFLATE) { + lua_settop(L, 0); + lua_pushliteral(L, ""); + return lzstream_docompress(L, s, 1, 1, Z_FINISH); + } + + lzstream_cleanup(L, s); + lua_pushboolean(L, 1); + return 1; +} + +/* ====================================================================== */ + +static int lzstream_adler(lua_State *L) { + lz_stream *s = lzstream_check(L, 1, LZ_ANY); + lua_pushnumber(L, s->zstream.adler); + return 1; +} + +/* ====================================================================== */ + +/* + zlib.deflate( + sink: function | { write: function [, close: function, flush: function] }, + compression level, [Z_DEFAILT_COMPRESSION] + method, [Z_DEFLATED] + windowBits, [15] + memLevel, [8] + strategy, [Z_DEFAULT_STRATEGY] + dictionary: [""] + ) +*/ +static int lzlib_deflate(lua_State *L) { + int level, method, windowBits, memLevel, strategy; + lz_stream *s; + const char *dictionary; + size_t dictionary_len; + + if (lua_istable(L, 1) || lua_isuserdata(L, 1)) { + /* is there a :write function? */ + lua_getfield(L, 1, "write"); + if (!lua_isfunction(L, -1)) { + luaL_argerror(L, 1, "output parameter does not provide :write function"); + } + lua_pop(L, 1); + } + else if (!lua_isfunction(L, 1)) { + luaL_argerror(L, 1, "output parameter must be a function, table or userdata value"); + } + + level = (int) luaL_optinteger(L, 2, Z_DEFAULT_COMPRESSION); + method = (int) luaL_optinteger(L, 3, Z_DEFLATED); + windowBits = (int) luaL_optinteger(L, 4, 15); + memLevel = (int) luaL_optinteger(L, 5, 8); + strategy = (int) luaL_optinteger(L, 6, Z_DEFAULT_STRATEGY); + dictionary = luaL_optlstring(L, 7, NULL, &dictionary_len); + + s = lzstream_new(L, 1); + + if (deflateInit2(&s->zstream, level, method, windowBits, memLevel, strategy) != Z_OK) { + lua_pushliteral(L, "call to deflateInit2 failed"); + lua_error(L); + } + + if (dictionary) { + if (deflateSetDictionary(&s->zstream, (const Bytef *) dictionary, dictionary_len) != Z_OK) { + lua_pushliteral(L, "call to deflateSetDictionnary failed"); + lua_error(L); + } + } + + s->state = LZ_DEFLATE; + return 1; +} + +/* + zlib.inflate( + source: string | function | { read: function, close: function }, + windowBits: number, [15] + dictionary: [""] + ) +*/ +static int lzlib_inflate(lua_State *L) +{ + int windowBits; + lz_stream *s; + int have_peek = 0; + const char *dictionary; + size_t dictionary_len; + + if (lua_istable(L, 1) || lua_isuserdata(L, 1)) { + /* is there a :read function? */ + lua_getfield(L, 1, "read"); + if (!lua_isfunction(L, -1)) { + luaL_argerror(L, 1, "input parameter does not provide :read function"); + } + lua_pop(L, 1); + /* check for peek function */ + lua_getfield(L, 1, "peek"); + have_peek = lua_isfunction(L, -1); + lua_pop(L, 1); + } + else if (!lua_isstring(L, 1) && !lua_isfunction(L, 1)) { + luaL_argerror(L, 1, "input parameter must be a string, function, table or userdata value"); + } + + windowBits = (int) luaL_optinteger(L, 2, 15); + dictionary = luaL_optlstring(L, 3, NULL, &dictionary_len); + + s = lzstream_new(L, 1); + + if (windowBits > 0 && windowBits < 16) { + windowBits |= 32; + } + + if (inflateInit2(&s->zstream, windowBits) != Z_OK) { + lua_pushliteral(L, "call to inflateInit2 failed"); + lua_error(L); + } + + if (dictionary) { + s->dictionary = (const Bytef *) dictionary; + s->dictionary_len = dictionary_len; + } + + s->peek = have_peek; + s->state = LZ_INFLATE; + return 1; +} + +/* ====================================================================== */ + +static int lz_pushresult (lua_State *L, lz_stream *s) { + if (s->error == Z_OK) { + lua_pushboolean(L, 1); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, zError(s->error)); + lua_pushinteger(L, s->error); + return 3; + } +} + +/* + Get block to process: + - top of stack gets +*/ +static const char* lzstream_fetch_block(lua_State *L, lz_stream *s, int hint) { + if (s->i_buffer_pos >= s->i_buffer_len) { + luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref); + s->i_buffer_ref = LUA_NOREF; + s->i_buffer = NULL; + + lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb); + if (!lua_isnil(L, -1)) { + if (lua_isfunction(L, -1)) { + lua_pushinteger(L, hint); + lua_call(L, 1, 1); + } else { + lua_getfield(L, -1, (s->peek ? "peek" : "read")); + lua_insert(L, -2); + lua_pushinteger(L, hint); + lua_call(L, 2, 1); + } + + if (lua_isstring(L, -1)) { + s->i_buffer_pos = 0; + s->i_buffer = lua_tolstring(L, -1, &s->i_buffer_len); + if (s->i_buffer_len > 0) { + s->i_buffer_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } else { + lua_pop(L, 1); + } + } else if (lua_isnil(L, -1)) { + lua_pop(L, 1); + } else { + lua_pushliteral(L, "deflate callback must return string or nil"); + lua_error(L); + } + } else { + lua_pop(L, 1); + } + } + + return s->i_buffer; +} + +static int lzstream_inflate_block(lua_State *L, lz_stream *s) { + if (lzstream_fetch_block(L, s, LZ_BUFFER_SIZE) || !s->eos) { + int r; + + if (s->i_buffer_len == s->i_buffer_pos) { + s->zstream.next_in = NULL; + s->zstream.avail_in = 0; + } else { + s->zstream.next_in = (unsigned char*)(s->i_buffer + s->i_buffer_pos); + s->zstream.avail_in = s->i_buffer_len - s->i_buffer_pos; + } + + s->zstream.next_out = (unsigned char*)s->o_buffer + s->o_buffer_len; + s->zstream.avail_out = s->o_buffer_max - s->o_buffer_len; + + /* munch some more */ + r = inflate(&s->zstream, Z_SYNC_FLUSH); + + if (r == Z_NEED_DICT) { + if (s->dictionary == NULL) { + lua_pushliteral(L, "no inflate dictionary provided"); + lua_error(L); + } + + if (inflateSetDictionary(&s->zstream, s->dictionary, s->dictionary_len) != Z_OK) { + lua_pushliteral(L, "call to inflateSetDictionnary failed"); + lua_error(L); + } + + r = inflate(&s->zstream, Z_SYNC_FLUSH); + } + + if (r != Z_OK && r != Z_STREAM_END && r != Z_BUF_ERROR) { + lzstream_cleanup(L, s); + s->error = r; + #if 1 + lua_pushfstring(L, "failed to decompress [%d]", r); + lua_error(L); + #endif + } + + if (r == Z_STREAM_END) { + luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref); + s->i_buffer_ref = LUA_NOREF; + s->i_buffer = NULL; + + s->eos = 1; + } + + /* number of processed bytes */ + if (s->peek) { + size_t processed = s->i_buffer_len - s->i_buffer_pos - s->zstream.avail_in; + + lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb); + lua_getfield(L, -1, "read"); + lua_insert(L, -2); + lua_pushinteger(L, processed); + lua_call(L, 2, 0); + } + + s->i_buffer_pos = s->i_buffer_len - s->zstream.avail_in; + s->o_buffer_len = s->o_buffer_max - s->zstream.avail_out; + } + + return s->o_buffer_len; +} + +/* +** Remove n bytes from the output buffer. +*/ +static void lzstream_remove(lz_stream *s, size_t n) { + memmove(s->o_buffer, s->o_buffer + n, s->o_buffer_len - n); + s->o_buffer_len -= n; +} + +/* +** Copy at most n bytes to buffer b and remove them from the +** output stream buffer. +*/ +static int lzstream_flush_buffer(lua_State *L, lz_stream *s, size_t n, luaL_Buffer *b) { + /* check output */ + if (n > s->o_buffer_len) { + n = s->o_buffer_len; + } + + if (n > 0) { + lua_pushlstring(L, s->o_buffer, n); + luaL_addvalue(b); + + lzstream_remove(s, n); + } + + return n; +} + +/* + z:read( + {number | '*l' | '*a'}* + ) +*/ +static int lz_test_eof(lua_State *L, lz_stream *s) { + lua_pushlstring(L, NULL, 0); + if (s->o_buffer_len > 0) { + return 1; + } else if (s->eos) { + return 0; + } else { + return lzstream_inflate_block(L, s); + } +} + +static int lz_read_line(lua_State *L, lz_stream *s) { + luaL_Buffer b; + size_t l = 0, n; + + luaL_buffinit(L, &b); + + if (s->o_buffer_len > 0 || !s->eos) do { + char *p = s->o_buffer; + size_t len = s->o_buffer_len; + + /* find newline in output buffer */ + for (n = 0; n < len; ++n, ++p) { + if (*p == '\n' || *p == '\r') { + int eat_nl = *p == '\r'; + luaL_addlstring(&b, s->o_buffer, n); + lzstream_remove(s, n+1); + l += n; + + if (eat_nl && lzstream_inflate_block(L, s)) { + if (s->o_buffer_len > 0 && *s->o_buffer == '\n') { + lzstream_remove(s, 1); + } + } + + luaL_pushresult(&b); + return 1; + } + } + + if (len > 0) { + luaL_addlstring(&b, s->o_buffer, len); + lzstream_remove(s, len); + l += len; + } + } while (lzstream_inflate_block(L, s)); + + luaL_pushresult(&b); + return l > 0 || !s->eos || s->o_buffer_len > 0; +} + + +static int lz_read_chars(lua_State *L, lz_stream *s, size_t n) { + size_t len; + luaL_Buffer b; + luaL_buffinit(L, &b); + + if (s->o_buffer_len > 0 || !s->eos) do { + size_t rlen = lzstream_flush_buffer(L, s, n, &b); + n -= rlen; + } while (n > 0 && lzstream_inflate_block(L, s)); + + luaL_pushresult(&b); + lua_tolstring(L, -1, &len); + return n == 0 || len > 0; +} + +static int lzstream_decompress(lua_State *L) { + lz_stream *s = lzstream_check(L, 1, LZ_INFLATE); + int nargs = lua_gettop(L) - 1; + int success; + int n; + if (nargs == 0) { /* no arguments? */ + success = lz_read_line(L, s); + n = 3; /* to return 1 result */ + } + else { /* ensure stack space for all results and for auxlib's buffer */ + luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); + success = 1; + for (n = 2; nargs-- && success; n++) { + if (lua_type(L, n) == LUA_TNUMBER) { + size_t l = (size_t)lua_tointeger(L, n); + success = (l == 0) ? lz_test_eof(L, s) : lz_read_chars(L, s, l); + } + else { + const char *p = lua_tostring(L, n); + luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); + switch (p[1]) { + case 'l': /* line */ + success = lz_read_line(L, s); + break; + case 'a': /* file */ + lz_read_chars(L, s, ~((size_t)0)); /* read MAX_SIZE_T chars */ + success = 1; /* always success */ + break; + default: + return luaL_argerror(L, n, "invalid format"); + } + } + } + } + if (s->error != Z_OK) { + return lz_pushresult(L, s); + } + if (!success) { + lua_pop(L, 1); /* remove last result */ + lua_pushnil(L); /* push nil instead */ + } + return n - 2; +} + + +static int lzstream_readline(lua_State *L) { + lz_stream *s; + int sucess; + + s = lzstream_check(L, lua_upvalueindex(1), LZ_INFLATE); + sucess = lz_read_line(L, s); + + if (s->error != Z_OK) { + return lz_pushresult(L, s); + } + + if (sucess) { + return 1; + } else { + /* EOF */ + return 0; + } +} + +static int lzstream_lines(lua_State *L) { + lzstream_check(L, 1, LZ_INFLATE); + lua_settop(L, 1); + lua_pushcclosure(L, lzstream_readline, 1); + return 1; +} + +/* ====================================================================== */ + +static int lzstream_docompress(lua_State *L, lz_stream *s, int from, int to, int flush) { + int r, arg; + int self = 0; + size_t b_size = s->o_buffer_max; + unsigned char *b = (unsigned char *)s->o_buffer; + + /* number of processed bytes */ + lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb); + if (!lua_isfunction(L, -1)) { + self = 1; + lua_getfield(L, -1, "write"); + } + + for (arg = from; arg <= to; arg++) { + s->zstream.next_in = (unsigned char*)luaL_checklstring(L, arg, (size_t*)&s->zstream.avail_in); + + do { + s->zstream.next_out = b; + s->zstream.avail_out = b_size; + + /* bake some more */ + r = deflate(&s->zstream, flush); + if (r != Z_OK && r != Z_STREAM_END && r != Z_BUF_ERROR) { + lzstream_cleanup(L, s); + lua_pushboolean(L, 0); + lua_pushfstring(L, "failed to compress [%d]", r); + return 2; + } + + if (s->zstream.avail_out != b_size) { + /* write output */ + lua_pushvalue(L, -1); /* function */ + if (self) lua_pushvalue(L, -3); /* self */ + lua_pushlstring(L, (char*)b, b_size - s->zstream.avail_out); /* data */ + lua_call(L, (self ? 2 : 1), 0); + } + + if (r == Z_STREAM_END) { + lzstream_cleanup(L, s); + break; + } + + /* process all input */ + } while (s->zstream.avail_in > 0 || s->zstream.avail_out == 0); + } + + lua_pushboolean(L, 1); + return 1; +} + +static int lzstream_compress(lua_State *L) { + lz_stream *s = lzstream_check(L, 1, LZ_DEFLATE); + return lzstream_docompress(L, s, 2, lua_gettop(L), Z_NO_FLUSH); +} + + +/* ====================================================================== */ + +static int lzstream_flush(lua_State *L) { + static int flush_values[] = { Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH }; + static const char *const flush_opts[] = { "sync", "full", "finish" }; + + lz_stream *s = lzstream_check(L, 1, LZ_DEFLATE); + int flush = luaL_checkoption(L, 2, flush_opts[0], flush_opts); + + lua_settop(L, 0); + lua_pushliteral(L, ""); + return lzstream_docompress(L, s, 1, 1, flush_values[flush]); +} + +/* +** ========================================================================= +** zlib functions +** ========================================================================= +*/ + +static int lzlib_version(lua_State *L) +{ + lua_pushstring(L, zlibVersion()); + return 1; +} + +/* ====================================================================== */ +static int lzlib_adler32(lua_State *L) +{ + if (lua_gettop(L) == 0) + { + /* adler32 initial value */ + lua_pushnumber(L, adler32(0L, Z_NULL, 0)); + } + else + { + /* update adler32 checksum */ + size_t len; + int adler = (int) luaL_checkinteger(L, 1); + const unsigned char* buf = (unsigned char*)luaL_checklstring(L, 2, &len); + + lua_pushnumber(L, adler32(adler, buf, len)); + } + return 1; +} + +/* ====================================================================== */ +static int lzlib_crc32(lua_State *L) +{ + if (lua_gettop(L) == 0) + { + /* crc32 initial value */ + lua_pushnumber(L, crc32(0L, Z_NULL, 0)); + } + else + { + /* update crc32 checksum */ + size_t len; + int crc = (int) luaL_checkinteger(L, 1); + const unsigned char* buf = (unsigned char*)luaL_checklstring(L, 2, &len); + + lua_pushnumber(L, crc32(crc, buf, len)); + } + return 1; +} + +/* ====================================================================== */ + + +static int lzlib_compress(lua_State *L) { + size_t avail_in; + const char *next_in = luaL_checklstring(L, 1, &avail_in); + int level = (int) luaL_optinteger(L, 2, Z_DEFAULT_COMPRESSION); + int method = (int) luaL_optinteger(L, 3, Z_DEFLATED); + int windowBits = (int) luaL_optinteger(L, 4, 15); + int memLevel = (int) luaL_optinteger(L, 5, 8); + int strategy = (int) luaL_optinteger(L, 6, Z_DEFAULT_STRATEGY); + + int ret; + luaL_Buffer b; + z_stream zs; + + luaL_buffinit(L, &b); + + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + + zs.next_out = Z_NULL; + zs.avail_out = 0; + zs.next_in = Z_NULL; + zs.avail_in = 0; + + ret = deflateInit2(&zs, level, method, windowBits, memLevel, strategy); + + if (ret != Z_OK) + { + lua_pushnil(L); + lua_pushnumber(L, ret); + return 2; + } + + zs.next_in = (unsigned char*)next_in; + zs.avail_in = avail_in; + + for(;;) + { + zs.next_out = (unsigned char*)luaL_prepbuffer(&b); + zs.avail_out = LUAL_BUFFERSIZE; + + /* munch some more */ + ret = deflate(&zs, Z_FINISH); + + /* push gathered data */ + luaL_addsize(&b, LUAL_BUFFERSIZE - zs.avail_out); + + /* done processing? */ + if (ret == Z_STREAM_END) + break; + + /* error condition? */ + if (ret != Z_OK) + break; + } + + /* cleanup */ + deflateEnd(&zs); + + luaL_pushresult(&b); + lua_pushnumber(L, ret); + return 2; +} + +/* ====================================================================== */ + +static int lzlib_decompress(lua_State *L) +{ + size_t avail_in; + const char *next_in = luaL_checklstring(L, 1, &avail_in); + int windowBits = (int) luaL_optinteger(L, 2, 15); + + int ret; + luaL_Buffer b; + z_stream zs; + + luaL_buffinit(L, &b); + + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + + zs.next_out = Z_NULL; + zs.avail_out = 0; + zs.next_in = Z_NULL; + zs.avail_in = 0; + + ret = inflateInit2(&zs, windowBits); + + if (ret != Z_OK) { + lua_pushliteral(L, "failed to initialize zstream structures"); + lua_error(L); + } + + zs.next_in = (unsigned char*)next_in; + zs.avail_in = avail_in; + + for (;;) { + zs.next_out = (unsigned char*)luaL_prepbuffer(&b); + zs.avail_out = LUAL_BUFFERSIZE; + + /* bake some more */ + ret = inflate(&zs, Z_FINISH); + + /* push gathered data */ + luaL_addsize(&b, LUAL_BUFFERSIZE - zs.avail_out); + + /* done processing? */ + if (ret == Z_STREAM_END) + break; + + if (ret != Z_OK && ret != Z_BUF_ERROR) { + /* cleanup */ + inflateEnd(&zs); + + lua_pushliteral(L, "failed to process zlib stream"); + lua_error(L); + } + } + + /* cleanup */ + inflateEnd(&zs); + + luaL_pushresult(&b); + return 1; +} + + +/* +** ========================================================================= +** Register functions +** ========================================================================= +*/ + +#if (LUA_VERSION_NUM >= 502) + +#define luaL_register(L,n,f) luaL_setfuncs(L,f,0) + +#endif + +LUALIB_API int luaopen_zlib(lua_State *L) +{ + const luaL_Reg lzstream_meta[] = + { + {"write", lzstream_compress }, + {"read", lzstream_decompress }, + {"lines", lzstream_lines }, + {"flush", lzstream_flush }, + {"close", lzstream_close }, + + {"adler", lzstream_adler }, + + {"__tostring", lzstream_tostring }, + {"__gc", lzstream_gc }, + {NULL, NULL} + }; + + const luaL_Reg zlib[] = + { + {"version", lzlib_version }, + {"adler32", lzlib_adler32 }, + {"crc32", lzlib_crc32 }, + + {"deflate", lzlib_deflate }, + {"inflate", lzlib_inflate }, + + {"compress", lzlib_compress }, + {"decompress", lzlib_decompress }, + + {NULL, NULL} + }; + + /* ====================================================================== */ + + /* create new metatable for zlib compression structures */ + luaL_newmetatable(L, ZSTREAMMETA); + lua_pushliteral(L, "__index"); + lua_pushvalue(L, -2); /* push metatable */ + lua_rawset(L, -3); /* metatable.__index = metatable */ + + /* + ** Stack: metatable + */ + luaL_register(L, NULL, lzstream_meta); + + lua_pop(L, 1); /* remove metatable from stack */ + + /* + ** Stack: + */ + lua_newtable(L); + + lua_pushliteral (L, "_COPYRIGHT"); + lua_pushliteral (L, "Copyright (C) 2003-2010 Tiago Dionizio"); + lua_settable (L, -3); + lua_pushliteral (L, "_DESCRIPTION"); + lua_pushliteral (L, "Lua 5 interface to access zlib library functions"); + lua_settable (L, -3); + lua_pushliteral (L, "_VERSION"); + lua_pushliteral (L, "lzlib 0.4-work3"); + lua_settable (L, -3); + +#define PUSH_LITERAL(name) \ + lua_pushliteral (L, #name); \ + lua_pushinteger (L, Z_##name); \ + lua_settable (L, -3); + +#define PUSH_NUMBER(name, value) \ + lua_pushliteral (L, #name); \ + lua_pushinteger (L, value); \ + lua_settable (L, -3); + + PUSH_LITERAL(NO_COMPRESSION) + PUSH_LITERAL(BEST_SPEED) + PUSH_LITERAL(BEST_COMPRESSION) + PUSH_LITERAL(DEFAULT_COMPRESSION) + + PUSH_LITERAL(FILTERED) + PUSH_LITERAL(HUFFMAN_ONLY) + PUSH_LITERAL(RLE) + PUSH_LITERAL(FIXED) + PUSH_LITERAL(DEFAULT_STRATEGY) + + PUSH_NUMBER(MINIMUM_MEMLEVEL, 1) + PUSH_NUMBER(MAXIMUM_MEMLEVEL, 9) + PUSH_NUMBER(DEFAULT_MEMLEVEL, 8) + + PUSH_NUMBER(DEFAULT_WINDOWBITS, 15) + PUSH_NUMBER(MINIMUM_WINDOWBITS, 8) + PUSH_NUMBER(MAXIMUM_WINDOWBITS, 15) + + PUSH_NUMBER(GZIP_WINDOWBITS, 16) + PUSH_NUMBER(RAW_WINDOWBITS, -1) + + luaL_register(L, NULL, zlib); + + /* + ** Stack: zlib table + */ + return 1; +} diff --git a/libs/lzlib/rockspec/INSTALL b/libs/lzlib/rockspec/INSTALL new file mode 100644 index 0000000..8306ced --- /dev/null +++ b/libs/lzlib/rockspec/INSTALL @@ -0,0 +1,23 @@ +Installation +------------ + + luarocks build lzlib-git-1.rockspec + + +Troubleshooting +--------------- + +* Error: Could not find expected file libz.a, or libz.so, or libz.so.* + for ZLIB -- you may have to install ZLIB in your system and/or pass + ZLIB_DIR or ZLIB_LIBDIR to the luarocks command. Example: luarocks + install lzlib ZLIB_DIR=/usr/local + + lzlib has a dependency on zlib, and currently (on Linux) LuaRocks + checks .so file existence by searching for the file in two hardcoded + paths, /usr/lib and /usr/local/lib. (instead of reading + /etc/ld.so.conf) + + Try to locate libz.so, libz.a or libz.dll and re-run the command + with ZLIB_LIBDIR=/path/to/libz.so. In my case on Ubuntu 12.04 : + + luarocks build --local lzlib-git-1.rockspec ZLIB_LIBDIR=/lib/x86_64-linux-gnu/ \ No newline at end of file diff --git a/libs/lzlib/rockspec/lzlib-git-1.rockspec b/libs/lzlib/rockspec/lzlib-git-1.rockspec new file mode 100644 index 0000000..cbd68e6 --- /dev/null +++ b/libs/lzlib/rockspec/lzlib-git-1.rockspec @@ -0,0 +1,37 @@ +package="lzlib" +version="git-1" +source = { + url = "git://github.com/LuaDist/lzlib.git", + branch = "master", +} +description = { + summary = "Lua bindings to the ZLib compression library", + detailed = [[ + This package provides a library to access zlib library functions + and also to read/write gzip files using an interface similar + to the base io package. + ]], + homepage = "http://luaforge.net/projects/lzlib/", + license = "MIT/X11" +} +dependencies = { + "lua >= 5.1" +} +external_dependencies = { + ZLIB = { + header = "zlib.h", + library = "z", + } +} +build = { + type = "builtin", + modules = { + zlib = { + sources = "lzlib.c", + libdirs = "$(ZLIB_LIBDIR)", + incdirs = "$(ZLIB_INCDIR)", + libraries = "z", + }, + gzip = "gzip.lua", + } +} diff --git a/libs/lzlib/test_gzip.lua b/libs/lzlib/test_gzip.lua new file mode 100644 index 0000000..9f8810b --- /dev/null +++ b/libs/lzlib/test_gzip.lua @@ -0,0 +1,101 @@ + +local gzip = require 'gzip' + +local function line(header, c) + header = header or '' + c = c or '-' + print(string.rep(string.sub(c, 1, 1), 78 - string.len(header))..header) +end + + +line(' gzip', '=') + +line(' gzip writing') +local loops = 1000 +local testfile = "test.gz" + +local of = gzip.open(testfile, "wb9") + +if (not of) then + error("Failed to open file test.gz for writing") +end + +for i = 1, loops do + of:write(i, "\n") +end + +of:close() + +local i = 0 +for l in gzip.lines(testfile) do + i = i + 1 + if (tostring(i) ~= l) then + error(tostring(i)) + end +end + +assert(i == loops) +print('Ok.') +line(' gzip reading') + +local inf = gzip.open(testfile) + +if (not inf) then + error("Failed to open file test.gz for reading") +end + +for i = 1, loops do + if (tostring(i) ~= inf:read("*l")) then + error(tostring(i)) + end +end + +inf:close() + +print('Ok.') +if false then + line(' compress seek') + + of = gzip.open(testfile, "wb1") + + if (not of) then + error("Failed to open file test.gz for writing") + end + + assert(of:seek("cur", 5) == 5) + assert(of:seek("set", 10) == 10) + + of:write("1") + + of:close() + + print('Ok.') + + line(' uncompress seek') + + inf = gzip.open(testfile) + + if (not inf) then + error("Failed to open file test.gz for reading") + end + + assert(inf:seek("set", 6) == 6) + assert(inf:seek("set", 4) == 4) + assert(inf:seek("cur", 1) == 5) + assert(inf:seek("cur", -1) == 4) + assert(inf:seek("cur", 1) == 5) + assert(inf:seek("set", 6) == 6) + + inf:read(4) + + assert(inf:read(1) == "1") + + inf:close() + + print('Ok.') + +end + +os.remove(testfile) + +line(' gzip', '=') diff --git a/libs/lzlib/test_prologue.lua b/libs/lzlib/test_prologue.lua new file mode 100644 index 0000000..4a39cbd --- /dev/null +++ b/libs/lzlib/test_prologue.lua @@ -0,0 +1,2 @@ +print('lua version : ' .. _VERSION) +print('zlib version: ' .. (require('zlib')._VERSION)) diff --git a/libs/lzlib/test_zlib2.lua b/libs/lzlib/test_zlib2.lua new file mode 100644 index 0000000..9484e8e --- /dev/null +++ b/libs/lzlib/test_zlib2.lua @@ -0,0 +1,93 @@ + +local zlib = require 'zlib' + +local gzip = require 'gzip' + +local data = 'abcde' +local cdata = zlib.compress(data) +local udata = zlib.decompress(cdata) + +print('udata=['..udata..']') + +local z = zlib.inflate(cdata) +print('z', z) +for i = 1, 5 do + print(i, z:read(1)) +end +z:close() + +of = gzip.open('txt.gz', "wb9") +i = assert(io.open('txt', 'w')) +for _,str in ipairs{'a', 'b', 'c'} do + local s = (str:rep(10)) ,'\n' + i:write(s) + of:write(s) +end +i:close() +of:close() + +i = assert(io.open('txt', 'rb')) org = i:read('*a') i:close() +i = io.open('txt.gz', 'rb') +dup = '' + +o = io.open('txt.out', 'wb') +z = zlib.inflate(i) +print('z = ', z) +repeat + local l = z:read(1024) + if not l then + break + end + dup = dup .. l + o:write(l) +until false +i:close() +o:close() +z:close() + +print('result:', (org == dup), org:len(), dup:len()) + +i = io.open('txt.gz', 'rb') +o = io.open('txt.lines.out', 'w') +cpeek = 0 +ccon = 0 +z = zlib.inflate({ + peek = function(self, hint) + local d = i:read(hint) + if (d ~= nil) then + i:seek('cur', -d:len()) + cpeek = cpeek + d:len() + end + --print('called peek with', hint, 'got', d and d:len()) + return d + end, + read = function(self, consume) + --print('called read with', consume) + ccon = ccon + consume + i:read(consume) + end +}) +print('z = ', z) +for line in z:lines() do + o:write(line, '\n') +end +i:close() +o:close() +z:close() + +print('stats:', cpeek, ccon) +print('z = ', z) + +i = io.open('txt', 'r') +o = io.open('txt-zlib.gz', 'wb') +z = zlib.deflate(o, nil, nil, 15 + 16) +for line in i:lines() do + z:write(line, '\n') +end +z:flush('finish') +z:close() +o:close() +i:close() + +os.remove("txt") +os.remove("txt.gz") \ No newline at end of file diff --git a/libs/lzlib/test_zlib3.lua b/libs/lzlib/test_zlib3.lua new file mode 100644 index 0000000..c017878 --- /dev/null +++ b/libs/lzlib/test_zlib3.lua @@ -0,0 +1,29 @@ +local zlib = require"zlib" + +local dataToDeflate = {} +print("Generating test data...") +for i = 0, 10000 do + table.insert(dataToDeflate, string.sub(tostring(math.random()), 3)) +end +dataToDeflate = table.concat(dataToDeflate) + +print("Length of data to deflate", #dataToDeflate) + +local buffer = {} +local func = function(data) + table.insert(buffer, data) +end + +stream = zlib.deflate(func) --best compression, deflated +stream:write(dataToDeflate) +--stream:flush("sync") +--stream:flush() +stream:close() + +--local deflatedData = string.sub(table.concat(buffer), 3) -- needed for IE +local deflatedData = table.concat(buffer) +print(#deflatedData) + +streamIn = zlib.inflate(deflatedData) +local inflatedData = streamIn:read() +assert(dataToDeflate == inflatedData) diff --git a/libs/lzlib/test_zlib_dict.lua b/libs/lzlib/test_zlib_dict.lua new file mode 100644 index 0000000..218a6f8 --- /dev/null +++ b/libs/lzlib/test_zlib_dict.lua @@ -0,0 +1,34 @@ +local zlib = require"zlib" + +-- dict used by the SPDY protocol +local spdy_dict = "optionsheadpostputdeletetraceaccept:accept-charset:accept-encoding:accept-language:accept-ranges:age:allow:authorization:cache-control:connection:content-base:content-encoding:content-language:content-length:content-location:content-md5:content-range:content-type:date:etag:expect:expires:from:host:if-match:if-modified-since:if-none-match:if-range:if-unmodified-since:last-modified:location:max-forwards:pragma:proxy-authenticate:proxy-authorization:range:referer:retry-after:server:trailer:transfer-encoding:upgrade:user-agent:vary:via:warning:www-authenticate:methodgetstatus200 OKversionHTTP/1.1urlpublicset-cookie:keep-aliveorigin:100101201202205206300302303304305306307402405406407408409410411412413414415416417502504505203 Non-Authoritative Information204 No Content301 Moved Permanently400 Bad Request401 Unauthorized403 Forbidden404 Not Found500 Internal Server Error501 Not Implemented503 Service UnavailableJan Feb Mar Apr May Jun Jul Aug Sept Oct Nov Dec 00:00:00 Mon, Tue, Wed, Thu, Fri, Sat, Sun, GMTchunked,text/html,image/png,image/jpg,image/gif,application/xml,application/xhtml+xml,text/plain,text/javascript,publicprivatemax-age=gzip,deflate,sdchcharset=utf-8charset=iso-8859-1,utf-,*,enq=0." + +local dataToDeflate = {} +print("Generating test data...") +for i = 0, 10000 do + table.insert(dataToDeflate, string.sub(tostring(math.random()), 3)) +end +dataToDeflate = table.concat(dataToDeflate) + +print("Length of data to deflate", #dataToDeflate) + +local buffer = {} +local func = function(data) + table.insert(buffer, data) +end + +stream = zlib.deflate(func, nil, nil, nil, nil, nil, spdy_dict) --best compression, deflated +stream:write(dataToDeflate) +--stream:flush("sync") +--stream:flush() +stream:close() + +--local deflatedData = string.sub(table.concat(buffer), 3) -- needed for IE +local deflatedData = table.concat(buffer) +print(#deflatedData) + +streamIn = zlib.inflate(deflatedData, nil, spdy_dict) +local inflatedData = streamIn:read() +assert(dataToDeflate == inflatedData, + table.concat{"inflated data: ", inflatedData, "\n", + "deflated_data: ", dataToDeflate, "\n"}) diff --git a/libs/lzlib/zlib.def b/libs/lzlib/zlib.def new file mode 100644 index 0000000..462fc5d --- /dev/null +++ b/libs/lzlib/zlib.def @@ -0,0 +1,2 @@ +EXPORTS +luaopen_zlib diff --git a/source/lzlib.c b/source/lzlib.c index d045b1c..ad87e73 100644 --- a/source/lzlib.c +++ b/source/lzlib.c @@ -4,999 +4,12 @@ The `fs.lzlib` module. See https://github.com/LuaDist/lzlib for informations and @usage local lzlib = require("ctr.fs.lzlib") */ -/************************************************************************ -* Author : Tiago Dionizio * -* Library : lzlib - Lua 5 interface to access zlib library functions * -* * -* 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. * -************************************************************************/ - -/* -Library modified in order to work with ctrµLua. -The outdated Lua API functions will be replaced if needed. -*/ - -#include -#include - #include <3ds/types.h> -#include "lua.h" -#include "lauxlib.h" +#include +#include -#include "zlib.h" - -/* -** ========================================================================= -** compile time options wich determine available functionality -** ========================================================================= -*/ - -/* TODO - -- also call flush on table/userdata when flush function is detected -- remove io_cb check inflate_block if condition -- only set eos when ZSTREAM_END is reached -- check for stream errors to close stream when really needed - -*/ - - -/* -** ========================================================================= -** zlib stream metamethods -** ========================================================================= -*/ -#define ZSTREAMMETA "zlib:zstream" - -#define LZ_ANY -1 -#define LZ_NONE 0 -#define LZ_DEFLATE 1 -#define LZ_INFLATE 2 - -#if 0 - #define LZ_BUFFER_SIZE LUAL_BUFFERSIZE -#else - #define LZ_BUFFER_SIZE 8192 -#endif - -typedef struct { - /* zlib structures */ - z_stream zstream; - /* stream state. LZ_DEFLATE | LZ_INFLATE */ - int state; - int error; - int peek; - int eos; - /* user callback source for reading/writing */ - int io_cb; - /* input buffer */ - int i_buffer_ref; - size_t i_buffer_pos; - size_t i_buffer_len; - const char *i_buffer; - /* output buffer */ - size_t o_buffer_len; - size_t o_buffer_max; - char o_buffer[LZ_BUFFER_SIZE]; - /* dictionary */ - const Bytef *dictionary; - size_t dictionary_len; -} lz_stream; - - -/* forward declarations */ -static int lzstream_docompress(lua_State *L, lz_stream *s, int from, int to, int flush); - - -static lz_stream *lzstream_new(lua_State *L, int src) { - lz_stream *s = (lz_stream*)lua_newuserdata(L, sizeof(lz_stream)); - - luaL_getmetatable(L, ZSTREAMMETA); - lua_setmetatable(L, -2); /* set metatable */ - - s->state = LZ_NONE; - s->error = Z_OK; - s->eos = 0; - s->io_cb = LUA_REFNIL; - - s->i_buffer = NULL; - s->i_buffer_ref = LUA_REFNIL; - s->i_buffer_pos = 0; - s->i_buffer_len = 0; - - s->peek = 0; - s->o_buffer_len = 0; - s->o_buffer_max = sizeof(s->o_buffer) / sizeof(s->o_buffer[0]); - - s->zstream.zalloc = Z_NULL; - s->zstream.zfree = Z_NULL; - - /* prepare source */ - if (lua_isstring(L, src)) { - lua_pushvalue(L, src); - s->i_buffer_ref = luaL_ref(L, LUA_REGISTRYINDEX); - s->i_buffer = lua_tolstring(L, src, &s->i_buffer_len); - } else { - /* table | function | userdata */ - lua_pushvalue(L, src); - s->io_cb = luaL_ref(L, LUA_REGISTRYINDEX); - } - return s; -} - -static void lzstream_cleanup(lua_State *L, lz_stream *s) { - if (s && s->state != LZ_NONE) { - if (s->state == LZ_INFLATE) { - inflateEnd(&s->zstream); - } - if (s->state == LZ_DEFLATE) { - deflateEnd(&s->zstream); - } - - luaL_unref(L, LUA_REGISTRYINDEX, s->io_cb); - luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref); - s->state = LZ_NONE; - } -} - -/* ====================================================================== */ - -static lz_stream *lzstream_get(lua_State *L, int index) { - lz_stream *s = (lz_stream*)luaL_checkudata(L, index, ZSTREAMMETA); - if (s == NULL) luaL_argerror(L, index, "bad zlib stream"); - return s; -} - -static lz_stream *lzstream_check(lua_State *L, int index, int state) { - lz_stream *s = lzstream_get(L, index); - if ((state != LZ_ANY && s->state != state) || s->state == LZ_NONE) { - luaL_argerror(L, index, "attempt to use invalid zlib stream"); - } - return s; -} - -/* ====================================================================== */ - -static int lzstream_tostring(lua_State *L) { - lz_stream *s = (lz_stream*)luaL_checkudata(L, 1, ZSTREAMMETA); - if (s == NULL) luaL_argerror(L, 1, "bad zlib stream"); - - if (s->state == LZ_NONE) { - lua_pushstring(L, "zlib stream (closed)"); - } else if (s->state == LZ_DEFLATE) { - lua_pushfstring(L, "zlib deflate stream (%p)", (void*)s); - } else if (s->state == LZ_INFLATE) { - lua_pushfstring(L, "zlib inflate stream (%p)", (void*)s); - } else { - lua_pushfstring(L, "%p", (void*)s); - } - - return 1; -} - -/* ====================================================================== */ - -static int lzstream_gc(lua_State *L) { - lz_stream *s = lzstream_get(L, 1); - lzstream_cleanup(L, s); - return 0; -} - -/* ====================================================================== */ - -static int lzstream_close(lua_State *L) { - lz_stream *s = lzstream_get(L, 1); - - if (s->state == LZ_DEFLATE) { - lua_settop(L, 0); - lua_pushliteral(L, ""); - return lzstream_docompress(L, s, 1, 1, Z_FINISH); - } - - lzstream_cleanup(L, s); - lua_pushboolean(L, 1); - return 1; -} - -/* ====================================================================== */ - -static int lzstream_adler(lua_State *L) { - lz_stream *s = lzstream_check(L, 1, LZ_ANY); - lua_pushnumber(L, s->zstream.adler); - return 1; -} - -/* ====================================================================== */ - -/* - zlib.deflate( - sink: function | { write: function [, close: function, flush: function] }, - compression level, [Z_DEFAILT_COMPRESSION] - method, [Z_DEFLATED] - windowBits, [15] - memLevel, [8] - strategy, [Z_DEFAULT_STRATEGY] - dictionary: [""] - ) -*/ -static int lzlib_deflate(lua_State *L) { - int level, method, windowBits, memLevel, strategy; - lz_stream *s; - const char *dictionary; - size_t dictionary_len; - - if (lua_istable(L, 1) || lua_isuserdata(L, 1)) { - /* is there a :write function? */ - lua_getfield(L, 1, "write"); - if (!lua_isfunction(L, -1)) { - luaL_argerror(L, 1, "output parameter does not provide :write function"); - } - lua_pop(L, 1); - } - else if (!lua_isfunction(L, 1)) { - luaL_argerror(L, 1, "output parameter must be a function, table or userdata value"); - } - - level = (int) luaL_optinteger(L, 2, Z_DEFAULT_COMPRESSION); - method = (int) luaL_optinteger(L, 3, Z_DEFLATED); - windowBits = (int) luaL_optinteger(L, 4, 15); - memLevel = (int) luaL_optinteger(L, 5, 8); - strategy = (int) luaL_optinteger(L, 6, Z_DEFAULT_STRATEGY); - dictionary = luaL_optlstring(L, 7, NULL, &dictionary_len); - - s = lzstream_new(L, 1); - - if (deflateInit2(&s->zstream, level, method, windowBits, memLevel, strategy) != Z_OK) { - lua_pushliteral(L, "call to deflateInit2 failed"); - lua_error(L); - } - - if (dictionary) { - if (deflateSetDictionary(&s->zstream, (const Bytef *) dictionary, dictionary_len) != Z_OK) { - lua_pushliteral(L, "call to deflateSetDictionnary failed"); - lua_error(L); - } - } - - s->state = LZ_DEFLATE; - return 1; -} - -/* - zlib.inflate( - source: string | function | { read: function, close: function }, - windowBits: number, [15] - dictionary: [""] - ) -*/ -static int lzlib_inflate(lua_State *L) -{ - int windowBits; - lz_stream *s; - int have_peek = 0; - const char *dictionary; - size_t dictionary_len; - - if (lua_istable(L, 1) || lua_isuserdata(L, 1)) { - /* is there a :read function? */ - lua_getfield(L, 1, "read"); - if (!lua_isfunction(L, -1)) { - luaL_argerror(L, 1, "input parameter does not provide :read function"); - } - lua_pop(L, 1); - /* check for peek function */ - lua_getfield(L, 1, "peek"); - have_peek = lua_isfunction(L, -1); - lua_pop(L, 1); - } - else if (!lua_isstring(L, 1) && !lua_isfunction(L, 1)) { - luaL_argerror(L, 1, "input parameter must be a string, function, table or userdata value"); - } - - windowBits = (int) luaL_optinteger(L, 2, 15); - dictionary = luaL_optlstring(L, 3, NULL, &dictionary_len); - - s = lzstream_new(L, 1); - - if (windowBits > 0 && windowBits < 16) { - windowBits |= 32; - } - - if (inflateInit2(&s->zstream, windowBits) != Z_OK) { - lua_pushliteral(L, "call to inflateInit2 failed"); - lua_error(L); - } - - if (dictionary) { - s->dictionary = (const Bytef *) dictionary; - s->dictionary_len = dictionary_len; - } - - s->peek = have_peek; - s->state = LZ_INFLATE; - return 1; -} - -/* ====================================================================== */ - -static int lz_pushresult (lua_State *L, lz_stream *s) { - if (s->error == Z_OK) { - lua_pushboolean(L, 1); - return 1; - } else { - lua_pushnil(L); - lua_pushstring(L, zError(s->error)); - lua_pushinteger(L, s->error); - return 3; - } -} - -/* - Get block to process: - - top of stack gets -*/ -static const char* lzstream_fetch_block(lua_State *L, lz_stream *s, int hint) { - if (s->i_buffer_pos >= s->i_buffer_len) { - luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref); - s->i_buffer_ref = LUA_NOREF; - s->i_buffer = NULL; - - lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb); - if (!lua_isnil(L, -1)) { - if (lua_isfunction(L, -1)) { - lua_pushinteger(L, hint); - lua_call(L, 1, 1); - } else { - lua_getfield(L, -1, (s->peek ? "peek" : "read")); - lua_insert(L, -2); - lua_pushinteger(L, hint); - lua_call(L, 2, 1); - } - - if (lua_isstring(L, -1)) { - s->i_buffer_pos = 0; - s->i_buffer = lua_tolstring(L, -1, &s->i_buffer_len); - if (s->i_buffer_len > 0) { - s->i_buffer_ref = luaL_ref(L, LUA_REGISTRYINDEX); - } else { - lua_pop(L, 1); - } - } else if (lua_isnil(L, -1)) { - lua_pop(L, 1); - } else { - lua_pushliteral(L, "deflate callback must return string or nil"); - lua_error(L); - } - } else { - lua_pop(L, 1); - } - } - - return s->i_buffer; -} - -static int lzstream_inflate_block(lua_State *L, lz_stream *s) { - if (lzstream_fetch_block(L, s, LZ_BUFFER_SIZE) || !s->eos) { - int r; - - if (s->i_buffer_len == s->i_buffer_pos) { - s->zstream.next_in = NULL; - s->zstream.avail_in = 0; - } else { - s->zstream.next_in = (unsigned char*)(s->i_buffer + s->i_buffer_pos); - s->zstream.avail_in = s->i_buffer_len - s->i_buffer_pos; - } - - s->zstream.next_out = (unsigned char*)s->o_buffer + s->o_buffer_len; - s->zstream.avail_out = s->o_buffer_max - s->o_buffer_len; - - /* munch some more */ - r = inflate(&s->zstream, Z_SYNC_FLUSH); - - if (r == Z_NEED_DICT) { - if (s->dictionary == NULL) { - lua_pushliteral(L, "no inflate dictionary provided"); - lua_error(L); - } - - if (inflateSetDictionary(&s->zstream, s->dictionary, s->dictionary_len) != Z_OK) { - lua_pushliteral(L, "call to inflateSetDictionnary failed"); - lua_error(L); - } - - r = inflate(&s->zstream, Z_SYNC_FLUSH); - } - - if (r != Z_OK && r != Z_STREAM_END && r != Z_BUF_ERROR) { - lzstream_cleanup(L, s); - s->error = r; - #if 1 - lua_pushfstring(L, "failed to decompress [%d]", r); - lua_error(L); - #endif - } - - if (r == Z_STREAM_END) { - luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref); - s->i_buffer_ref = LUA_NOREF; - s->i_buffer = NULL; - - s->eos = 1; - } - - /* number of processed bytes */ - if (s->peek) { - size_t processed = s->i_buffer_len - s->i_buffer_pos - s->zstream.avail_in; - - lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb); - lua_getfield(L, -1, "read"); - lua_insert(L, -2); - lua_pushinteger(L, processed); - lua_call(L, 2, 0); - } - - s->i_buffer_pos = s->i_buffer_len - s->zstream.avail_in; - s->o_buffer_len = s->o_buffer_max - s->zstream.avail_out; - } - - return s->o_buffer_len; -} - -/* -** Remove n bytes from the output buffer. -*/ -static void lzstream_remove(lz_stream *s, size_t n) { - memmove(s->o_buffer, s->o_buffer + n, s->o_buffer_len - n); - s->o_buffer_len -= n; -} - -/* -** Copy at most n bytes to buffer b and remove them from the -** output stream buffer. -*/ -static int lzstream_flush_buffer(lua_State *L, lz_stream *s, size_t n, luaL_Buffer *b) { - /* check output */ - if (n > s->o_buffer_len) { - n = s->o_buffer_len; - } - - if (n > 0) { - lua_pushlstring(L, s->o_buffer, n); - luaL_addvalue(b); - - lzstream_remove(s, n); - } - - return n; -} - -/* - z:read( - {number | '*l' | '*a'}* - ) -*/ -static int lz_test_eof(lua_State *L, lz_stream *s) { - lua_pushlstring(L, NULL, 0); - if (s->o_buffer_len > 0) { - return 1; - } else if (s->eos) { - return 0; - } else { - return lzstream_inflate_block(L, s); - } -} - -static int lz_read_line(lua_State *L, lz_stream *s) { - luaL_Buffer b; - size_t l = 0, n; - - luaL_buffinit(L, &b); - - if (s->o_buffer_len > 0 || !s->eos) do { - char *p = s->o_buffer; - size_t len = s->o_buffer_len; - - /* find newline in output buffer */ - for (n = 0; n < len; ++n, ++p) { - if (*p == '\n' || *p == '\r') { - int eat_nl = *p == '\r'; - luaL_addlstring(&b, s->o_buffer, n); - lzstream_remove(s, n+1); - l += n; - - if (eat_nl && lzstream_inflate_block(L, s)) { - if (s->o_buffer_len > 0 && *s->o_buffer == '\n') { - lzstream_remove(s, 1); - } - } - - luaL_pushresult(&b); - return 1; - } - } - - if (len > 0) { - luaL_addlstring(&b, s->o_buffer, len); - lzstream_remove(s, len); - l += len; - } - } while (lzstream_inflate_block(L, s)); - - luaL_pushresult(&b); - return l > 0 || !s->eos || s->o_buffer_len > 0; -} - - -static int lz_read_chars(lua_State *L, lz_stream *s, size_t n) { - size_t len; - luaL_Buffer b; - luaL_buffinit(L, &b); - - if (s->o_buffer_len > 0 || !s->eos) do { - size_t rlen = lzstream_flush_buffer(L, s, n, &b); - n -= rlen; - } while (n > 0 && lzstream_inflate_block(L, s)); - - luaL_pushresult(&b); - lua_tolstring(L, -1, &len); - return n == 0 || len > 0; -} - -static int lzstream_decompress(lua_State *L) { - lz_stream *s = lzstream_check(L, 1, LZ_INFLATE); - int nargs = lua_gettop(L) - 1; - int success; - int n; - if (nargs == 0) { /* no arguments? */ - success = lz_read_line(L, s); - n = 3; /* to return 1 result */ - } - else { /* ensure stack space for all results and for auxlib's buffer */ - luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); - success = 1; - for (n = 2; nargs-- && success; n++) { - if (lua_type(L, n) == LUA_TNUMBER) { - size_t l = (size_t)lua_tointeger(L, n); - success = (l == 0) ? lz_test_eof(L, s) : lz_read_chars(L, s, l); - } - else { - const char *p = lua_tostring(L, n); - luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); - switch (p[1]) { - case 'l': /* line */ - success = lz_read_line(L, s); - break; - case 'a': /* file */ - lz_read_chars(L, s, ~((size_t)0)); /* read MAX_SIZE_T chars */ - success = 1; /* always success */ - break; - default: - return luaL_argerror(L, n, "invalid format"); - } - } - } - } - if (s->error != Z_OK) { - return lz_pushresult(L, s); - } - if (!success) { - lua_pop(L, 1); /* remove last result */ - lua_pushnil(L); /* push nil instead */ - } - return n - 2; -} - - -static int lzstream_readline(lua_State *L) { - lz_stream *s; - int sucess; - - s = lzstream_check(L, lua_upvalueindex(1), LZ_INFLATE); - sucess = lz_read_line(L, s); - - if (s->error != Z_OK) { - return lz_pushresult(L, s); - } - - if (sucess) { - return 1; - } else { - /* EOF */ - return 0; - } -} - -static int lzstream_lines(lua_State *L) { - lzstream_check(L, 1, LZ_INFLATE); - lua_settop(L, 1); - lua_pushcclosure(L, lzstream_readline, 1); - return 1; -} - -/* ====================================================================== */ - -static int lzstream_docompress(lua_State *L, lz_stream *s, int from, int to, int flush) { - int r, arg; - int self = 0; - size_t b_size = s->o_buffer_max; - unsigned char *b = (unsigned char *)s->o_buffer; - - /* number of processed bytes */ - lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb); - if (!lua_isfunction(L, -1)) { - self = 1; - lua_getfield(L, -1, "write"); - } - - for (arg = from; arg <= to; arg++) { - s->zstream.next_in = (unsigned char*)luaL_checklstring(L, arg, (size_t*)&s->zstream.avail_in); - - do { - s->zstream.next_out = b; - s->zstream.avail_out = b_size; - - /* bake some more */ - r = deflate(&s->zstream, flush); - if (r != Z_OK && r != Z_STREAM_END && r != Z_BUF_ERROR) { - lzstream_cleanup(L, s); - lua_pushboolean(L, 0); - lua_pushfstring(L, "failed to compress [%d]", r); - return 2; - } - - if (s->zstream.avail_out != b_size) { - /* write output */ - lua_pushvalue(L, -1); /* function */ - if (self) lua_pushvalue(L, -3); /* self */ - lua_pushlstring(L, (char*)b, b_size - s->zstream.avail_out); /* data */ - lua_call(L, (self ? 2 : 1), 0); - } - - if (r == Z_STREAM_END) { - lzstream_cleanup(L, s); - break; - } - - /* process all input */ - } while (s->zstream.avail_in > 0 || s->zstream.avail_out == 0); - } - - lua_pushboolean(L, 1); - return 1; -} - -static int lzstream_compress(lua_State *L) { - lz_stream *s = lzstream_check(L, 1, LZ_DEFLATE); - return lzstream_docompress(L, s, 2, lua_gettop(L), Z_NO_FLUSH); -} - - -/* ====================================================================== */ - -static int lzstream_flush(lua_State *L) { - static int flush_values[] = { Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH }; - static const char *const flush_opts[] = { "sync", "full", "finish" }; - - lz_stream *s = lzstream_check(L, 1, LZ_DEFLATE); - int flush = luaL_checkoption(L, 2, flush_opts[0], flush_opts); - - lua_settop(L, 0); - lua_pushliteral(L, ""); - return lzstream_docompress(L, s, 1, 1, flush_values[flush]); -} - -/* -** ========================================================================= -** zlib functions -** ========================================================================= -*/ - -static int lzlib_version(lua_State *L) -{ - lua_pushstring(L, zlibVersion()); - return 1; -} - -/* ====================================================================== */ -static int lzlib_adler32(lua_State *L) -{ - if (lua_gettop(L) == 0) - { - /* adler32 initial value */ - lua_pushnumber(L, adler32(0L, Z_NULL, 0)); - } - else - { - /* update adler32 checksum */ - size_t len; - int adler = (int) luaL_checkinteger(L, 1); - const unsigned char* buf = (unsigned char*)luaL_checklstring(L, 2, &len); - - lua_pushnumber(L, adler32(adler, buf, len)); - } - return 1; -} - -/* ====================================================================== */ -static int lzlib_crc32(lua_State *L) -{ - if (lua_gettop(L) == 0) - { - /* crc32 initial value */ - lua_pushnumber(L, crc32(0L, Z_NULL, 0)); - } - else - { - /* update crc32 checksum */ - size_t len; - int crc = (int) luaL_checkinteger(L, 1); - const unsigned char* buf = (unsigned char*)luaL_checklstring(L, 2, &len); - - lua_pushnumber(L, crc32(crc, buf, len)); - } - return 1; -} - -/* ====================================================================== */ - - -static int lzlib_compress(lua_State *L) { - size_t avail_in; - const char *next_in = luaL_checklstring(L, 1, &avail_in); - int level = (int) luaL_optinteger(L, 2, Z_DEFAULT_COMPRESSION); - int method = (int) luaL_optinteger(L, 3, Z_DEFLATED); - int windowBits = (int) luaL_optinteger(L, 4, 15); - int memLevel = (int) luaL_optinteger(L, 5, 8); - int strategy = (int) luaL_optinteger(L, 6, Z_DEFAULT_STRATEGY); - - int ret; - luaL_Buffer b; - z_stream zs; - - luaL_buffinit(L, &b); - - zs.zalloc = Z_NULL; - zs.zfree = Z_NULL; - - zs.next_out = Z_NULL; - zs.avail_out = 0; - zs.next_in = Z_NULL; - zs.avail_in = 0; - - ret = deflateInit2(&zs, level, method, windowBits, memLevel, strategy); - - if (ret != Z_OK) - { - lua_pushnil(L); - lua_pushnumber(L, ret); - return 2; - } - - zs.next_in = (unsigned char*)next_in; - zs.avail_in = avail_in; - - for(;;) - { - zs.next_out = (unsigned char*)luaL_prepbuffer(&b); - zs.avail_out = LUAL_BUFFERSIZE; - - /* munch some more */ - ret = deflate(&zs, Z_FINISH); - - /* push gathered data */ - luaL_addsize(&b, LUAL_BUFFERSIZE - zs.avail_out); - - /* done processing? */ - if (ret == Z_STREAM_END) - break; - - /* error condition? */ - if (ret != Z_OK) - break; - } - - /* cleanup */ - deflateEnd(&zs); - - luaL_pushresult(&b); - lua_pushnumber(L, ret); - return 2; -} - -/* ====================================================================== */ - -static int lzlib_decompress(lua_State *L) -{ - size_t avail_in; - const char *next_in = luaL_checklstring(L, 1, &avail_in); - int windowBits = (int) luaL_optinteger(L, 2, 15); - - int ret; - luaL_Buffer b; - z_stream zs; - - luaL_buffinit(L, &b); - - zs.zalloc = Z_NULL; - zs.zfree = Z_NULL; - - zs.next_out = Z_NULL; - zs.avail_out = 0; - zs.next_in = Z_NULL; - zs.avail_in = 0; - - ret = inflateInit2(&zs, windowBits); - - if (ret != Z_OK) { - lua_pushliteral(L, "failed to initialize zstream structures"); - lua_error(L); - } - - zs.next_in = (unsigned char*)next_in; - zs.avail_in = avail_in; - - for (;;) { - zs.next_out = (unsigned char*)luaL_prepbuffer(&b); - zs.avail_out = LUAL_BUFFERSIZE; - - /* bake some more */ - ret = inflate(&zs, Z_FINISH); - - /* push gathered data */ - luaL_addsize(&b, LUAL_BUFFERSIZE - zs.avail_out); - - /* done processing? */ - if (ret == Z_STREAM_END) - break; - - if (ret != Z_OK && ret != Z_BUF_ERROR) { - /* cleanup */ - inflateEnd(&zs); - - lua_pushliteral(L, "failed to process zlib stream"); - lua_error(L); - } - } - - /* cleanup */ - inflateEnd(&zs); - - luaL_pushresult(&b); - return 1; -} - - -/* -** ========================================================================= -** Register functions -** ========================================================================= -*/ - -#if (LUA_VERSION_NUM >= 502) - -#define luaL_register(L,n,f) luaL_setfuncs(L,f,0) - -#endif - -LUALIB_API int luaopen_zlib(lua_State *L) -{ - const luaL_Reg lzstream_meta[] = - { - {"write", lzstream_compress }, - {"read", lzstream_decompress }, - {"lines", lzstream_lines }, - {"flush", lzstream_flush }, - {"close", lzstream_close }, - - {"adler", lzstream_adler }, - - {"__tostring", lzstream_tostring }, - {"__gc", lzstream_gc }, - {NULL, NULL} - }; - - const luaL_Reg zlib[] = - { - {"version", lzlib_version }, - {"adler32", lzlib_adler32 }, - {"crc32", lzlib_crc32 }, - - {"deflate", lzlib_deflate }, - {"inflate", lzlib_inflate }, - - {"compress", lzlib_compress }, - {"decompress", lzlib_decompress }, - - {NULL, NULL} - }; - - /* ====================================================================== */ - - /* create new metatable for zlib compression structures */ - luaL_newmetatable(L, ZSTREAMMETA); - lua_pushliteral(L, "__index"); - lua_pushvalue(L, -2); /* push metatable */ - lua_rawset(L, -3); /* metatable.__index = metatable */ - - /* - ** Stack: metatable - */ - luaL_register(L, NULL, lzstream_meta); - - lua_pop(L, 1); /* remove metatable from stack */ - - /* - ** Stack: - */ - lua_newtable(L); - - lua_pushliteral (L, "_COPYRIGHT"); - lua_pushliteral (L, "Copyright (C) 2003-2010 Tiago Dionizio"); - lua_settable (L, -3); - lua_pushliteral (L, "_DESCRIPTION"); - lua_pushliteral (L, "Lua 5 interface to access zlib library functions"); - lua_settable (L, -3); - lua_pushliteral (L, "_VERSION"); - lua_pushliteral (L, "lzlib 0.4-work3"); - lua_settable (L, -3); - -#define PUSH_LITERAL(name) \ - lua_pushliteral (L, #name); \ - lua_pushinteger (L, Z_##name); \ - lua_settable (L, -3); - -#define PUSH_NUMBER(name, value) \ - lua_pushliteral (L, #name); \ - lua_pushinteger (L, value); \ - lua_settable (L, -3); - - PUSH_LITERAL(NO_COMPRESSION) - PUSH_LITERAL(BEST_SPEED) - PUSH_LITERAL(BEST_COMPRESSION) - PUSH_LITERAL(DEFAULT_COMPRESSION) - - PUSH_LITERAL(FILTERED) - PUSH_LITERAL(HUFFMAN_ONLY) - PUSH_LITERAL(RLE) - PUSH_LITERAL(FIXED) - PUSH_LITERAL(DEFAULT_STRATEGY) - - PUSH_NUMBER(MINIMUM_MEMLEVEL, 1) - PUSH_NUMBER(MAXIMUM_MEMLEVEL, 9) - PUSH_NUMBER(DEFAULT_MEMLEVEL, 8) - - PUSH_NUMBER(DEFAULT_WINDOWBITS, 15) - PUSH_NUMBER(MINIMUM_WINDOWBITS, 8) - PUSH_NUMBER(MAXIMUM_WINDOWBITS, 15) - - PUSH_NUMBER(GZIP_WINDOWBITS, 16) - PUSH_NUMBER(RAW_WINDOWBITS, -1) - - luaL_register(L, NULL, zlib); - - /* - ** Stack: zlib table - */ - return 1; -} +#include void load_lzlib(lua_State *L) { luaL_requiref(L, "ctr.fs.lzlib", luaopen_zlib, false); From 410b736ab0f829e8754cf8fc949ab65e3aa02dfd Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Sun, 4 Oct 2015 21:15:26 +0200 Subject: [PATCH 020/101] Fixed a bug in the path --- libs/lua-5.3.1/src/luaconf.h | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/libs/lua-5.3.1/src/luaconf.h b/libs/lua-5.3.1/src/luaconf.h index 9d075c6..4cc26f7 100644 --- a/libs/lua-5.3.1/src/luaconf.h +++ b/libs/lua-5.3.1/src/luaconf.h @@ -167,26 +167,7 @@ ** hierarchy or if you want to install your libraries in ** non-conventional directories. */ -#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR -#if defined(_WIN32) /* { */ -/* -** In Windows, any exclamation mark ('!') in the path is replaced by the -** path of the directory of the executable file of the current process. -*/ -#define LUA_LDIR "!\\lua\\" -#define LUA_CDIR "!\\" -#define LUA_SHRDIR "!\\..\\share\\lua\\" LUA_VDIR "\\" -#define LUA_PATH_DEFAULT \ - LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ - LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \ - LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \ - ".\\?.lua;" ".\\?\\init.lua" -#define LUA_CPATH_DEFAULT \ - LUA_CDIR"?.dll;" \ - LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \ - LUA_CDIR"loadall.dll;" ".\\?.dll" - -#else /* }{ */ +#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR "/" #define LUA_ROOT "sdmc:/" #define LUA_LDIR LUA_ROOT "3ds/ctruLua/libs/" @@ -194,13 +175,9 @@ #define LUA_PATH_DEFAULT \ LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ LUA_LDIR LUA_VDIR"?.lua;" LUA_LDIR LUA_VDIR"?/init.lua;" \ -/* LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \ - LUA_CDIR LUA_VDIR"?.lua;" LUA_CDIR LUA_VDIR"?/init.lua;" */ \ "./?.lua;" "./?/init.lua" #define LUA_CPATH_DEFAULT \ LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so" -#endif /* } */ - /* @@ LUA_DIRSEP is the directory separator (for submodules). From 71ddca2bf4784cbbfdee1b3f4ccb3d20f830e226 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Wed, 7 Oct 2015 12:08:44 +0200 Subject: [PATCH 021/101] Added the ctr.cfgu lib, to get User Configuration, Added a method in HTTPC Warning: requires the latest ctrulib to build ! --- source/cfgu.c | 311 +++++++++++++++++++++++++++++++++++++++++++++++++ source/ctr.c | 8 ++ source/httpc.c | 34 +++++- 3 files changed, 347 insertions(+), 6 deletions(-) create mode 100644 source/cfgu.c diff --git a/source/cfgu.c b/source/cfgu.c new file mode 100644 index 0000000..57829d0 --- /dev/null +++ b/source/cfgu.c @@ -0,0 +1,311 @@ +/*** +The `cfgu` module. +Used to get some user config. +@module ctr.cfgu +@usage local cfgu = require("ctr.cfgu") +*/ + +#include + +#include <3ds/types.h> +#include <3ds/services/cfgu.h> +#include <3ds/util/utf.h> + +#include +#include + +/*** +Initialize the CFGU module. +@function init +*/ +static int cfgu_init(lua_State *L) { + initCfgu(); + + return 0; +} + +/*** +Disable the CFGU module. +@function shutdown +*/ +static int cfgu_shutdown(lua_State *L) { + exitCfgu(); + + return 0; +} + +/*** +Return the console region. +@function getRegion +@treturn number region +*/ +static int cfgu_getRegion(lua_State *L) { + u8 region = 0; + + CFGU_SecureInfoGetRegion(®ion); + + lua_pushinteger(L, region); + return 1; +} + +/*** +Return the system model. +@function getModel +@treturn number model +*/ +static int cfgu_getModel(lua_State *L) { + u8 model = 0; + + CFGU_GetSystemModel(&model); + + lua_pushinteger(L, model); + return 1; +} + +/*** +Return the system language. +@function getLanguage +@treturn number language +*/ +static int cfgu_getLanguage(lua_State *L) { + u8 language = 0; + + CFGU_GetSystemLanguage(&language); + + lua_pushinteger(L, language); + return 1; +} + +/*** +Generate an unique hash from the console ID. +@function genHash +@tparam number salt 20-bits salt +@treturn number 64-bits hash +*/ +static int cfgu_genHash(lua_State *L) { + u32 salt = luaL_optinteger(L, 1, 0); + u64 hash = 0; + + CFGU_GenHashConsoleUnique(salt, &hash); + + lua_pushinteger(L, hash); + + return 1; +} + +/*** +Return the username. +@function getUsername +@treturn string username +*/ +static int cfgu_getUsername(lua_State *L) { + const u16 *block = malloc(0x1C); + + CFGU_GetConfigInfoBlk2(0x1C, 0xA0000, (u8*)block); + u8 *name = malloc(0x14); + utf16_to_utf8(name, block, 0x14); + + lua_pushlstring(L, (const char *)name, 0x14); // The username is only 0x14 characters long. + return 1; +} + +/*** +Return the user birthday. +@function getBirthday +@treturn number month +@treturn number day +*/ +static int cfgu_getBirthday(lua_State *L) { + u16 tmp = 0; + + CFGU_GetConfigInfoBlk2(0x2, 0xA0001, (u8*)&tmp); + + u8 month = tmp/256; + u8 day = tmp%256; + + lua_pushinteger(L, month); + lua_pushinteger(L, day); + return 2; +} + +// Functions + +static const struct luaL_Reg cfgu_lib[] = { + {"init", cfgu_init }, + {"shutdown", cfgu_shutdown }, + {"getRegion", cfgu_getRegion }, + {"getModel", cfgu_getModel }, + {"getLanguage", cfgu_getLanguage}, + {"genHash", cfgu_genHash }, + {"getUsername", cfgu_getUsername}, + {"getBirthday", cfgu_getBirthday}, + {NULL, NULL} +}; + +// Constants + +struct { char *name; int value; } cfgu_constants[] = { + /*** + Constant returned by `getRegion` if the console is from Japan. + It is equal to `0`. + @field REGION_JPN + */ + {"REGION_JPN", CFG_REGION_JPN}, + /*** + Constant returned by `getRegion` if the console is from USA. + It is equal to `1`. + @field REGION_USA + */ + {"REGION_USA", CFG_REGION_USA}, + /*** + Constant returned by `getRegion` if the console is from Europe. + It is equal to `2`. + @field REGION_EUR + */ + {"REGION_EUR", CFG_REGION_EUR}, + /*** + Constant returned by `getRegion` if the console is from Australia. + It is equal to `3`. + @field REGION_AUS + */ + {"REGION_AUS", CFG_REGION_AUS}, + /*** + Constant returned by `getRegion` if the console is from China. + It is equal to `4`. + @field REGION_CHN + */ + {"REGION_CHN", CFG_REGION_CHN}, + /*** + Constant returned by `getRegion` if the console is from Korea. + It is equal to `5`. + @field REGION_KOR + */ + {"REGION_KOR", CFG_REGION_KOR}, + /*** + Constant returned by `getRegion` if the console is from Taiwan. + It is equal to `6`. + @field REGION_TWN + */ + {"REGION_TWN", CFG_REGION_TWN}, + + /*** + Constant returned by `getLanguage` if the language is Japanese. + It is equal to `0`. + @field LANGUAGE_JP + */ + {"LANGUAGE_JP", CFG_LANGUAGE_JP}, + /*** + Constant returned by `getLanguage` if the language is English. + It is equal to `1`. + @field LANGUAGE_EN + */ + {"LANGUAGE_EN", CFG_LANGUAGE_EN}, + /*** + Constant returned by `getLanguage` if the language is French. + It is equal to `2`. + @field LANGUAGE_FR + */ + {"LANGUAGE_FR", CFG_LANGUAGE_FR}, + /*** + Constant returned by `getLanguage` if the language is German. + It is equal to `3`. + @field LANGUAGE_DE + */ + {"LANGUAGE_DE", CFG_LANGUAGE_DE}, + /*** + Constant returned by `getLanguage` if the language is Italian. + It is equal to `4`. + @field LANGUAGE_JP + */ + {"LANGUAGE_IT", CFG_LANGUAGE_IT}, + /*** + Constant returned by `getLanguage` if the language is Spanish. + It is equal to `5`. + @field LANGUAGE_ES + */ + {"LANGUAGE_ES", CFG_LANGUAGE_ES}, + /*** + Constant returned by `getLanguage` if the language is Chinese. + It is equal to `6`. + @field LANGUAGE_ZH + */ + {"LANGUAGE_ZH", CFG_LANGUAGE_ZH}, + /*** + Constant returned by `getLanguage` if the language is Korean. + It is equal to `7`. + @field LANGUAGE_KO + */ + {"LANGUAGE_KO", CFG_LANGUAGE_KO}, + /*** + Constant returned by `getLanguage` if the language is Dutch. + It is equal to `8`. + @field LANGUAGE_NL + */ + {"LANGUAGE_NL", CFG_LANGUAGE_NL}, + /*** + Constant returned by `getLanguage` if the language is Portuguese. + It is equal to `9`. + @field LANGUAGE_PT + */ + {"LANGUAGE_PT", CFG_LANGUAGE_PT}, + /*** + Constant returned by `getLanguage` if the language is Russian. + It is equal to `10`. + @field LANGUAGE_RU + */ + {"LANGUAGE_RU", CFG_LANGUAGE_RU}, + /*** + Constant returned by `getLanguage` if the language is Taiwanese. + It is equal to `11`. + @field LANGUAGE_TW + */ + {"LANGUAGE_TW", CFG_LANGUAGE_TW}, + + /*** + Constant returned by `getModel` if the console is a 3DS. + It is equal to `0`. + @field MODEL_3DS + */ + {"MODEL_3DS", 0}, + /*** + Constant returned by `getModel` if the console is a 3DS XL. + It is equal to `1`. + @field MODEL_3DSXL + */ + {"MODEL_3DSXL", 1}, + /*** + Constant returned by `getModel` if the console is a New 3DS. + It is equal to `2`. + @field MODEL_3DSXL + */ + {"MODEL_N3DS", 2}, + /*** + Constant returned by `getModel` if the console is a 2DS. + It is equal to `3`. + @field MODEL_2DS + */ + {"MODEL_2DS", 3}, + /*** + Constant returned by `getModel` if the console is a New 3DS XL. + It is equal to `4`. + @field MODEL_N3DSXL + */ + {"MOdEL_N3DSXL", 4}, + + {NULL, 0} +}; + +int luaopen_cfgu_lib(lua_State *L) { + luaL_newlib(L, cfgu_lib); + + for (int i = 0; cfgu_constants[i].name; i++) { + lua_pushinteger(L, cfgu_constants[i].value); + lua_setfield(L, -2, cfgu_constants[i].name); + } + + return 1; +} + +void load_cfgu_lib(lua_State *L) { + luaL_requiref(L, "ctr.cfgu", luaopen_cfgu_lib, false); +} diff --git a/source/ctr.c b/source/ctr.c index 37e2c19..2359bcb 100644 --- a/source/ctr.c +++ b/source/ctr.c @@ -70,6 +70,13 @@ The `ctr.qtm` module. */ void load_qtm_lib(lua_State *L); +/*** +The `ctr.cfgu` module. +@table cfgu +@see ctr.cfgu +*/ +void load_cfgu_lib(lua_State *L); + //void load_cam_lib(lua_State *L); /*** @@ -111,6 +118,7 @@ struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } { "fs", load_fs_lib, unload_fs_lib }, { "httpc", load_httpc_lib, unload_httpc_lib }, { "qtm", load_qtm_lib, NULL }, + { "cfgu", load_cfgu_lib, NULL }, // { "cam", load_cam_lib, NULL }, { NULL, NULL } }; diff --git a/source/httpc.c b/source/httpc.c index 756c069..e104e18 100644 --- a/source/httpc.c +++ b/source/httpc.c @@ -58,6 +58,27 @@ static int httpc_open(lua_State *L) { return 1; } +/*** +Add a field in the request header. +@function :addRequestHeaderField +@tparam string name Name of the field +@tparam string value Value of the field +*/ +static int httpc_addRequestHeaderField(lua_State *L) { + httpcContext *context = lua_touserdata(L, 1); + char *name = (char*)luaL_checkstring(L, 2); + char *value = (char*)luaL_checkstring(L, 3); + + Result ret = httpcAddRequestHeaderField(context, name ,value); + if (ret != 0) { + lua_pushnil(L); + lua_pushinteger(L, ret); + return 2; + } + lua_pushboolean(L, true); + return 1; +} + /*** Begin a request to get the content at the URL. @function :beginRequest @@ -156,12 +177,13 @@ static int httpc_close(lua_State *L) { // object static const struct luaL_Reg httpc_methods[] = { - {"open", httpc_open}, - {"beginRequest", httpc_beginRequest}, - {"getStatusCode", httpc_getStatusCode}, - {"getDownloadSize", httpc_getDownloadSize}, - {"downloadData", httpc_downloadData}, - {"close", httpc_close}, + {"open", httpc_open }, + {"addRequestHeaderField", httpc_addRequestHeaderField}, + {"beginRequest", httpc_beginRequest }, + {"getStatusCode", httpc_getStatusCode }, + {"getDownloadSize", httpc_getDownloadSize }, + {"downloadData", httpc_downloadData }, + {"close", httpc_close }, {NULL, NULL} }; From c5337a5b2ec12036806a1e07fe80df0f8cc6abe7 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Wed, 7 Oct 2015 20:49:43 +0200 Subject: [PATCH 022/101] Added map:setSpace(), to set the space between the drawn tiles. --- source/map.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/source/map.c b/source/map.c index 04e8552..e0b2f78 100644 --- a/source/map.c +++ b/source/map.c @@ -24,6 +24,8 @@ typedef struct { u16 *data; int width; int height; + int spaceX; // in pixels + int spaceY; // in pixels } map_userdata; void getTilePos(map_userdata *map, u16 tile, int *texX, int *texY) { @@ -62,6 +64,8 @@ static int map_load(lua_State *L) { map->tileSizeY = tileSizeY; map->tilesetSizeX = (map->texture->texture->width/tileSizeX); map->tilesetSizeY = (map->texture->texture->height/tileSizeY); + map->spaceX = 0; + map->spaceY = 0; // read the map file FILE *mapFile = fopen(mapPath, "r"); @@ -132,7 +136,7 @@ static int map_draw(lua_State *L) { for (int yp=0; ypheight; yp++) { u16 tile = getTile(map, xp, yp); getTilePos(map, tile, &texX, &texY); - sf2d_draw_texture_part(map->texture->texture, (x+(map->tileSizeX*xp)), (y+(map->tileSizeY*yp)), texX, texY, map->tileSizeX, map->tileSizeY); + sf2d_draw_texture_part(map->texture->texture, (x+(map->tileSizeX*xp)+(xp*map->spaceX)), (y+(map->tileSizeY*yp)+(yp*map->spaceY)), texX, texY, map->tileSizeX, map->tileSizeY); } } } else { @@ -140,7 +144,7 @@ static int map_draw(lua_State *L) { for (int yp=0; ypheight; yp++) { u16 tile = getTile(map, xp, yp); getTilePos(map, tile, &texX, &texY); - sf2d_draw_texture_part_blend(map->texture->texture, (x+(map->tileSizeX*xp)), (y+(map->tileSizeY*yp)), texX, texY, map->tileSizeX, map->tileSizeY, map->texture->blendColor); + sf2d_draw_texture_part_blend(map->texture->texture, (x+(map->tileSizeX*xp)+(xp*map->spaceX)), (y+(map->tileSizeY*yp)+(yp*map->spaceY)), texX, texY, map->tileSizeX, map->tileSizeY, map->texture->blendColor); } } } @@ -212,6 +216,23 @@ static int map_setTile(lua_State *L) { return 0; } +/*** +Set the space between draw tiles (in pixels). +@function :setSpace +@tparam number x X space +@tparam number y Y space +*/ +static int map_setSpace(lua_State *L) { + map_userdata *map = luaL_checkudata(L, 1, "LMap"); + int x = luaL_optinteger(L, 2, map->spaceX); + int y = luaL_optinteger(L, 3, map->spaceY); + + map->spaceX = x; + map->spaceY = y; + + return 0; +} + // object static const struct luaL_Reg map_methods[] = { {"draw", map_draw }, @@ -219,6 +240,7 @@ static const struct luaL_Reg map_methods[] = { {"getSize", map_getSize }, {"getTile", map_getTile }, {"setTile", map_setTile }, + {"setSpace", map_setSpace }, {"__gc", map_unload }, {NULL, NULL} }; From dcdeec6525736d144b7c76fc61a936a7f76a20ba Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Fri, 9 Oct 2015 00:02:23 +0200 Subject: [PATCH 023/101] Updated the sftdlib, Added some functions in gfx and gfx.texture --- libs/sftdlib/libsftd/include/sftd.h | 44 ++++ libs/sftdlib/libsftd/source/sftd.c | 328 ++++++++++++++++++++++++++-- libs/sftdlib/sample/source/main.c | 19 +- source/gfx.c | 111 +++++++++- source/texture.c | 30 +++ 5 files changed, 484 insertions(+), 48 deletions(-) diff --git a/libs/sftdlib/libsftd/include/sftd.h b/libs/sftdlib/libsftd/include/sftd.h index 54fe73a..9e3facc 100644 --- a/libs/sftdlib/libsftd/include/sftd.h +++ b/libs/sftdlib/libsftd/include/sftd.h @@ -105,6 +105,50 @@ void sftd_draw_wtext(sftd_font *font, int x, int y, unsigned int color, unsigned */ void sftd_draw_wtextf(sftd_font *font, int x, int y, unsigned int color, unsigned int size, const wchar_t *text, ...); +/** + * @brief Returns the width of the given text in pixels + * @param font the font used to calculate the width + * @param size the font size + * @param text a pointer to the text that will be used to calculate the length + */ +int sftd_get_text_width(sftd_font *font, unsigned int size, const char *text); + +/** + * @brief Draws text using a font. The text will wrap after the pixels specified in lineWidth. + * @param font the font to use + * @param x the x coordinate to draw the text to + * @param y the y coordinate to draw the text to + * @param color the color to draw the font + * @param size the font size + * @param lineWidth The length of one line before a line break accours. + * @param text a pointer to the text to draw + */ +void sftd_draw_text_wrap(sftd_font *font, int x, int y, unsigned int color, unsigned int size, unsigned int lineWidth, const char *text); + +/** + * @brief Calculates the bounding box of the text wih the given attributes. + * @param boundingWidth Pointer to the address where the width will be stored. + * @param boundingHeight Pointer to the address where the height will be stored. + * @param font the font to use + * @param size the font size + * @param lineWidth The length of one line before a line break accours. + * @param text a pointer to the text to draw + */ +void sftd_calc_bounding_box(int *boundingWidth, int *boundingHeight, sftd_font *font, unsigned int size, unsigned int lineWidth, const char *text); + +/** + * @brief Draws formatted text using a font. The text will wrap after the pixels specified in lineWidth. + * @param font the font to use + * @param x the x coordinate to draw the text to + * @param y the y coordinate to draw the text to + * @param color the color to draw the font + * @param size the font size + * @param lineWidth The length of one line before a line break accours. + * @param text a pointer to the text to draw + * @param ... variable arguments + */ +void sftd_draw_textf_wrap(sftd_font *font, int x, int y, unsigned int color, unsigned int size, unsigned int lineWidth, const char *text, ...); + // (ctruLua addition) Based on sftd_draw_wtext, returns the width of the text drawn. int sftd_width_wtext(sftd_font *font, unsigned int size, const wchar_t *text); diff --git a/libs/sftdlib/libsftd/source/sftd.c b/libs/sftdlib/libsftd/source/sftd.c index 5b98af4..f8f2b4f 100644 --- a/libs/sftdlib/libsftd/source/sftd.c +++ b/libs/sftdlib/libsftd/source/sftd.c @@ -4,6 +4,7 @@ #include #include #include +#include #include FT_CACHE_H #include FT_FREETYPE_H @@ -28,6 +29,7 @@ struct sftd_font { unsigned int buffer_size; }; }; + FTC_Manager ftcmanager; FTC_CMapCache cmapcache; FTC_ImageCache imagecache; texture_atlas *tex_atlas; @@ -65,20 +67,6 @@ int sftd_init() return 0; } - error = FTC_Manager_New( - ftlibrary, - 0, /* use default */ - 0, /* use default */ - 0, /* use default */ - &ftc_face_requester, /* use our requester */ - NULL, /* user data */ - &ftcmanager); - - if (error != FT_Err_Ok) { - FT_Done_FreeType(ftlibrary); - return 0; - } - sftd_initialized = 1; return 1; } @@ -92,15 +80,17 @@ int sftd_fini() return 0; } - FTC_Manager_Done(ftcmanager); - sftd_initialized = 0; return 1; } sftd_font *sftd_load_font_file(const char *filename) { + FT_Error error; + sftd_font *font = malloc(sizeof(*font)); + if (!font) + return NULL; size_t len = strlen(filename); @@ -108,8 +98,23 @@ sftd_font *sftd_load_font_file(const char *filename) strcpy(font->filename, filename); font->filename[len] = '\0'; - FTC_CMapCache_New(ftcmanager, &font->cmapcache); - FTC_ImageCache_New(ftcmanager, &font->imagecache); + error = FTC_Manager_New( + ftlibrary, + 0, /* use default */ + 0, /* use default */ + 0, /* use default */ + &ftc_face_requester, /* use our requester */ + NULL, /* user data */ + &font->ftcmanager); + + if (error != FT_Err_Ok) { + free(font->filename); + free(font); + return NULL; + } + + FTC_CMapCache_New(font->ftcmanager, &font->cmapcache); + FTC_ImageCache_New(font->ftcmanager, &font->imagecache); font->from = SFTD_LOAD_FROM_FILE; font->tex_atlas = texture_atlas_create(ATLAS_DEFAULT_W, ATLAS_DEFAULT_H, @@ -120,12 +125,31 @@ sftd_font *sftd_load_font_file(const char *filename) sftd_font *sftd_load_font_mem(const void *buffer, unsigned int size) { + FT_Error error; + sftd_font *font = malloc(sizeof(*font)); + if (!font) + return NULL; + font->font_buffer = buffer; font->buffer_size = size; - FTC_CMapCache_New(ftcmanager, &font->cmapcache); - FTC_ImageCache_New(ftcmanager, &font->imagecache); + error = FTC_Manager_New( + ftlibrary, + 0, /* use default */ + 0, /* use default */ + 0, /* use default */ + &ftc_face_requester, /* use our requester */ + NULL, /* user data */ + &font->ftcmanager); + + if (error != FT_Err_Ok) { + free(font); + return NULL; + } + + FTC_CMapCache_New(font->ftcmanager, &font->cmapcache); + FTC_ImageCache_New(font->ftcmanager, &font->imagecache); font->from = SFTD_LOAD_FROM_MEM; font->tex_atlas = texture_atlas_create(ATLAS_DEFAULT_W, ATLAS_DEFAULT_H, @@ -138,7 +162,8 @@ void sftd_free_font(sftd_font *font) { if (font) { FTC_FaceID face_id = (FTC_FaceID)font; - FTC_Manager_RemoveFaceID(ftcmanager, face_id); + FTC_Manager_RemoveFaceID(font->ftcmanager, face_id); + FTC_Manager_Done(font->ftcmanager); if (font->from == SFTD_LOAD_FROM_FILE) { free(font->filename); } @@ -183,7 +208,7 @@ void sftd_draw_text(sftd_font *font, int x, int y, unsigned int color, unsigned { FTC_FaceID face_id = (FTC_FaceID)font; FT_Face face; - FTC_Manager_LookupFace(ftcmanager, face_id, &face); + FTC_Manager_LookupFace(font->ftcmanager, face_id, &face); FT_Int charmap_index; charmap_index = FT_Get_Charmap_Index(face->charmap); @@ -245,6 +270,7 @@ void sftd_draw_text(sftd_font *font, int x, int y, unsigned int color, unsigned text++; } } + void sftd_draw_textf(sftd_font *font, int x, int y, unsigned int color, unsigned int size, const char *text, ...) { char buffer[256]; @@ -259,7 +285,7 @@ void sftd_draw_wtext(sftd_font *font, int x, int y, unsigned int color, unsigned { FTC_FaceID face_id = (FTC_FaceID)font; FT_Face face; - FTC_Manager_LookupFace(ftcmanager, face_id, &face); + FTC_Manager_LookupFace(font->ftcmanager, face_id, &face); FT_Int charmap_index; charmap_index = FT_Get_Charmap_Index(face->charmap); @@ -332,6 +358,260 @@ void sftd_draw_wtextf(sftd_font *font, int x, int y, unsigned int color, unsigne va_end(args); } +int sftd_get_text_width(sftd_font *font, unsigned int size, const char *text) +{ + FTC_FaceID face_id = (FTC_FaceID)font; + FT_Face face; + FTC_Manager_LookupFace(font->ftcmanager, face_id, &face); + + FT_Int charmap_index; + charmap_index = FT_Get_Charmap_Index(face->charmap); + + FT_Glyph glyph; + FT_Bool use_kerning = FT_HAS_KERNING(face); + FT_UInt glyph_index, previous = 0; + int pen_x = 0; + int pen_y = size; + + FTC_ScalerRec scaler; + scaler.face_id = face_id; + scaler.width = size; + scaler.height = size; + scaler.pixel = 1; + + FT_ULong flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL; + + while (*text) { + glyph_index = FTC_CMapCache_Lookup(font->cmapcache, (FTC_FaceID)font, charmap_index, *text); + + if (use_kerning && previous && glyph_index) { + FT_Vector delta; + FT_Get_Kerning(face, previous, glyph_index, FT_KERNING_DEFAULT, &delta); + pen_x += delta.x >> 6; + } + + if (!texture_atlas_exists(font->tex_atlas, glyph_index)) { + FTC_ImageCache_LookupScaler(font->imagecache, &scaler, flags, glyph_index, &glyph, NULL); + + if (!atlas_add_glyph(font->tex_atlas, glyph_index, (FT_BitmapGlyph)glyph, size)) { + continue; + } + } + + bp2d_rectangle rect; + int bitmap_left, bitmap_top; + int advance_x, advance_y; + int glyph_size; + + texture_atlas_get(font->tex_atlas, glyph_index, + &rect, &bitmap_left, &bitmap_top, + &advance_x, &advance_y, &glyph_size); + + const float draw_scale = size/(float)glyph_size; + + pen_x += (advance_x >> 16) * draw_scale; + pen_y += (advance_y >> 16) * draw_scale; + + previous = glyph_index; + text++; + } + return pen_x; +} + +void sftd_draw_text_wrap(sftd_font *font, int x, int y, unsigned int color, unsigned int size, unsigned int lineWidth, const char *text) +{ + FTC_FaceID face_id = (FTC_FaceID)font; + FT_Face face; + FTC_Manager_LookupFace(font->ftcmanager, face_id, &face); + + FT_Int charmap_index; + charmap_index = FT_Get_Charmap_Index(face->charmap); + + FT_Glyph glyph; + FT_Bool use_kerning = FT_HAS_KERNING(face); + FT_UInt glyph_index, previous = 0; + int pen_x = x; + int pen_y = y + size; + + FTC_ScalerRec scaler; + scaler.face_id = face_id; + scaler.width = size; + scaler.height = size; + scaler.pixel = 1; + + FT_ULong flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL; + + bool isFirstLine = true; + char buffer[strlen(text)]; + sprintf(buffer, text); + char *currentWord; + int currentWordLength; + int currentCharIndex; + + currentWord = strtok(buffer, " "); + while (currentWord) { + currentWordLength = strlen(currentWord); + if(pen_x + sftd_get_text_width(font, size, currentWord) >= lineWidth && !isFirstLine) { + pen_x = x; + pen_y += size; + } + isFirstLine = false; + for(currentCharIndex = 0; currentCharIndex < currentWordLength + 1; currentCharIndex++) { + if(currentCharIndex < currentWordLength) { + glyph_index = FTC_CMapCache_Lookup(font->cmapcache, (FTC_FaceID)font, charmap_index, currentWord[currentCharIndex]); + } + else { + glyph_index = FTC_CMapCache_Lookup(font->cmapcache, (FTC_FaceID)font, charmap_index, ' '); + } + + // TODO get word size and linewrap if needed + + if (use_kerning && previous && glyph_index) { + FT_Vector delta; + FT_Get_Kerning(face, previous, glyph_index, FT_KERNING_DEFAULT, &delta); + pen_x += delta.x >> 6; + } + + if (!texture_atlas_exists(font->tex_atlas, glyph_index)) { + FTC_ImageCache_LookupScaler(font->imagecache, &scaler, flags, glyph_index, &glyph, NULL); + + if (!atlas_add_glyph(font->tex_atlas, glyph_index, (FT_BitmapGlyph)glyph, size)) { + continue; + } + } + + bp2d_rectangle rect; + int bitmap_left, bitmap_top; + int advance_x, advance_y; + int glyph_size; + + texture_atlas_get(font->tex_atlas, glyph_index, + &rect, &bitmap_left, &bitmap_top, + &advance_x, &advance_y, &glyph_size); + + const float draw_scale = size/(float)glyph_size; + + sf2d_draw_texture_part_scale_blend(font->tex_atlas->tex, + pen_x + bitmap_left * draw_scale, + pen_y - bitmap_top * draw_scale, + rect.x, rect.y, rect.w, rect.h, + draw_scale, + draw_scale, + color); + + pen_x += (advance_x >> 16) * draw_scale; + pen_y += (advance_y >> 16) * draw_scale; + + + previous = glyph_index; + + + } + currentWord = strtok(NULL, " "); + } +} + +void sftd_calc_bounding_box(int *boundingWidth, int *boundingHeight, sftd_font *font, unsigned int size, unsigned int lineWidth, const char *text) +{ + FTC_FaceID face_id = (FTC_FaceID)font; + FT_Face face; + FTC_Manager_LookupFace(font->ftcmanager, face_id, &face); + + FT_Int charmap_index; + charmap_index = FT_Get_Charmap_Index(face->charmap); + + FT_Glyph glyph; + FT_Bool use_kerning = FT_HAS_KERNING(face); + FT_UInt glyph_index, previous = 0; + int pen_x = 0; + int pen_y = size; + + FTC_ScalerRec scaler; + scaler.face_id = face_id; + scaler.width = size; + scaler.height = size; + scaler.pixel = 1; + + FT_ULong flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL; + + bool isFirstLine = true; + char buffer[strlen(text)]; + sprintf(buffer, text); + char *currentWord; + int currentWordLength; + int currentCharIndex; + int greatesLineWidth = 0; + + currentWord = strtok(buffer, " "); + while (currentWord) { + currentWordLength = strlen(currentWord); + if(pen_x + sftd_get_text_width(font, size, currentWord) >= lineWidth && !isFirstLine) { + if(pen_x > greatesLineWidth) { + greatesLineWidth = pen_x; + } + pen_x = 0; + pen_y += size; + } + isFirstLine = false; + for(currentCharIndex = 0; currentCharIndex < currentWordLength + 1; currentCharIndex++) { + if(currentCharIndex < currentWordLength) { + glyph_index = FTC_CMapCache_Lookup(font->cmapcache, (FTC_FaceID)font, charmap_index, currentWord[currentCharIndex]); + } + else { + glyph_index = FTC_CMapCache_Lookup(font->cmapcache, (FTC_FaceID)font, charmap_index, ' '); + } + + // TODO get word size and linewrap if needed + + if (use_kerning && previous && glyph_index) { + FT_Vector delta; + FT_Get_Kerning(face, previous, glyph_index, FT_KERNING_DEFAULT, &delta); + pen_x += delta.x >> 6; + } + + if (!texture_atlas_exists(font->tex_atlas, glyph_index)) { + FTC_ImageCache_LookupScaler(font->imagecache, &scaler, flags, glyph_index, &glyph, NULL); + + if (!atlas_add_glyph(font->tex_atlas, glyph_index, (FT_BitmapGlyph)glyph, size)) { + continue; + } + } + + bp2d_rectangle rect; + int bitmap_left, bitmap_top; + int advance_x, advance_y; + int glyph_size; + + texture_atlas_get(font->tex_atlas, glyph_index, + &rect, &bitmap_left, &bitmap_top, + &advance_x, &advance_y, &glyph_size); + + const float draw_scale = size/(float)glyph_size; + + pen_x += (advance_x >> 16) * draw_scale; + pen_y += (advance_y >> 16) * draw_scale; + + + previous = glyph_index; + + + } + currentWord = strtok(NULL, " "); + } + *boundingWidth = greatesLineWidth; + *boundingHeight = pen_y; +} + +void sftd_draw_textf_wrap(sftd_font *font, int x, int y, unsigned int color, unsigned int size, unsigned int lineWidth, const char *text, ...) +{ + char buffer[256]; + va_list args; + va_start(args, text); + vsnprintf(buffer, 256, text, args); + sftd_draw_text_wrap(font, x, y, color, size, lineWidth, buffer); + va_end(args); +} + // (ctruLua addition) Based on sftd_draw_wtext, returns the width of the text drawn. int sftd_width_wtext(sftd_font *font, unsigned int size, const wchar_t *text) { @@ -390,4 +670,4 @@ int sftd_width_wtext(sftd_font *font, unsigned int size, const wchar_t *text) } return pen_x; -} \ No newline at end of file +} diff --git a/libs/sftdlib/sample/source/main.c b/libs/sftdlib/sample/source/main.c index c402119..6558a8e 100644 --- a/libs/sftdlib/sample/source/main.c +++ b/libs/sftdlib/sample/source/main.c @@ -16,6 +16,10 @@ int main() sftd_init(); sftd_font *font = sftd_load_font_mem(FreeSans_ttf, FreeSans_ttf_size); + const char *someText = "Font drawing on the top screen! Text wraps after 300 pixels... Lorem ipsum dolor sit amet, consetetur sadipscing elit."; + int textWidth = 0; + int textHeight = 0; + while (aptMainLoop()) { hidScanInput(); @@ -24,18 +28,9 @@ int main() sf2d_start_frame(GFX_TOP, GFX_LEFT); sftd_draw_textf(font, 10, 10, RGBA8(0, 255, 0, 255), 20, "FPS %f", sf2d_get_fps()); - - sftd_draw_text(font, 10, 30, RGBA8(255, 0, 0, 255), 20, "Font drawing on the top screen!"); - sftd_draw_text(font, 10, 50, RGBA8(0, 255, 0, 255), 15, "Font drawing on the top screen!"); - sftd_draw_text(font, 10, 68, RGBA8(0, 0, 255, 255), 25, "Font drawing on the top screen!"); - sftd_draw_text(font, 10, 90, RGBA8(255, 255, 0, 255), 10, "Font drawing on the top screen!"); - sftd_draw_text(font, 10, 105, RGBA8(255, 0, 255, 255), 8, "Font drawing on the top screen!"); - sftd_draw_text(font, 10, 120, RGBA8(0, 255, 255, 255), 30, "Font drawing on the top screen!"); - - sftd_draw_text(font, 10, 155, RGBA8(255, 0, 0, 255), 20, "Font drawing on the top screen!"); - sftd_draw_text(font, 10, 170, RGBA8(0, 255, 0, 255), 2, "Font drawing on the top screen!"); - sftd_draw_text(font, 10, 180, RGBA8(0, 0, 255, 255), 10, "Font drawing on the top screen!"); - sftd_draw_text(font, 10, 205, RGBA8(255, 255, 0, 255), 170, "Font drawing on the top screen!"); + sftd_calc_bounding_box(&textWidth, &textHeight, font, 20, 300, someText); + sf2d_draw_rectangle(10, 40, textWidth, textHeight, RGBA8(0, 100, 0, 255)); + sftd_draw_text_wrap(font, 10, 40, RGBA8(255, 255, 255, 255), 20, 300, someText); sf2d_end_frame(); diff --git a/source/gfx.c b/source/gfx.c index be1310a..821fe02 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -121,6 +121,19 @@ static int gfx_get3D(lua_State *L) { return 1; } +/*** +Enable or disable the VBlank waiting. +@function setVBlankWait +@tparam boolean true to enable, false to disable +*/ +static int gfx_setVBlankWait(lua_State *L) { + bool enable = lua_toboolean(L, 1); + + sf2d_set_vblank_wait(enable); + + return 0; +} + /*** Get free VRAM space. @function vramSpaceFree @@ -255,20 +268,94 @@ static int gfx_text(lua_State *L) { return 0; } +/*** +Draw a text with a new line after a certain number of pixels. +Warning: No UTF32 support. +@function wrappedText +@tparam integer x text drawing origin horizontal coordinate, in pixels +@tparam integer y text drawing origin vertical coordinate, in pixels +@tparam string text the text to draw +@tparam integer width width of a line, in pixels +@tparam[opt=9] integer size drawing size, in pixels +@tparam[opt=default color] integer color drawing color +@tparam[opt=default font] font font to use +*/ +static int gfx_wrappedText(lua_State *L) { + int x = luaL_checkinteger(L, 1); + int y = luaL_checkinteger(L, 2); + size_t len; + const char *text = luaL_checklstring(L, 3, &len); + unsigned int lineWidth = luaL_checkinteger(L, 4); + + int size = luaL_optinteger(L, 5, 9); + u32 color = luaL_optinteger(L, 6, color_default); + font_userdata *font = luaL_testudata(L, 7, "LFont"); + if (font == NULL) { + lua_getfield(L, LUA_REGISTRYINDEX, "LFontDefault"); + font = luaL_testudata(L, -1, "LFont"); + if (font == NULL) luaL_error(L, "No default font set and no font object passed"); + } + if (font->font == NULL) luaL_error(L, "The font object was unloaded"); + + // Wide caracters support. (wchar = UTF32 on 3DS.) + // Disabled as sftd_draw_wtext_wrap() doesn't exist. + /*wchar_t wtext[len]; + len = mbstowcs(wtext, text, len); + *(wtext+len) = 0x0; // text end */ + + sftd_draw_text_wrap(font->font, x, y, color, size, lineWidth, text); + + return 0; +} + +/*** +Calculate the size of a text draw with `wrappedText`. +@function calcBoundingBox +@tparam string text The text to check +@tparam integer lineWidth width of a line, in pixels +@tparam[opt=9] integer size drawing size, in pixels +@tparam[opt=default font] font font to use +@treturn integer width of the text, in pixels +@treturn integer height of the text, in pixels +*/ +static int gfx_calcBoundingBox(lua_State *L) { + size_t len; + const char *text = luaL_checklstring(L, 1, &len); + unsigned int lineWidth = luaL_checkinteger(L, 2); + int size = luaL_optinteger(L, 3, 9); + font_userdata *font = luaL_testudata(L, 4, "LFont"); + if (font == NULL) { + lua_getfield(L, LUA_REGISTRYINDEX, "LFontDefault"); + font = luaL_testudata(L, -1, "LFont"); + if (font == NULL) luaL_error(L, "No default font set and no font object passed"); + } + if (font->font == NULL) luaL_error(L, "The font object was unloaded"); + + int w, h = 0; + sftd_calc_bounding_box(&w, &h, font->font, size, lineWidth, text); + + lua_pushinteger(L, w); + lua_pushinteger(L, h); + return 2; +} + // Functions static const struct luaL_Reg gfx_lib[] = { - { "startFrame", gfx_startFrame }, - { "endFrame", gfx_endFrame }, - { "render", gfx_render }, - { "getFPS", gfx_getFPS }, - { "set3D", gfx_set3D }, - { "get3D", gfx_get3D }, - { "vramSpaceFree", gfx_vramSpaceFree }, - { "line", gfx_line }, - { "point", gfx_point }, - { "rectangle", gfx_rectangle }, - { "circle", gfx_circle }, - { "text", gfx_text }, + { "startFrame", gfx_startFrame }, + { "endFrame", gfx_endFrame }, + { "render", gfx_render }, + { "getFPS", gfx_getFPS }, + { "set3D", gfx_set3D }, + { "get3D", gfx_get3D }, + { "setVBlankWait", gfx_setVBlankWait }, + { "vramSpaceFree", gfx_vramSpaceFree }, + { "line", gfx_line }, + { "point", gfx_point }, + { "rectangle", gfx_rectangle }, + { "circle", gfx_circle }, + { "text", gfx_text }, + { "wrappedText", gfx_wrappedText }, + { "calcBoundingBox", gfx_calcBoundingBox }, { NULL, NULL } }; diff --git a/source/texture.c b/source/texture.c index a9da11f..a369737 100644 --- a/source/texture.c +++ b/source/texture.c @@ -76,6 +76,35 @@ static int texture_load(lua_State *L) { return 1; } +/*** +Create an empty texture. +@function new +@tparam number width Texture width +@tparam number height Texture height +@tparam[opt=PLACE_RAM] number place where to put the loaded texture +@treturn texture the loaded texture object +*/ +static int texture_new(lua_State *L) { + int w = luaL_checkinteger(L, 1); + int h = luaL_checkinteger(L, 2); + u8 place = luaL_checkinteger(L, 3); + + texture_userdata *texture; + texture = (texture_userdata *)lua_newuserdata(L, sizeof(*texture)); + + luaL_getmetatable(L, "LTexture"); + lua_setmetatable(L, -2); + + texture->texture = sf2d_create_texture(w, h, TEXFMT_RGBA8, place); + sf2d_texture_tile32(texture->texture); + + texture->scaleX = 1.0f; + texture->scaleY = 1.0f; + texture->blendColor = 0xffffffff; + + return 1; +} + /*** Texture object @section Methods @@ -242,6 +271,7 @@ static const struct luaL_Reg texture_methods[] = { // module static const struct luaL_Reg texture_functions[] = { {"load", texture_load}, + {"new", texture_new }, {NULL, NULL} }; From 8500bcb8fa54109b4d461568f16f9f6418bc5a2d Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Sat, 17 Oct 2015 23:15:06 +0200 Subject: [PATCH 024/101] ADDED SOCKETS ! Added ROMFS support (maybe), Improved the news library, added a VBLank waiting function Sockets are only very buggy/unstable TCP clients, but working. The library was written from scratch. --- Makefile | 7 ++ source/ctr.c | 26 +++++--- source/fs.c | 10 +++ source/gfx.c | 18 ++++++ source/ir.c | 1 + source/news.c | 10 +-- source/socket.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 222 insertions(+), 16 deletions(-) create mode 100644 source/socket.c diff --git a/Makefile b/Makefile index 606550f..d7bd5ff 100644 --- a/Makefile +++ b/Makefile @@ -25,12 +25,14 @@ include $(DEVKITARM)/3ds_rules # - .png # - icon.png # - /default_icon.png +# ROMFS: if set, use the files at this path to build a ROMFS #--------------------------------------------------------------------------------- TARGET := ctruLua BUILD := build SOURCES := source libs/lua-5.3.1/src DATA := data INCLUDES := include libs/lua-5.3.1/src libs/lzlib +#ROMFS := romfs APP_TITLE := ctruLua APP_DESCRIPTION := Lua for the 3DS. Yes, it works. @@ -125,6 +127,11 @@ ifeq ($(strip $(NO_SMDH)),) export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh endif +ifneq ($(ROMFS),) + export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) + CFLAGS += -DROMFS +endif + .PHONY: $(BUILD) clean all #--------------------------------------------------------------------------------- diff --git a/source/ctr.c b/source/ctr.c index 2359bcb..b046636 100644 --- a/source/ctr.c +++ b/source/ctr.c @@ -77,6 +77,13 @@ The `ctr.cfgu` module. */ void load_cfgu_lib(lua_State *L); +/*** +The `ctr.socket` module. +@table socket +@see ctr.socket +*/ +void load_socket_lib(lua_State *L); + //void load_cam_lib(lua_State *L); /*** @@ -110,15 +117,16 @@ static const struct luaL_Reg ctr_lib[] = { // Subtables struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } ctr_libs[] = { - { "gfx", load_gfx_lib, unload_gfx_lib }, - { "news", load_news_lib, NULL }, - { "ptm", load_ptm_lib, NULL }, - { "hid", load_hid_lib, unload_hid_lib }, - { "ir", load_ir_lib, NULL }, - { "fs", load_fs_lib, unload_fs_lib }, - { "httpc", load_httpc_lib, unload_httpc_lib }, - { "qtm", load_qtm_lib, NULL }, - { "cfgu", load_cfgu_lib, NULL }, + { "gfx", load_gfx_lib, unload_gfx_lib }, + { "news", load_news_lib, NULL }, + { "ptm", load_ptm_lib, NULL }, + { "hid", load_hid_lib, unload_hid_lib }, + { "ir", load_ir_lib, NULL }, + { "fs", load_fs_lib, unload_fs_lib }, + { "httpc", load_httpc_lib, unload_httpc_lib }, + { "qtm", load_qtm_lib, NULL }, + { "cfgu", load_cfgu_lib, NULL }, + { "socket", load_socket_lib, NULL }, // { "cam", load_cam_lib, NULL }, { NULL, NULL } }; diff --git a/source/fs.c b/source/fs.c index 940ef58..576ec48 100644 --- a/source/fs.c +++ b/source/fs.c @@ -9,6 +9,9 @@ Handle *fsuHandle; FS_archive sdmcArchive; +#ifdef ROMFS +FS_archive romfsArchive; +#endif void load_lzlib(lua_State *L); @@ -126,12 +129,19 @@ void load_fs_lib(lua_State *L) { sdmcArchive = (FS_archive){ARCH_SDMC, FS_makePath(PATH_EMPTY, "")}; FSUSER_OpenArchive(fsuHandle, &sdmcArchive); + #ifdef ROMFS + romfsArchive = (FS_archive){ARCH_ROMFS, FS_makePath(PATH_EMPTY, "")}; + FSUSER_OpenArchive(fsuHandle, &romfsArchive); + #endif luaL_requiref(L, "ctr.fs", luaopen_fs_lib, false); } void unload_fs_lib(lua_State *L) { FSUSER_CloseArchive(fsuHandle, &sdmcArchive); + #ifdef ROMFS + FSUSER_CloseArchive(fsuHandle, &romfsArchive); + #endif fsExit(); } diff --git a/source/gfx.c b/source/gfx.c index 821fe02..c43be37 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -9,6 +9,7 @@ The `gfx` module. #include #include <3ds/vram.h> +#include <3ds/services/gsp.h> #include #include @@ -134,6 +135,22 @@ static int gfx_setVBlankWait(lua_State *L) { return 0; } +/*** +Wait for the VBlank interruption. +@function waitForVBlank +@tparam[opt=GFX_TOP] number screen the screen's VBlank to wait for +*/ +static int gfx_waitForVBlank(lua_State *L) { + u8 screen = luaL_optinteger(L, 1, GFX_TOP); + if (screen == GFX_TOP) { + gspWaitForVBlank0(); + } else { + gspWaitForVBlank1(); + } + + return 0; +} + /*** Get free VRAM space. @function vramSpaceFree @@ -348,6 +365,7 @@ static const struct luaL_Reg gfx_lib[] = { { "set3D", gfx_set3D }, { "get3D", gfx_get3D }, { "setVBlankWait", gfx_setVBlankWait }, + { "waitForVBlank", gfx_waitForVBlank }, { "vramSpaceFree", gfx_vramSpaceFree }, { "line", gfx_line }, { "point", gfx_point }, diff --git a/source/ir.c b/source/ir.c index c58333c..705091b 100644 --- a/source/ir.c +++ b/source/ir.c @@ -103,6 +103,7 @@ Receive some data from the IR module. @function receive @tparam[opt=buffer size] number size bytes to receive @tparam[opt=false] boolean wait wait until the data is received +@return string data */ static int ir_receive(lua_State *L) { u32 size = luaL_optinteger(L, 1, bufferSize); diff --git a/source/news.c b/source/news.c index 18932d6..20f78d8 100644 --- a/source/news.c +++ b/source/news.c @@ -33,17 +33,13 @@ Send a notification to the user. WIP, do not use !!! static int news_notification(lua_State *L) { const char *title = luaL_checkstring(L, 1); const char *message = luaL_checkstring(L, 2); - const void *imageData = luaL_optstring(L, 3, NULL); + + u32 imageDataLength = 0; + const void *imageData = luaL_optlstring(L, 3, NULL, (size_t*)&imageDataLength); bool jpeg = false; if (lua_isboolean(L, 4)) jpeg = lua_toboolean(L, 4); - u32 imageDataLength = 0; - if (imageData) { - lua_len(L, 3); - luaL_checkinteger(L, -1); - } - const u16* cTitle = 0; const u16* cMessage = 0; u32 titleLength, messageLength; diff --git a/source/socket.c b/source/socket.c new file mode 100644 index 0000000..173e0a0 --- /dev/null +++ b/source/socket.c @@ -0,0 +1,166 @@ +/*** +The `socket` module. Almost like luasocket, but for TCP/UDP only. +See http://w3.impa.br/~diego/software/luasocket/reference.html for a +documentation. +@module ctr.socket +@usage local socket = require("ctr.socket") +*/ + +#include <3ds.h> +#include <3ds/types.h> +#include <3ds/services/soc.h> + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + int socket; + struct sockaddr_in addr; + struct hostent *host; // only user for client sockets +} socket_userdata; + +static int socket_init(lua_State *L) { + u32 size = luaL_optinteger(L, 1, 0x10000); + Result ret = SOC_Initialize((u32*)memalign(0x1000, size), size); + + if (ret) { + lua_pushboolean(L, false); + lua_pushinteger(L, ret); + return 2; + } + + lua_pushboolean(L, true); + return 1; +} + +static int socket_shutdown(lua_State *L) { + SOC_Shutdown(); + + return 0; +} + +static int socket_tcp(lua_State *L) { + socket_userdata *data = lua_newuserdata(L, sizeof(*data)); + luaL_getmetatable(L, "LSocket"); + lua_setmetatable(L, -2); + + data->socket = socket(AF_INET, SOCK_STREAM, 0); + if (data->socket < 0) { + lua_pushnil(L); + lua_pushstring(L, "Failed to create a TCP socket"); + return 2; + } + + data->addr.sin_family = AF_INET; + + return 1; +} + +/* methods */ + +static int socket_close(lua_State *L) { + socket_userdata *data = luaL_checkudata(L, 1, "LSocket"); + + close(data->socket); + + return 0; +} + +static int socket_connect(lua_State *L) { + socket_userdata *data = luaL_checkudata(L, 1, "LSocket"); + char *addr = (char*)luaL_checkstring(L, 2); + int port = luaL_checkinteger(L, 3); + + data->host = gethostbyname(addr); + if (data->host == NULL) { + lua_pushnil(L); + lua_pushstring(L, "No such host"); + return 2; + } + + data->addr.sin_port = htons(port); + bcopy((char*)data->host->h_addr, (char*)&data->addr.sin_addr.s_addr, data->host->h_length); + + if (connect(data->socket, (const struct sockaddr*)&data->addr, sizeof(data->addr)) < 0) { + lua_pushnil(L); + lua_pushstring(L, "Connection failed"); + return 2; + } + + lua_pushinteger(L, 1); + return 1; +} + +static int socket_receive(lua_State *L) { + socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); + int count = 0; + int flags = 0; + if (lua_isnumber(L, 2)) { + count = luaL_checkinteger(L, 2); + } else if (lua_isstring(L, 2) && luaL_checkstring(L, 2) == (char*)&"*a") { + count = SIZE_MAX/2; + } else { + lua_pushnil(L); + lua_pushstring(L, ""); + return 2; + } + + char *buff = malloc(count); + recv(userdata->socket, buff, count, flags); + + lua_pushstring(L, buff); + return 1; +} + +static int socket_send(lua_State *L) { + socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); + size_t size = 0; + char *data = (char*)luaL_checklstring(L, 2, &size); + + size_t sent = send(userdata->socket, data, size, 0); + + lua_pushinteger(L, sent); + return 1; +} + +// module functions +static const struct luaL_Reg socket_functions[] = { + {"init", socket_init }, + {"shutdown", socket_shutdown}, + {"tcp", socket_tcp }, + {NULL, NULL} +}; + +// object +static const struct luaL_Reg socket_methods[] = { + {"close", socket_close }, + {"connect", socket_connect }, + {"receive", socket_receive }, + {"send", socket_send }, + {NULL, NULL} +}; + +int luaopen_socket_lib(lua_State *L) { + luaL_newmetatable(L, "LSocket"); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + luaL_setfuncs(L, socket_methods, 0); + + luaL_newlib(L, socket_functions); + + return 1; +} + +void load_socket_lib(lua_State *L) { + luaL_requiref(L, "ctr.socket", luaopen_socket_lib, false); +} From 4d56037289de6bfac3a252a864a9f5ae08240c65 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Sun, 18 Oct 2015 16:07:29 +0200 Subject: [PATCH 025/101] Added documentation and (untested) code for server sockets --- source/socket.c | 129 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 111 insertions(+), 18 deletions(-) diff --git a/source/socket.c b/source/socket.c index 173e0a0..d921c1c 100644 --- a/source/socket.c +++ b/source/socket.c @@ -1,7 +1,5 @@ /*** -The `socket` module. Almost like luasocket, but for TCP/UDP only. -See http://w3.impa.br/~diego/software/luasocket/reference.html for a -documentation. +The `socket` module. Almost like luasocket, but for TCP only. @module ctr.socket @usage local socket = require("ctr.socket") */ @@ -29,8 +27,13 @@ typedef struct { struct hostent *host; // only user for client sockets } socket_userdata; +/*** +Initialize the socket module +@function init +@tparam[opt=0x100000] number buffer size (in bytes), must be a multiple of 0x1000 +*/ static int socket_init(lua_State *L) { - u32 size = luaL_optinteger(L, 1, 0x10000); + u32 size = luaL_optinteger(L, 1, 0x100000); Result ret = SOC_Initialize((u32*)memalign(0x1000, size), size); if (ret) { @@ -43,55 +46,116 @@ static int socket_init(lua_State *L) { return 1; } +/*** +Disable the socket module. Must be called before exiting ctrµLua. +@function shutdown +*/ static int socket_shutdown(lua_State *L) { SOC_Shutdown(); return 0; } +/*** +Return a TCP socket. +@function tcp +@treturn TCPMaster TCP socket +*/ static int socket_tcp(lua_State *L) { - socket_userdata *data = lua_newuserdata(L, sizeof(*data)); + socket_userdata *userdata = lua_newuserdata(L, sizeof(*userdata)); luaL_getmetatable(L, "LSocket"); lua_setmetatable(L, -2); - data->socket = socket(AF_INET, SOCK_STREAM, 0); - if (data->socket < 0) { + userdata->socket = socket(AF_INET, SOCK_STREAM, 0); + if (userdata->socket < 0) { lua_pushnil(L); lua_pushstring(L, "Failed to create a TCP socket"); return 2; } - data->addr.sin_family = AF_INET; + userdata->addr.sin_family = AF_INET; return 1; } -/* methods */ +/*** +TCP Sockets +@section TCP +*/ -static int socket_close(lua_State *L) { - socket_userdata *data = luaL_checkudata(L, 1, "LSocket"); +/*** +Accept a connection on a server. +@function :accept +@treturn TCPClient tcp client object, or nil. +*/ +static int socket_accept(lua_State *L) { + socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); - close(data->socket); + socket_userdata *client = lua_newuserdata(L, sizeof(*client)); + luaL_getmetatable(L, "LSocket"); + lua_setmetatable(L, -2); + + socklen_t addrSize = sizeof(client->addr); + client->socket = accept(userdata->socket, (struct sockaddr*)&client->addr, &addrSize); + if (client->socket < 0) { + lua_pushnil(L); + return 1; + } + + return 1; +} + +/*** +Bind a socket. The TCP object become a TCPServer object. +@function :bind +@tparam number port the port to bind the socket on. +*/ +static int socket_bind(lua_State *L) { + socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); + int port = luaL_checkinteger(L, 2); + + userdata->addr.sin_addr.s_addr = htonl(INADDR_ANY); + userdata->addr.sin_port = htons(port); + + bind(userdata->socket, (struct sockaddr*)&userdata->addr, sizeof(userdata->addr)); return 0; } +/*** +Close an existing socket. +@function :close +*/ +static int socket_close(lua_State *L) { + socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); + + closesocket(userdata->socket); + + return 0; +} + +/*** +Connect a socket to a server. The TCP object becomes a TCPClient object. +@function :connect +@tparam string host address of the host +@tparam number port port of the server +*/ static int socket_connect(lua_State *L) { - socket_userdata *data = luaL_checkudata(L, 1, "LSocket"); + socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); char *addr = (char*)luaL_checkstring(L, 2); int port = luaL_checkinteger(L, 3); - data->host = gethostbyname(addr); - if (data->host == NULL) { + userdata->host = gethostbyname(addr); + if (userdata->host == NULL) { lua_pushnil(L); lua_pushstring(L, "No such host"); return 2; } - data->addr.sin_port = htons(port); - bcopy((char*)data->host->h_addr, (char*)&data->addr.sin_addr.s_addr, data->host->h_length); + userdata->addr.sin_port = htons(port); + bcopy((char*)userdata->host->h_addr, (char*)&userdata->addr.sin_addr.s_addr, userdata->host->h_length); - if (connect(data->socket, (const struct sockaddr*)&data->addr, sizeof(data->addr)) < 0) { + if (connect(userdata->socket, (const struct sockaddr*)&userdata->addr, sizeof(userdata->addr)) < 0) { lua_pushnil(L); lua_pushstring(L, "Connection failed"); return 2; @@ -101,6 +165,26 @@ static int socket_connect(lua_State *L) { return 1; } +/*** +Open the socket for connections. +@function :listen +@tparam[opt=16] number max maximum number of simultaneous connections +*/ +static int socket_listen(lua_State *L) { + socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); + int max = luaL_optinteger(L, 2, 16); + + listen(userdata->socket, max); + + return 0; +} + +/*** +Receive some data from the socket. +@function :receive +@tparam number size amount of data to receive; can also be "*a" to receive everything +@treturn string data +*/ static int socket_receive(lua_State *L) { socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); int count = 0; @@ -122,6 +206,12 @@ static int socket_receive(lua_State *L) { return 1; } +/*** +Send some data over the TCP socket. +@function :send +@tparam string data data to send +@treturn number amount of data sent +*/ static int socket_send(lua_State *L) { socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); size_t size = 0; @@ -143,8 +233,11 @@ static const struct luaL_Reg socket_functions[] = { // object static const struct luaL_Reg socket_methods[] = { + {"accept", socket_accept }, + {"bind", socket_bind }, {"close", socket_close }, {"connect", socket_connect }, + {"listen", socket_listen }, {"receive", socket_receive }, {"send", socket_send }, {NULL, NULL} From a8d31de1e4e3fca7b5005990b1d7b2944944170f Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Thu, 22 Oct 2015 16:13:14 +0200 Subject: [PATCH 026/101] Added the ctr.cam library (untested but should work) --- source/cam.c | 703 +++++++++++++++++++++++++++++++++++++++++++++++++++ source/ctr.c | 9 +- 2 files changed, 710 insertions(+), 2 deletions(-) create mode 100644 source/cam.c diff --git a/source/cam.c b/source/cam.c new file mode 100644 index 0000000..e310717 --- /dev/null +++ b/source/cam.c @@ -0,0 +1,703 @@ +/*** +The `cam` module. +@module ctr.cam +@usage local cam = require("ctr.cam") +*/ + +#include <3ds.h> +#include <3ds/types.h> +#include <3ds/svc.h> +#include <3ds/services/cam.h> + +#include + +#include +#include + +#include + +#include "texture.h" + +/*** +Initialize the camera module. +@function init +*/ +static int cam_init(lua_State *L) { + Result ret = camInit(); + if (ret) { + lua_pushboolean(L, false); + lua_pushinteger(L, ret); + return 2; + } + + lua_pushboolean(L, true); + return 1; +} + +/*** +Disable the camera module. +@function shutdown +*/ +static int cam_shutdown(lua_State *L) { + camExit(); + + return 0; +} + +/*** +Activate a camera. +@function activate +@tparam number camera camera to activate (`SELECT_x`) +*/ +static int cam_activate(lua_State *L) { + u8 cam = luaL_checkinteger(L, 1); + + CAMU_Activate(cam); + + return 0; +} + +/*** +Set the exposure of a camera. +@function setExposure +@tparam number camera (`SELECT_x`) +@tparam number exposure (from `-128` to `127`) +*/ +static int cam_setExposure(lua_State *L) { + u8 cam = luaL_checkinteger(L, 1); + s8 expo = luaL_checkinteger(L, 2); + + CAMU_SetExposure(cam, expo); + + return 0; +} + +/*** +Set the white balance of a camera. +@function setWhiteBalance +@tparam number camera (`SELECT_x`) +@tparam number white white balance (`WHITE_BALANCE_x`) +*/ +static int cam_setWhiteBalance(lua_State *L) { + u8 cam = luaL_checkinteger(L, 1); + u8 bal = luaL_checkinteger(L, 2); + + CAMU_SetWhiteBalance(cam, bal); + + return 0; +} + +/*** +Set the sharpness of a camera. +@function setSharpness +@tparam number camera (`SELECT_x`) +@tparam number sharpness from (from `-128` to `127`) +*/ +static int cam_setSharpness(lua_State *L) { + u8 cam = luaL_checkinteger(L, 1); + s8 sharp = luaL_checkinteger(L, 2); + + CAMU_SetSharpness(cam, sharp); + + return 0; +} + +/*** +Set the auto exposure mode of a camera. +@function setAutoExposure +@tparam number camera (`SELECT_x`) +@tparam boolean auto `true` to enable, `false` to disable +*/ +static int cam_setAutoExposure(lua_State *L) { + u8 cam = luaL_checkinteger(L, 1); + bool expo = lua_toboolean(L, 2); + + CAMU_SetAutoExposure(cam, expo); + + return 0; +} + +/*** +Check if the auto exposure mode is enabled for a camera. +@function isAutoExposure +@tparam number camera (`SELECT_x`) +@treturn boolean `true` if enabled, `false` if not +*/ +static int cam_isAutoExposure(lua_State *L) { + u8 cam = luaL_checkinteger(L, 1); + bool expo = false; + + CAMU_IsAutoExposure(&expo, cam); + + lua_pushboolean(L, expo); + return 1; +} + +/*** +Set the auto white balance mode for a camera. +@function setAutoWhiteBalance +@tparam number camera (`SELECT_x`) +@tparam boolean auto `true` to enable, `false` to disable +*/ +static int cam_setAutoWhiteBalance(lua_State *L) { + u8 cam = luaL_checkinteger(L, 1); + bool bal = lua_toboolean(L, 2); + + CAMU_SetAutoWhiteBalance(cam, bal); + + return 0; +} + +/*** +Check if the auto white balance mode is enabled for a camera. +@function isAutoWhiteBalance +@tparam number camera (`SELECT_x`) +@treturn boolean `true` if enabled, `false` if not +*/ +static int cam_isAutoWhiteBalance(lua_State *L) { + u8 cam = luaL_checkinteger(L, 1); + bool bal = false; + + CAMU_IsAutoWhiteBalance(&bal, cam); + + lua_pushboolean(L, bal); + return 1; +} + +/*** +Set the contrast on a camera. +@function setContrast +@tparam number camera (`SELECT_x`) +@tparam number contrast (`CONTRAST_x`) +*/ +static int cam_setContrast(lua_State *L) { + u8 cam = luaL_checkinteger(L, 1); + u8 cont = luaL_checkinteger(L, 2); + + CAMU_SetContrast(cam, cont); + + return 0; +} + +/*** +Set the lens correction of a camera. +@function setLensCorrection +@tparam number camera (`SELECT_x`) +@tparam number correction lens correction (`LENS_CORRECTION_x`) +*/ +static int cam_setLensCorrection(lua_State *L) { + u8 cam = luaL_checkinteger(L, 1); + u8 corr = luaL_checkinteger(L, 2); + + CAMU_SetLensCorrection(cam, corr); + + return 0; +} + +/*** +Set the window where the exposure will be check. +@function setAutoExposureWindow +@tparam number x x starting position of the window +@tparam number y y starting position of the window +@tparam number w width of the window +@tparam number h height of the window +*/ +static int cam_setAutoExposureWindow(lua_State *L) { + u8 cam = luaL_checkinteger(L, 1); + s16 x = luaL_checkinteger(L, 2); + s16 y = luaL_checkinteger(L, 3); + s16 w = luaL_checkinteger(L, 4); + s16 h = luaL_checkinteger(L, 5); + + CAMU_SetAutoExposureWindow(cam, x, y, w, h); + + return 0; +} + +/*** +Set the window where the white balance will be check. +@function setAutoWhiteBalanceWindow +@tparam number x x starting position of the window +@tparam number y y starting position of the window +@tparam number w width of the window +@tparam number h height of the window +*/ +static int cam_setAutoWhiteBalanceWindow(lua_State *L) { + u8 cam = luaL_checkinteger(L, 1); + s16 x = luaL_checkinteger(L, 2); + s16 y = luaL_checkinteger(L, 3); + s16 w = luaL_checkinteger(L, 4); + s16 h = luaL_checkinteger(L, 5); + + CAMU_SetAutoWhiteBalanceWindow(cam, x, y, w, h); + + return 0; +} + +/*** +Enable or disable the noise filter on a camera. +@function setNoiseFilter +@tparam number camera (`SELECT_x`) +@tparam boolean filter `true` to enable, `false` to disable +*/ +static int cam_setNoiseFilter(lua_State *L) { + u8 cam = luaL_checkinteger(L, 1); + bool fil = lua_toboolean(L, 2); + + CAMU_SetNoiseFilter(cam, fil); + + return 0; +} + +/*** +Play a shutter sound. +@function playShutterSound +@tparam number sound shutter sound type (`SHUTTER_x`) +*/ +static int cam_playShutterSound(lua_State *L) { + u8 shut = luaL_checkinteger(L, 1); + + CAMU_PlayShutterSound(shut); + + return 0; +} + +/*** +Set the size of the base camera image. +@function setSize +@tparam number camera (`SELECT_x`) +@tparam number size (`SIZE_x`) +@tparam number context (`CONTEXT_x`) +*/ +static int cam_setSize(lua_State *L) { + u8 cam = luaL_checkinteger(L, 1); + u8 size = luaL_checkinteger(L, 2); + u8 context = luaL_checkinteger(L, 3); + + CAMU_SetSize(cam, size, context); + + return 0; +} + +/*** +Set the effect applied on the image. +@function setEffect +@tparam number camera (`SELECT_x`) +@tparam number effect (`EFFECT_x`) +@tparam number context (`CONTEXT_x`) +*/ +static int cam_setEffect(lua_State *L) { + u8 cam = luaL_checkinteger(L, 1); + u8 effect = luaL_checkinteger(L, 2); + u8 context = luaL_checkinteger(L, 3); + + CAMU_SetEffect(cam, effect, context); + + return 0; +} + +/*** +Take a picture and put it in a texture. +@function takePicture +@tparam number camera should be PORT_CAM1 if you have only 1 camera activated (`PORT_x`) +@tparam number w width of the picture +@tparam number h height of the picture +@tparam[opt=PLACE_RAM] number place where to put the texture +@treturn texture the texture object +*/ +static int cam_takePicture(lua_State *L) { + u8 cam = luaL_checkinteger(L, 1); + s16 w = luaL_optinteger(L, 2, 640); + s16 h = luaL_optinteger(L, 3, 480); + u32 bufSize = 0; + + // Take the actual picture + CAMU_GetMaxBytes(&bufSize, w, h); + u8* buf = malloc(bufSize); + CAMU_SetTransferBytes(cam, bufSize, w, h); + + Handle camReceiveEvent = 0; + + CAMU_ClearBuffer(cam); + CAMU_SetReceiving(&camReceiveEvent, buf, cam, bufSize, (s16)bufSize); + CAMU_StartCapture(cam); + svcWaitSynchronization(camReceiveEvent, 300000000ULL); + CAMU_SetReceiving(&camReceiveEvent, buf, cam, bufSize, (s16)bufSize); // I don't know why you have to put it twice ... + svcWaitSynchronization(camReceiveEvent, 300000000ULL); + CAMU_StopCapture(cam); + + // Load it in a texture + u8 place = luaL_optinteger(L, 4, SF2D_PLACE_RAM); + + texture_userdata *texture; + texture = (texture_userdata *)lua_newuserdata(L, sizeof(*texture)); + + luaL_getmetatable(L, "LTexture"); + lua_setmetatable(L, -2); + + texture->texture = sf2d_create_texture_mem_RGBA8(buf, w, h, TEXFMT_RGB565, place); + sf2d_texture_tile32(texture->texture); + + texture->scaleX = 1.0f; + texture->scaleY = 1.0f; + texture->blendColor = 0xffffffff; + + return 1; +} + +// Functions +static const struct luaL_Reg cam_lib[] = { + {"init", cam_init }, + {"shutdown", cam_shutdown }, + {"activate", cam_activate }, + {"setExposure", cam_setExposure }, + {"setWhiteBalance", cam_setWhiteBalance }, + {"setSharpness", cam_setSharpness }, + {"setAutoExposure", cam_setAutoExposure }, + {"isAutoExposure", cam_isAutoExposure }, + {"setAutoWhiteBalance", cam_setAutoWhiteBalance }, + {"isAutoWhiteBalance", cam_isAutoWhiteBalance }, + {"setContrast", cam_setContrast }, + {"setLensCorrection", cam_setLensCorrection }, + {"setAutoExposureWindow", cam_setAutoExposureWindow }, + {"setAutoWhiteBalanceWindow", cam_setAutoWhiteBalanceWindow}, + {"setNoiseFilter", cam_setNoiseFilter }, + {"playShutterSound", cam_playShutterSound }, + {"setSize", cam_setSize }, + {"setEffect", cam_setEffect }, + {"takePicture", cam_takePicture }, + {NULL, NULL} +}; + +// Constants. Warning: long block +struct { char *name; int value; } cam_constants[] = { + // port + /*** + First camera activated. + @field PORT_CAM1 + */ + {"PORT_CAM1", PORT_CAM1}, + /*** + Second camera activated. + @field PORT_CAM2 + */ + {"PORT_CAM2", PORT_CAM2}, + /*** + The two activated camera. Should not be used ATM. + @field PORT_BOTH + */ + {"PORT_BOTH", PORT_BOTH}, + // camera selection + /*** + @field SELECT_NONE + */ + {"SELECT_NONE", SELECT_NONE }, + /*** + @field SELECT_OUT1 + */ + {"SELECT_OUT1", SELECT_OUT1 }, + /*** + @field SELECT_IN1 + */ + {"SELECT_IN1", SELECT_IN1 }, + /*** + @field SELECT_OUT2 + */ + {"SELECT_OUT2", SELECT_OUT2 }, + /*** + @field SELECT_IN1_OUT1 + */ + {"SELECT_IN1_OUT1", SELECT_IN1_OUT1 }, + /*** + @field SELECT_OUT1_OUT2 + */ + {"SELECT_OUT1_OUT2", SELECT_OUT1_OUT2}, + /*** + @field SELECT_IN1_OUT2 + */ + {"SELECT_IN1_OUT2", SELECT_IN1_OUT2 }, + /*** + @field SELECT_ALL + */ + {"SELECT_ALL", SELECT_ALL }, + // context + /*** + @field CONTEXT_NONE + */ + {"CONTEXT_NONE", CONTEXT_NONE}, + /*** + @field CONTEXT_A + */ + {"CONTEXT_A", CONTEXT_A }, + /*** + @field CONTEXT_B + */ + {"CONTEXT_B", CONTEXT_B }, + /*** + @field CONTEXT_BOTH + */ + {"CONTEXT_BOTH", CONTEXT_BOTH}, + // image flip + /*** + @field FLIP_NONE + */ + {"FLIP_NONE", FLIP_NONE }, + /*** + @field FLIP_HORIZONTAL + */ + {"FLIP_HORIZONTAL", FLIP_HORIZONTAL}, + /*** + @field FLIP_VERTICAL + */ + {"FLIP_VERTICAL", FLIP_VERTICAL }, + /*** + @field FLIP_REVERSE + */ + {"FLIP_REVERSE", FLIP_REVERSE }, + // image size + /*** + 640x480 + @field SIZE_VGA + */ + {"SIZE_VGA", SIZE_VGA }, + /*** + 320x240 + @field SIZE_QVGA + */ + {"SIZE_QVGA", SIZE_QVGA }, + /*** + 160x120 + @field SIZE_QQVGA + */ + {"SIZE_QQVGA", SIZE_QQVGA }, + /*** + 352x288 + @field SIZE_CIF + */ + {"SIZE_CIF", SIZE_CIF }, + /*** + 176x144 + @field SIZE_QCIF + */ + {"SIZE_QCIF", SIZE_QCIF }, + /*** + 256x192 + @field SIZE_DS_LCD + */ + {"SIZE_DS_LCD", SIZE_DS_LCD }, + /*** + 512x384 + @field SIZE_DS_LCDx4 + */ + {"SIZE_DS_LCDx4", SIZE_DS_LCDx4 }, + /*** + 400x240 + @field SIZE_CTR_TOP_LCD + */ + {"SIZE_CTR_TOP_LCD", SIZE_CTR_TOP_LCD }, + /*** + 320x240 + @field SIZE_BOTTOM_LCD + */ + {"SIZE_CTR_BOTTOM_LCD", SIZE_CTR_BOTTOM_LCD}, + // frame rate + {"FRAME_RATE_15", FRAME_RATE_15 }, + {"FRAME_RATE_15_TO_5", FRAME_RATE_15_TO_5 }, + {"FRAME_RATE_15_To_2", FRAME_RATE_15_TO_2 }, + {"FRAME_RATE_10", FRAME_RATE_10 }, + {"FRAME_RATE_8_5", FRAME_RATE_8_5 }, + {"FRAME_RATE_5", FRAME_RATE_5 }, + {"FRAME_RATE_20", FRAME_RATE_20 }, + {"FRAME_RATE_20_TO_5", FRAME_RATE_20_TO_5 }, + {"FRAME_RATE_30", FRAME_RATE_30 }, + {"FRAME_RATE_30_TO_5", FRAME_RATE_30_TO_5 }, + {"FRAME_RATE_15_TO_10", FRAME_RATE_15_TO_10}, + {"FRAME_RATE_20_TO_10", FRAME_RATE_20_TO_10}, + {"FRAME_RATE_30_TO_10", FRAME_RATE_30_TO_10}, + // white balance + /*** + @field WHITe_BALANCE_AUTO + */ + {"WHITE_BALANCE_AUTO", WHITE_BALANCE_AUTO }, + /*** + @field WHITE_BALANCE_3200K + */ + {"WHITE_BALANCE_3200K", WHITE_BALANCE_3200K}, + /*** + @field WHITE_BALANCE_4150K + */ + {"WHITE_BALANCE_4150K", WHITE_BALANCE_4150K}, + /*** + @field WHITE_BALANCE_5200K + */ + {"WHITE_BALANCE_5200K", WHITE_BALANCE_5200K}, + /*** + @field WHITE_BALANCE_6000K + */ + {"WHITE_BALANCE_6000K", WHITE_BALANCE_6000K}, + /*** + @field WHITE_BALANCE_7000K + */ + {"WHITE_BALANCE_7000K", WHITE_BALANCE_7000K}, + // white balance aliases + /*** + @field WHITE_BALANCE_MAX + */ + {"WHITE_BALANCE_MAX", WHITE_BALANCE_MAX }, + /*** + @field WHITE_BALANCE_TUNGSTEN + */ + {"WHITE_BALANCE_TUNGSTEN", WHITE_BALANCE_TUNGSTEN }, + /*** + @field WHITE_BALANCE_WHITE_FLUORESCENT_LIGHT + */ + {"WHITE_BALANCE_WHITE_FLUORESCENT_LIGHT", WHITE_BALANCE_WHITE_FLUORESCENT_LIGHT}, + /*** + @field WHITE_BALANCE_DAYLIGHT + */ + {"WHITE_BALANCE_DAYLIGHT", WHITE_BALANCE_DAYLIGHT }, + /*** + @field WHITE_BALANCE_CLOUDY + */ + {"WHITE_BALANCE_CLOUDY", WHITE_BALANCE_CLOUDY }, + /*** + @field WHITE_BALANCE_HORIZON + */ + {"WHITE_BALANCE_HORIZON", WHITE_BALANCE_HORIZON }, + /*** + @field WHITE_BALANCE_SHADE + */ + {"WHITE_BALANCE_SHADE", WHITE_BALANCE_SHADE }, + // photo mode + {"PHOTO_MODE_NORMAL", PHOTO_MODE_NORMAL }, + {"PHOTO_MODE_PORTRAIT", PHOTO_MODE_PORTRAIT }, + {"PHOTO_MODE_LANDSCAPE", PHOTO_MODE_LANDSCAPE}, + {"PHOTO_MODE_NIGHTVIEW", PHOTO_MODE_NIGHTVIEW}, + {"PHOTO_MODE_LETTER", PHOTO_MODE_LETTER }, + // camera special effects + /*** + @field EFFECT_NONE + */ + {"EFFECT_NONE", EFFECT_NONE }, + /*** + @field EFFECT_MONO + */ + {"EFFECT_MONO", EFFECT_MONO }, + /*** + @field EFFECT_SEPIA + */ + {"EFFECT_SEPIA", EFFECT_SEPIA }, + /*** + @field EFFECT_NEGATIVE + */ + {"EFFECT_NEGATIVE", EFFECT_NEGATIVE}, + /*** + @field EFFECT_NEGAFILM + */ + {"EFFECT_NEGAFILM", EFFECT_NEGAFILM}, + /*** + @field EFFECT_SEPIA01 + */ + {"EFFECT_SEPIA01", EFFECT_SEPIA01 }, + // contrast + /*** + @field CONTRAST_01 + */ + {"CONTRAST_01", CONTRAST_PATTERN_01}, + /*** + @field CONTRAST_02 + */ + {"CONTRAST_02", CONTRAST_PATTERN_02}, + /*** + @field CONTRAST_03 + */ + {"CONTRAST_03", CONTRAST_PATTERN_03}, + /*** + @field CONTRAST_04 + */ + {"CONTRAST_04", CONTRAST_PATTERN_04}, + /*** + @field CONTRAST_05 + */ + {"CONTRAST_05", CONTRAST_PATTERN_05}, + /*** + @field CONTRAST_06 + */ + {"CONTRAST_06", CONTRAST_PATTERN_06}, + /*** + @field CONTRAST_07 + */ + {"CONTRAST_07", CONTRAST_PATTERN_07}, + /*** + @field CONTRAST_08 + */ + {"CONTRAST_08", CONTRAST_PATTERN_08}, + /*** + @field CONTRAST_09 + */ + {"CONTRAST_09", CONTRAST_PATTERN_09}, + /*** + @field CONTRAST_10 + */ + {"CONTRAST_10", CONTRAST_PATTERN_10}, + /*** + @field CONTRAST_11 + */ + {"CONTRAST_11", CONTRAST_PATTERN_11}, + /*** + @field CONTRAST_LOW + */ + {"CONTRAST_LOW", CONTRAST_LOW }, + /*** + @field CONTRAST_NORMAL + */ + {"CONTRAST_NORMAL", CONTRAST_NORMAL }, + /*** + @field CONTRAST_HIGH + */ + {"CONTRAST_HIGH", CONTRAST_HIGH }, + // lens correction + /*** + @field LENS_CORRECTION_OFF + */ + {"LENS_CORRECTION_OFF", LENS_CORRECTION_OFF }, + /*** + @field LENS_CORRECTION_NORMAL + */ + {"LENS_CORRECTION_NORMAL", LENS_CORRECTION_NORMAL}, + /*** + @field LENS_CORRECTION_BRIGHT + */ + {"LENS_CORRECTION_BRIGHT", LENS_CORRECTION_BRIGHT}, + // shutter sounds + /*** + @field SHUTTER_NORMAL + */ + {"SHUTTER_NORMAL", SHUTTER_SOUND_TYPE_NORMAL }, + /*** + @field SHUTTER_MOVIE + */ + {"SHUTTER_MOVIE", SHUTTER_SOUND_TYPE_MOVIE }, + /*** + @field SHUTTER_MOVIE_END + */ + {"SHUTTER_MOVIE_END", SHUTTER_SOUND_TYPE_MOVIE_END}, + // Wow, look, the end of the tunnel ! + // I think I will never do that again + {NULL, 0} +}; + +int luaopen_cam_lib(lua_State *L) { + luaL_newlib(L, cam_lib); + + for (int i = 0; cam_constants[i].name; i++) { + lua_pushinteger(L, cam_constants[i].value); + lua_setfield(L, -2, cam_constants[i].name); + } + + return 1; +} + +void load_cam_lib(lua_State *L) { + luaL_requiref(L, "ctr.cam", luaopen_cam_lib, false); +} diff --git a/source/ctr.c b/source/ctr.c index b046636..da788c0 100644 --- a/source/ctr.c +++ b/source/ctr.c @@ -84,7 +84,12 @@ The `ctr.socket` module. */ void load_socket_lib(lua_State *L); -//void load_cam_lib(lua_State *L); +/*** +The `ctr.cam` module. +@table cam +@see ctr.cam +*/ +void load_cam_lib(lua_State *L); /*** Return whether or not the program should continue. @@ -127,7 +132,7 @@ struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } { "qtm", load_qtm_lib, NULL }, { "cfgu", load_cfgu_lib, NULL }, { "socket", load_socket_lib, NULL }, -// { "cam", load_cam_lib, NULL }, + { "cam", load_cam_lib, NULL }, { NULL, NULL } }; From d5f8202c691dd2c41f538b4b8128fde98f0895f3 Mon Sep 17 00:00:00 2001 From: Reuh Date: Fri, 23 Oct 2015 21:02:27 +0200 Subject: [PATCH 027/101] Fixed tcpsocketobject:received when receiving less data than passed at arguments It cuts the string at the end of received data. --- source/socket.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/socket.c b/source/socket.c index d921c1c..5c69cec 100644 --- a/source/socket.c +++ b/source/socket.c @@ -200,7 +200,8 @@ static int socket_receive(lua_State *L) { } char *buff = malloc(count); - recv(userdata->socket, buff, count, flags); + int len = recv(userdata->socket, buff, count, flags); + *(buff+len) = 0x0; // text end lua_pushstring(L, buff); return 1; From 09ea79bc86d8fcd61aaa85ea662a2ef91d5641ca Mon Sep 17 00:00:00 2001 From: Reuh Date: Sat, 24 Oct 2015 11:12:10 +0200 Subject: [PATCH 028/101] Added line receiving to socket:receive and changed *a to a Added l and L. Removed the starting * of *a, because it isn't used in Lua 5.3 io lib. --- source/socket.c | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/source/socket.c b/source/socket.c index 5c69cec..3ad23c2 100644 --- a/source/socket.c +++ b/source/socket.c @@ -181,25 +181,52 @@ static int socket_listen(lua_State *L) { /*** Receive some data from the socket. +If no data is avaible, it returns an empty string (non-blocking). @function :receive -@tparam number size amount of data to receive; can also be "*a" to receive everything +@tparam[opt="l"] number/string size amount of bytes to receive; or + "a" to receive everything, + "l" to receive the next line, skipping the end of line, + "L" to receive the next line, keeping the end of line. @treturn string data */ static int socket_receive(lua_State *L) { socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); int count = 0; int flags = 0; + if (lua_isnumber(L, 2)) { count = luaL_checkinteger(L, 2); - } else if (lua_isstring(L, 2) && luaL_checkstring(L, 2) == (char*)&"*a") { - count = SIZE_MAX/2; } else { - lua_pushnil(L); - lua_pushstring(L, ""); - return 2; + const char *p = luaL_optstring(L, 2, "l"); + if (*p == 'a') { + count = SIZE_MAX/2; + + } else if (*p == 'l') { + luaL_Buffer b; + luaL_buffinit(L, &b); + + char buff; + while (recv(userdata->socket, &buff, 1, flags) > 0 && buff != '\n') luaL_addchar(&b, buff); + + luaL_pushresult(&b); + return 1; + + } else if (*p == 'L') { + luaL_Buffer b; + luaL_buffinit(L, &b); + + char buff; + while (buff != '\n' && recv(userdata->socket, &buff, 1, flags) > 0) luaL_addchar(&b, buff); + + luaL_pushresult(&b); + return 1; + + } else { + return luaL_argerror(L, 2, "invalid format"); + } } - char *buff = malloc(count); + char *buff = malloc(count+1); int len = recv(userdata->socket, buff, count, flags); *(buff+len) = 0x0; // text end From 3aeb5b3534402f7d6656bfa480554346e07bb671 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Sat, 24 Oct 2015 12:08:03 +0200 Subject: [PATCH 029/101] Fixed te cam lib, added some field for the documentation, added setFrameRate --- source/cam.c | 90 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 71 insertions(+), 19 deletions(-) diff --git a/source/cam.c b/source/cam.c index e310717..afa9009 100644 --- a/source/cam.c +++ b/source/cam.c @@ -296,6 +296,21 @@ static int cam_setEffect(lua_State *L) { return 0; } +/*** +Set the frame rate of a camera. +@function setFrameRate +@tparam number camera (`SELECT_x`) +@tparam number rate frame rate (`FRAME_RATE_x`) +*/ +static int cam_setFrameRate(lua_State *L) { + u8 cam = luaL_checkinteger(L, 1); + u8 rate = luaL_checkinteger(L, 2); + + CAMU_SetFrameRate(cam, rate); + + return 0; +} + /*** Take a picture and put it in a texture. @function takePicture @@ -319,10 +334,8 @@ static int cam_takePicture(lua_State *L) { Handle camReceiveEvent = 0; CAMU_ClearBuffer(cam); - CAMU_SetReceiving(&camReceiveEvent, buf, cam, bufSize, (s16)bufSize); CAMU_StartCapture(cam); - svcWaitSynchronization(camReceiveEvent, 300000000ULL); - CAMU_SetReceiving(&camReceiveEvent, buf, cam, bufSize, (s16)bufSize); // I don't know why you have to put it twice ... + CAMU_SetReceiving(&camReceiveEvent, buf, cam, bufSize, (s16)bufSize); svcWaitSynchronization(camReceiveEvent, 300000000ULL); CAMU_StopCapture(cam); @@ -365,13 +378,12 @@ static const struct luaL_Reg cam_lib[] = { {"playShutterSound", cam_playShutterSound }, {"setSize", cam_setSize }, {"setEffect", cam_setEffect }, + {"setFrameRate", cam_setFrameRate }, {"takePicture", cam_takePicture }, {NULL, NULL} }; -// Constants. Warning: long block struct { char *name; int value; } cam_constants[] = { - // port /*** First camera activated. @field PORT_CAM1 @@ -387,7 +399,6 @@ struct { char *name; int value; } cam_constants[] = { @field PORT_BOTH */ {"PORT_BOTH", PORT_BOTH}, - // camera selection /*** @field SELECT_NONE */ @@ -420,7 +431,6 @@ struct { char *name; int value; } cam_constants[] = { @field SELECT_ALL */ {"SELECT_ALL", SELECT_ALL }, - // context /*** @field CONTEXT_NONE */ @@ -437,7 +447,6 @@ struct { char *name; int value; } cam_constants[] = { @field CONTEXT_BOTH */ {"CONTEXT_BOTH", CONTEXT_BOTH}, - // image flip /*** @field FLIP_NONE */ @@ -454,7 +463,6 @@ struct { char *name; int value; } cam_constants[] = { @field FLIP_REVERSE */ {"FLIP_REVERSE", FLIP_REVERSE }, - // image size /*** 640x480 @field SIZE_VGA @@ -500,21 +508,58 @@ struct { char *name; int value; } cam_constants[] = { @field SIZE_BOTTOM_LCD */ {"SIZE_CTR_BOTTOM_LCD", SIZE_CTR_BOTTOM_LCD}, - // frame rate + /*** + @field FRAME_RATE_15 + */ {"FRAME_RATE_15", FRAME_RATE_15 }, + /*** + @field FRAME_RATE_15_TO_5 + */ {"FRAME_RATE_15_TO_5", FRAME_RATE_15_TO_5 }, + /*** + @field FRAME_RATE_15_TO_2 + */ {"FRAME_RATE_15_To_2", FRAME_RATE_15_TO_2 }, + /*** + @field FRAME_RATE_10 + */ {"FRAME_RATE_10", FRAME_RATE_10 }, + /*** + @field FRAME_RATE_8_5 + */ {"FRAME_RATE_8_5", FRAME_RATE_8_5 }, + /*** + @field FRAME_RATE_5 + */ {"FRAME_RATE_5", FRAME_RATE_5 }, + /*** + @field FRAME_RATE_20 + */ {"FRAME_RATE_20", FRAME_RATE_20 }, + /*** + @field FRAME_RATE_20_TO_5 + */ {"FRAME_RATE_20_TO_5", FRAME_RATE_20_TO_5 }, + /*** + @field FRAME_RATE_30 + */ {"FRAME_RATE_30", FRAME_RATE_30 }, + /*** + @field FRAME_RATE_TO_5 + */ {"FRAME_RATE_30_TO_5", FRAME_RATE_30_TO_5 }, + /*** + @field FRAME_RATE_15_TO_10 + */ {"FRAME_RATE_15_TO_10", FRAME_RATE_15_TO_10}, + /*** + @field FRAME_RATE_20_TO_10 + */ {"FRAME_RATE_20_TO_10", FRAME_RATE_20_TO_10}, + /*** + @field FRAME_RATE_30_TO_10 + */ {"FRAME_RATE_30_TO_10", FRAME_RATE_30_TO_10}, - // white balance /*** @field WHITe_BALANCE_AUTO */ @@ -539,7 +584,6 @@ struct { char *name; int value; } cam_constants[] = { @field WHITE_BALANCE_7000K */ {"WHITE_BALANCE_7000K", WHITE_BALANCE_7000K}, - // white balance aliases /*** @field WHITE_BALANCE_MAX */ @@ -568,13 +612,26 @@ struct { char *name; int value; } cam_constants[] = { @field WHITE_BALANCE_SHADE */ {"WHITE_BALANCE_SHADE", WHITE_BALANCE_SHADE }, - // photo mode + /*** + @field PHOTO_MODE_NORMAL + */ {"PHOTO_MODE_NORMAL", PHOTO_MODE_NORMAL }, + /*** + @field PHOTO_MODE_PORTRAIT + */ {"PHOTO_MODE_PORTRAIT", PHOTO_MODE_PORTRAIT }, + /*** + @field PHOTO_MODE_LANDSCAPE + */ {"PHOTO_MODE_LANDSCAPE", PHOTO_MODE_LANDSCAPE}, + /*** + @field PHOTO_MODE_NIGHTVIEW + */ {"PHOTO_MODE_NIGHTVIEW", PHOTO_MODE_NIGHTVIEW}, + /*** + @field PHOTO_MODE_LETTER + */ {"PHOTO_MODE_LETTER", PHOTO_MODE_LETTER }, - // camera special effects /*** @field EFFECT_NONE */ @@ -599,7 +656,6 @@ struct { char *name; int value; } cam_constants[] = { @field EFFECT_SEPIA01 */ {"EFFECT_SEPIA01", EFFECT_SEPIA01 }, - // contrast /*** @field CONTRAST_01 */ @@ -656,7 +712,6 @@ struct { char *name; int value; } cam_constants[] = { @field CONTRAST_HIGH */ {"CONTRAST_HIGH", CONTRAST_HIGH }, - // lens correction /*** @field LENS_CORRECTION_OFF */ @@ -669,7 +724,6 @@ struct { char *name; int value; } cam_constants[] = { @field LENS_CORRECTION_BRIGHT */ {"LENS_CORRECTION_BRIGHT", LENS_CORRECTION_BRIGHT}, - // shutter sounds /*** @field SHUTTER_NORMAL */ @@ -682,8 +736,6 @@ struct { char *name; int value; } cam_constants[] = { @field SHUTTER_MOVIE_END */ {"SHUTTER_MOVIE_END", SHUTTER_SOUND_TYPE_MOVIE_END}, - // Wow, look, the end of the tunnel ! - // I think I will never do that again {NULL, 0} }; From 8e94add9a685243600217ae559703e1a1a407107 Mon Sep 17 00:00:00 2001 From: Reuh Date: Sat, 24 Oct 2015 13:34:53 +0200 Subject: [PATCH 030/101] Fixed documentation of socket:connect --- source/socket.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/socket.c b/source/socket.c index 3ad23c2..3d7f7b9 100644 --- a/source/socket.c +++ b/source/socket.c @@ -139,6 +139,9 @@ Connect a socket to a server. The TCP object becomes a TCPClient object. @function :connect @tparam string host address of the host @tparam number port port of the server +@treturn[1] boolean true if success +@treturn[2] boolean false if failed +@treturn[2] string error string */ static int socket_connect(lua_State *L) { socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); @@ -161,7 +164,7 @@ static int socket_connect(lua_State *L) { return 2; } - lua_pushinteger(L, 1); + lua_pushboolean(L, 1); return 1; } From 81f8edb25dc9b05428179e1c84c9c95830ceb6e5 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Sat, 24 Oct 2015 22:03:09 +0200 Subject: [PATCH 031/101] Added UDP sockets functions (untested, like 80% of my code) I didn't find a server/protocol to test the UDP sockets, so I didn't test. Also, there's no built-in "pseudo-connected" mode, you have to manage it in your code. --- source/socket.c | 157 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 132 insertions(+), 25 deletions(-) diff --git a/source/socket.c b/source/socket.c index 3d7f7b9..8a4cf3a 100644 --- a/source/socket.c +++ b/source/socket.c @@ -1,5 +1,6 @@ /*** -The `socket` module. Almost like luasocket, but for TCP only. +The `socket` module. Almost like luasocket, but for the TCP part only. +The UDP part is only without connection. @module ctr.socket @usage local socket = require("ctr.socket") */ @@ -79,34 +80,34 @@ static int socket_tcp(lua_State *L) { } /*** -TCP Sockets -@section TCP +Return an UDP socket. +@function udp +@treturn UDPMaster UDP socket */ - -/*** -Accept a connection on a server. -@function :accept -@treturn TCPClient tcp client object, or nil. -*/ -static int socket_accept(lua_State *L) { - socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); - - socket_userdata *client = lua_newuserdata(L, sizeof(*client)); +static int socket_udp(lua_State *L) { + socket_userdata *userdata = lua_newuserdata(L, sizeof(*userdata)); luaL_getmetatable(L, "LSocket"); lua_setmetatable(L, -2); - socklen_t addrSize = sizeof(client->addr); - client->socket = accept(userdata->socket, (struct sockaddr*)&client->addr, &addrSize); - if (client->socket < 0) { + userdata->socket = socket(AF_INET, SOCK_DGRAM, 0); + if (userdata->socket < 0) { lua_pushnil(L); - return 1; + lua_pushstring(L, "Failed to create a TCP socket"); + return 2; } + userdata->addr.sin_family = AF_INET; + return 1; } /*** -Bind a socket. The TCP object become a TCPServer object. +All sockets +@section sockets +*/ + +/*** +Bind a socket. The socket object become a socketServer object. @function :bind @tparam number port the port to bind the socket on. */ @@ -134,6 +135,33 @@ static int socket_close(lua_State *L) { return 0; } +/*** +TCP Sockets +@section TCP +*/ + +/*** +Accept a connection on a server. +@function :accept +@treturn TCPClient tcp client object, or nil. +*/ +static int socket_accept(lua_State *L) { + socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); + + socket_userdata *client = lua_newuserdata(L, sizeof(*client)); + luaL_getmetatable(L, "LSocket"); + lua_setmetatable(L, -2); + + socklen_t addrSize = sizeof(client->addr); + client->socket = accept(userdata->socket, (struct sockaddr*)&client->addr, &addrSize); + if (client->socket < 0) { + lua_pushnil(L); + return 1; + } + + return 1; +} + /*** Connect a socket to a server. The TCP object becomes a TCPClient object. @function :connect @@ -254,23 +282,102 @@ static int socket_send(lua_State *L) { return 1; } +/*** +UDP sockets +@section UDP +*/ + +/*** +Receive some data from a server. +@function :receivefrom +@tparam number count amount of data to receive +@tparam string host host name +@tparam number port port +@treturn string data +*/ +static int socket_receivefrom(lua_State *L) { + socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); + int count = luaL_checkinteger(L, 2); + size_t namesize = 0; + char *hostname = (char*)luaL_optlstring(L, 3, NULL, &namesize); + int port = luaL_optinteger(L, 4, 0); + + struct sockaddr_in from = {0}; + if (hostname != NULL) { // For a server + struct hostent *hostinfo = gethostbyname(hostname); + if (hostinfo == NULL) { + lua_pushnil(L); + return 1; + } + from.sin_addr = *(struct in_addr*)hostinfo->h_addr; + from.sin_port = htons(port); + from.sin_family = AF_INET; + } + + char* buffer = malloc(count+1); + int n = recvfrom(userdata->socket, buffer, count, 0, (struct sockaddr*)&from, NULL); + *(buffer+n) = 0x0; + + lua_pushstring(L, buffer); + if (hostname != NULL) { + return 1; + } else { + lua_pushstring(L, inet_ntoa(from.sin_addr)); + lua_pushinteger(L, ntohs(from.sin_port)); + return 3; + } +} + +/*** +Send some data to a server. +@function :sendto +@tparam string data data to send +@tparam string host host name +@tparam number port port +*/ +static int socket_sendto(lua_State *L) { + socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); + size_t datasize = 0; + char *data = (char*)luaL_checklstring(L, 2, &datasize); + size_t namesize = 0; + char *hostname = (char*)luaL_checklstring(L, 3, &namesize); + int port = luaL_checkinteger(L, 4); + + struct hostent *hostinfo = gethostbyname(hostname); + if (hostinfo == NULL) { + lua_pushnil(L); + return 1; + } + struct sockaddr_in to = {0}; + to.sin_addr = *(struct in_addr*)hostinfo->h_addr; + to.sin_port = htons(port); + to.sin_family = AF_INET; + + sendto(userdata->socket, data, datasize, 0, (struct sockaddr*)&to, sizeof(to)); + + return 0; +} + // module functions static const struct luaL_Reg socket_functions[] = { {"init", socket_init }, {"shutdown", socket_shutdown}, {"tcp", socket_tcp }, + {"udp", socket_udp }, {NULL, NULL} }; // object static const struct luaL_Reg socket_methods[] = { - {"accept", socket_accept }, - {"bind", socket_bind }, - {"close", socket_close }, - {"connect", socket_connect }, - {"listen", socket_listen }, - {"receive", socket_receive }, - {"send", socket_send }, + {"accept", socket_accept }, + {"bind", socket_bind }, + {"close", socket_close }, + {"connect", socket_connect }, + {"listen", socket_listen }, + {"receive", socket_receive }, + {"receivefrom", socket_receivefrom}, + {"send", socket_send }, + {"sendto", socket_sendto }, {NULL, NULL} }; From 0efdc235765edc876b1ba2a657422f5cd6a2aee5 Mon Sep 17 00:00:00 2001 From: Reuh Date: Sun, 25 Oct 2015 11:08:27 +0100 Subject: [PATCH 032/101] Moved library path modifications to main.lua, made all paths relative The library path can now be easlily modified. Instead of always launching /3ds/ctruLua/main.lua, ctruLua will now launch the main.lua in the current directory (the ctruLua.3dsx directory when launched with HBL). On citra, this will be the root of the sdmc directory. --- libs/lua-5.3.1/src/luaconf.h | 35 ++++++++++++++++++++++++------ sdcard/3ds/ctruLua/editor/main.lua | 6 ++--- sdcard/3ds/ctruLua/main.lua | 7 ++++-- sdcard/3ds/ctruLua/openfile.lua | 5 +++-- source/fs.c | 13 +++++++---- source/main.c | 4 +--- 6 files changed, 49 insertions(+), 21 deletions(-) diff --git a/libs/lua-5.3.1/src/luaconf.h b/libs/lua-5.3.1/src/luaconf.h index 4cc26f7..74a9252 100644 --- a/libs/lua-5.3.1/src/luaconf.h +++ b/libs/lua-5.3.1/src/luaconf.h @@ -167,17 +167,38 @@ ** hierarchy or if you want to install your libraries in ** non-conventional directories. */ -#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR "/" - -#define LUA_ROOT "sdmc:/" -#define LUA_LDIR LUA_ROOT "3ds/ctruLua/libs/" -#define LUA_CDIR LUA_ROOT "3ds/ctruLua/libs/" +#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR +#if defined(_WIN32) /* { */ +/* +** In Windows, any exclamation mark ('!') in the path is replaced by the +** path of the directory of the executable file of the current process. +*/ +#define LUA_LDIR "!\\lua\\" +#define LUA_CDIR "!\\" +#define LUA_SHRDIR "!\\..\\share\\lua\\" LUA_VDIR "\\" #define LUA_PATH_DEFAULT \ - LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ - LUA_LDIR LUA_VDIR"?.lua;" LUA_LDIR LUA_VDIR"?/init.lua;" \ + LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \ + LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \ + ".\\?.lua;" ".\\?\\init.lua" +#define LUA_CPATH_DEFAULT \ + LUA_CDIR"?.dll;" \ + LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \ + LUA_CDIR"loadall.dll;" ".\\?.dll" + +#else /* }{ */ + +#define LUA_ROOT "/usr/local/" +#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR "/" +#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR "/" +#define LUA_PATH_DEFAULT \ + LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \ "./?.lua;" "./?/init.lua" #define LUA_CPATH_DEFAULT \ LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so" +#endif /* } */ + /* @@ LUA_DIRSEP is the directory separator (for submodules). diff --git a/sdcard/3ds/ctruLua/editor/main.lua b/sdcard/3ds/ctruLua/editor/main.lua index ec14bad..26709b4 100644 --- a/sdcard/3ds/ctruLua/editor/main.lua +++ b/sdcard/3ds/ctruLua/editor/main.lua @@ -3,8 +3,8 @@ local hid = require("ctr.hid") local gfx = require("ctr.gfx") -- Open libs -local keyboard = dofile("sdmc:/3ds/ctruLua/keyboard.lua") -local openfile = dofile("sdmc:/3ds/ctruLua/openfile.lua") +local keyboard = require("keyboard") +local openfile = require("openfile") local color = dofile("color.lua") local syntax = dofile("syntax.lua") @@ -12,7 +12,7 @@ local syntax = dofile("syntax.lua") local font = gfx.font.load("VeraMono.ttf") -- Open file -local path, status = openfile("Choose a file to edit", "/3ds/ctruLua/", nil, "any") +local path, status = openfile("Choose a file to edit", nil, nil, "any") if not path then return end local lineEnding local lines = {} diff --git a/sdcard/3ds/ctruLua/main.lua b/sdcard/3ds/ctruLua/main.lua index 560a7f6..a0870ab 100644 --- a/sdcard/3ds/ctruLua/main.lua +++ b/sdcard/3ds/ctruLua/main.lua @@ -1,8 +1,11 @@ local fs = require("ctr.fs") +-- Set up path +local ldir = fs.getDirectory().."libs/" +package.path = package.path..";".. ldir.."?.lua;".. ldir.."?/init.lua" + repeat - fs.setDirectory("sdmc:/3ds/ctruLua") - local file = dofile("openfile.lua")("Choose a Lua file to execute", "/3ds/ctruLua/", ".lua", "exist") + local file = require("openfile")("Choose a Lua file to execute", nil, ".lua", "exist") if file then fs.setDirectory(file:match("^(.-)[^/]*$")) dofile(file) diff --git a/sdcard/3ds/ctruLua/openfile.lua b/sdcard/3ds/ctruLua/openfile.lua index 32a924e..2f67bae 100644 --- a/sdcard/3ds/ctruLua/openfile.lua +++ b/sdcard/3ds/ctruLua/openfile.lua @@ -1,6 +1,6 @@ --- Open a file explorer to select a file. -- string title: title of the file explorer. --- string curdir: the directory to initially open the file explorer in. +-- string curdir: the directory to initially open the file explorer in, or nil for the current directory. -- string exts: the file extensions the user can select, separated by ";". If nil, all extensions are accepted. -- string type: "exist" to select an existing file, "new" to select an non-existing file or "any" to select a existing -- or non-existing file name. If nil, defaults to "exist". @@ -11,9 +11,10 @@ return function(title, curdir, exts, type) local ctr = require("ctr") local gfx = require("ctr.gfx") - local keyboard = dofile("sdmc:/3ds/ctruLua/keyboard.lua") + local keyboard = require("keyboard") -- Arguments + local curdir = curdir or ctr.fs.getDirectory() local type = type or "exist" -- Variables diff --git a/source/fs.c b/source/fs.c index 576ec48..ea6231e 100644 --- a/source/fs.c +++ b/source/fs.c @@ -1,4 +1,6 @@ #include +#include +#include #include <3ds/types.h> #include <3ds/util/utf.h> @@ -88,12 +90,15 @@ static int fs_setDirectory(lua_State *L) { int result = chdir(path); - if (result == 0) + if (result == 0) { lua_pushboolean(L, true); - else - lua_pushboolean(L, false); + return 1; - return 1; + } else { + lua_pushboolean(L, false); + lua_pushstring(L, strerror(errno)); + return 2; + } } static const struct luaL_Reg fs_lib[] = { diff --git a/source/main.c b/source/main.c index be7d9d7..21750ed 100644 --- a/source/main.c +++ b/source/main.c @@ -4,8 +4,6 @@ #include #include -#define BOOT_FILE "sdmc:/3ds/ctruLua/main.lua" - void load_ctr_lib(lua_State *L); void unload_ctr_lib(lua_State *L); @@ -47,7 +45,7 @@ int main() { load_ctr_lib(L); // Do the actual thing - if (luaL_dofile(L, BOOT_FILE)) error(luaL_checkstring(L, -1)); + if (luaL_dofile(L, "main.lua")) error(luaL_checkstring(L, -1)); // Unload libs unload_ctr_lib(L); From d015918d225e1501c92ee94251a33ef951eb3efa Mon Sep 17 00:00:00 2001 From: Reuh Date: Sun, 25 Oct 2015 12:03:21 +0100 Subject: [PATCH 033/101] Ignore the sdmc: prefix in fs.list; added missing space on some strings for the null-termination --- source/font.c | 2 +- source/fs.c | 4 +++- source/gfx.c | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/source/font.c b/source/font.c index be75d45..935aa64 100644 --- a/source/font.c +++ b/source/font.c @@ -91,7 +91,7 @@ static int font_object_width(lua_State *L) { int size = luaL_optinteger(L, 3, 9); // Wide caracters support. (wchar = UTF32 on 3DS.) - wchar_t wtext[len]; + wchar_t wtext[len+1]; len = mbstowcs(wtext, text, len); *(wtext+len) = 0x0; // text end diff --git a/source/fs.c b/source/fs.c index ea6231e..1b0bef1 100644 --- a/source/fs.c +++ b/source/fs.c @@ -20,6 +20,8 @@ void load_lzlib(lua_State *L); static int fs_list(lua_State *L) { const char *path = luaL_checkstring(L, 1); + if (strncmp(path, "sdmc:", 5) == 0) path += 5; // Ignore sdmc: prefix + lua_newtable(L); int i = 1; // table index @@ -36,7 +38,7 @@ static int fs_list(lua_State *L) { if (!entriesRead) break; - uint8_t name[256]; // utf8 file name + uint8_t name[0x106+1]; // utf8 file name size_t size = utf16_to_utf8(name, buffer.name, 0x106); *(name+size) = 0x0; // mark text end diff --git a/source/gfx.c b/source/gfx.c index c43be37..b984027 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -276,7 +276,7 @@ static int gfx_text(lua_State *L) { if (font->font == NULL) luaL_error(L, "The font object was unloaded"); // Wide caracters support. (wchar = UTF32 on 3DS.) - wchar_t wtext[len]; + wchar_t wtext[len+1]; len = mbstowcs(wtext, text, len); *(wtext+len) = 0x0; // text end @@ -316,7 +316,7 @@ static int gfx_wrappedText(lua_State *L) { // Wide caracters support. (wchar = UTF32 on 3DS.) // Disabled as sftd_draw_wtext_wrap() doesn't exist. - /*wchar_t wtext[len]; + /*wchar_t wtext[len+1]; len = mbstowcs(wtext, text, len); *(wtext+len) = 0x0; // text end */ From 3b8d56de6320c92d9969ee6ab271dbc4933e8332 Mon Sep 17 00:00:00 2001 From: Reuh Date: Sun, 25 Oct 2015 12:22:58 +0100 Subject: [PATCH 034/101] Documented ctr.fs --- source/fs.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/source/fs.c b/source/fs.c index 1b0bef1..4d89cde 100644 --- a/source/fs.c +++ b/source/fs.c @@ -1,3 +1,8 @@ +/*** +The `fs` module. +@module ctr.fs +@usage local fs = require("ctr.fs") +*/ #include #include #include @@ -15,8 +20,31 @@ FS_archive sdmcArchive; FS_archive romfsArchive; #endif +/*** +The `ctr.fs.lzlib` module. +@table lzlib +@see ctr.fs.lzlib +*/ void load_lzlib(lua_State *L); +/*** +Lists a directory contents. +@function list +@tparam string path the directory we wants to list the content +@treturn table the item list. Each item is a table like: +` + { + name = "Item name.txt", + shortName = "ITEM~", + shortExt = "TXT", + isDirectory = false, + isHidden = false, + isArchive = false, + isReadOnly = false, + fileSize = 321 -- (integer) in bytes + } +` +*/ static int fs_list(lua_State *L) { const char *path = luaL_checkstring(L, 1); @@ -71,6 +99,12 @@ static int fs_list(lua_State *L) { return 1; } +/*** +Check if a item (file or directory) exists. +@function exists +@tparam string path the item +@treturn boolean true if it exists, false otherwise +*/ static int fs_exists(lua_State *L) { const char *path = luaL_checkstring(L, 1); @@ -79,6 +113,11 @@ static int fs_exists(lua_State *L) { return 1; } +/*** +Get the current working directory. +@function getDirectory +@treturn string the current working directory +*/ static int fs_getDirectory(lua_State *L) { char cwd[256]; @@ -87,6 +126,14 @@ static int fs_getDirectory(lua_State *L) { return 1; } +/*** +Set the current working directory. +@function setDirectory +@tparam path path of the new working directory +@treturn[1] boolean true if success +@treturn[2] boolean false if failed +@treturn[2] string error message +*/ static int fs_setDirectory(lua_State *L) { const char *path = luaL_checkstring(L, 1); @@ -113,7 +160,7 @@ static const struct luaL_Reg fs_lib[] = { // submodules struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } fs_libs[] = { - {"zip", load_lzlib, NULL}, + {"lzlib", load_lzlib, NULL}, {NULL, NULL} }; From 095308732ce0adc46db9b59e9c8e48db95409dee Mon Sep 17 00:00:00 2001 From: Reuh Date: Sun, 25 Oct 2015 12:33:44 +0100 Subject: [PATCH 035/101] Documented ctr.gfx.color --- source/color.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/source/color.c b/source/color.c index bb27e80..a044b99 100644 --- a/source/color.c +++ b/source/color.c @@ -1,3 +1,8 @@ +/*** +The `gfx.color` module +@module ctr.gfx.color +@usage local color = require("ctr.gfx.color") +*/ #include #include @@ -6,18 +11,33 @@ u32 color_default = RGBA8(255, 255, 255, 255); u32 color_background = RGBA8(0, 0, 0, 255); +/*** +Set a color as the default one. +@function setDefault +@tparam integer color the color to set as the default one. +*/ static int color_setDefault(lua_State *L) { color_default = luaL_checkinteger(L, 1); return 0; } +/*** +Return the default color. +@function getDefault +@treturn integer default color +*/ static int color_getDefault(lua_State *L) { lua_pushinteger(L, color_default); return 1; } +/*** +Set a color as the background one. +@function setBackground +@tparam integer color the color to set as the background one. +*/ static int color_setBackground(lua_State *L) { color_background = luaL_checkinteger(L, 1); sf2d_set_clear_color(color_background); @@ -25,12 +45,26 @@ static int color_setBackground(lua_State *L) { return 0; } +/*** +Return the background color. +@function getBackground +@treturn integer background color +*/ static int color_getBackground(lua_State *L) { lua_pushinteger(L, color_background); return 1; } +/*** +Return a color from red, green, blue and alpha values. +@function RGBA8 +@tparam integer r the red value (0-255) +@tparam integer g the green value (0-255) +@tparam integer b the blue value (0-255) +@tparam[opt=255] integer a the alpha (opacity) value (0-255) +@treturn integer the color +*/ static int color_RGBA8(lua_State *L) { int r = luaL_checkinteger(L, 1); int g = luaL_checkinteger(L, 2); From b32fa5da2bfd008005c9e74ce01979a30acd803e Mon Sep 17 00:00:00 2001 From: Reuh Date: Sun, 25 Oct 2015 12:41:43 +0100 Subject: [PATCH 036/101] Moved keyboard.lua and openfile.lua to the libs/ directory --- sdcard/3ds/ctruLua/{ => libs}/keyboard.lua | 0 sdcard/3ds/ctruLua/{ => libs}/openfile.lua | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename sdcard/3ds/ctruLua/{ => libs}/keyboard.lua (100%) rename sdcard/3ds/ctruLua/{ => libs}/openfile.lua (100%) diff --git a/sdcard/3ds/ctruLua/keyboard.lua b/sdcard/3ds/ctruLua/libs/keyboard.lua similarity index 100% rename from sdcard/3ds/ctruLua/keyboard.lua rename to sdcard/3ds/ctruLua/libs/keyboard.lua diff --git a/sdcard/3ds/ctruLua/openfile.lua b/sdcard/3ds/ctruLua/libs/openfile.lua similarity index 100% rename from sdcard/3ds/ctruLua/openfile.lua rename to sdcard/3ds/ctruLua/libs/openfile.lua From e6e99f59c4542b9bdebba0badfc808c6a2da949b Mon Sep 17 00:00:00 2001 From: Reuh Date: Sun, 25 Oct 2015 17:01:26 +0100 Subject: [PATCH 037/101] In ctr.fs: automatically select archive when using absolute paths without archive prefix; fs.list should work with RomFS --- source/fs.c | 47 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/source/fs.c b/source/fs.c index 4d89cde..684570b 100644 --- a/source/fs.c +++ b/source/fs.c @@ -27,6 +27,26 @@ The `ctr.fs.lzlib` module. */ void load_lzlib(lua_State *L); +/* +Automatically prefix the path if it's absolute (starts with /) +*/ +const char* prefix_path(const char* path) { + if (strncmp(path, "/", 1) == 0) { + #ifdef ROMFS + char* prefix = "romfs:"; + #else + char* prefix = "sdmc:"; + #endif + + char out[256]; + strcpy(out, prefix); + return strcat(out, path); + + } else { + return path; + } +} + /*** Lists a directory contents. @function list @@ -46,17 +66,32 @@ Lists a directory contents. ` */ static int fs_list(lua_State *L) { - const char *path = luaL_checkstring(L, 1); - - if (strncmp(path, "sdmc:", 5) == 0) path += 5; // Ignore sdmc: prefix + const char *path = prefix_path(luaL_checkstring(L, 1)); lua_newtable(L); int i = 1; // table index + // Get default archive + #ifdef ROMFS + FS_archive archive = romfsArchive; + #else + FS_archive archive = sdmcArchive; + #endif + // Archive path override (and skip path prefix) + if (strncmp(path, "sdmc:", 5) == 0) { + path += 5; + archive = sdmcArchive; + #ifdef ROMFS + } else if (strncmp(path, "romfs:", 6) == 0) { + path += 6; + archive = romfsArchive; + #endif + } + FS_path dirPath = FS_makePath(PATH_CHAR, path); Handle dirHandle; - FSUSER_OpenDirectory(fsuHandle, &dirHandle, sdmcArchive, dirPath); + FSUSER_OpenDirectory(fsuHandle, &dirHandle, archive, dirPath); u32 entriesRead = 0; do { @@ -106,7 +141,7 @@ Check if a item (file or directory) exists. @treturn boolean true if it exists, false otherwise */ static int fs_exists(lua_State *L) { - const char *path = luaL_checkstring(L, 1); + const char *path = prefix_path(luaL_checkstring(L, 1)); lua_pushboolean(L, access(path, F_OK) == 0); @@ -135,7 +170,7 @@ Set the current working directory. @treturn[2] string error message */ static int fs_setDirectory(lua_State *L) { - const char *path = luaL_checkstring(L, 1); + const char *path = prefix_path(luaL_checkstring(L, 1)); int result = chdir(path); From 4acd13de9b5f9f92595ebf50aec10b2b50152c95 Mon Sep 17 00:00:00 2001 From: Reuh Date: Sun, 25 Oct 2015 18:56:39 +0100 Subject: [PATCH 038/101] Close socket on GC --- source/socket.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/socket.c b/source/socket.c index 8a4cf3a..4183284 100644 --- a/source/socket.c +++ b/source/socket.c @@ -372,6 +372,7 @@ static const struct luaL_Reg socket_methods[] = { {"accept", socket_accept }, {"bind", socket_bind }, {"close", socket_close }, + {"__gc", socket_close }, {"connect", socket_connect }, {"listen", socket_listen }, {"receive", socket_receive }, From 0105970ab7dd805a01bf63dcaaef0b1ab795cad5 Mon Sep 17 00:00:00 2001 From: Reuh Date: Fri, 4 Dec 2015 18:30:38 +0100 Subject: [PATCH 039/101] Updated to Lua 5.3.2 --- Makefile | 4 +- libs/lua-5.3.1/src/ltablib.c | 359 -------------- libs/lua-5.3.1/src/lvm.h | 68 --- libs/{lua-5.3.1 => lua-5.3.2}/Makefile | 2 +- libs/{lua-5.3.1 => lua-5.3.2}/README | 2 +- .../doc/contents.html | 0 libs/{lua-5.3.1 => lua-5.3.2}/doc/index.css | 0 libs/{lua-5.3.1 => lua-5.3.2}/doc/logo.gif | Bin libs/{lua-5.3.1 => lua-5.3.2}/doc/lua.1 | 0 libs/{lua-5.3.1 => lua-5.3.2}/doc/lua.css | 26 + libs/{lua-5.3.1 => lua-5.3.2}/doc/luac.1 | 0 libs/{lua-5.3.1 => lua-5.3.2}/doc/manual.css | 0 libs/{lua-5.3.1 => lua-5.3.2}/doc/manual.html | 156 +++--- .../doc/osi-certified-72x60.png | Bin libs/{lua-5.3.1 => lua-5.3.2}/doc/readme.html | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/Makefile | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/lapi.c | 149 +++--- libs/{lua-5.3.1 => lua-5.3.2}/src/lapi.h | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/lauxlib.c | 88 +++- libs/{lua-5.3.1 => lua-5.3.2}/src/lauxlib.h | 4 +- libs/{lua-5.3.1 => lua-5.3.2}/src/lbaselib.c | 41 +- libs/{lua-5.3.1 => lua-5.3.2}/src/lbitlib.c | 51 +- libs/{lua-5.3.1 => lua-5.3.2}/src/lcode.c | 6 +- libs/{lua-5.3.1 => lua-5.3.2}/src/lcode.h | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/lcorolib.c | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/lctype.c | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/lctype.h | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/ldblib.c | 6 +- libs/{lua-5.3.1 => lua-5.3.2}/src/ldebug.c | 8 +- libs/{lua-5.3.1 => lua-5.3.2}/src/ldebug.h | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/ldo.c | 194 +++++--- libs/{lua-5.3.1 => lua-5.3.2}/src/ldo.h | 26 +- libs/{lua-5.3.1 => lua-5.3.2}/src/ldump.c | 4 +- libs/{lua-5.3.1 => lua-5.3.2}/src/lfunc.c | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/lfunc.h | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/lgc.c | 28 +- libs/{lua-5.3.1 => lua-5.3.2}/src/lgc.h | 41 +- libs/{lua-5.3.1 => lua-5.3.2}/src/linit.c | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/liolib.c | 31 +- libs/{lua-5.3.1 => lua-5.3.2}/src/llex.c | 10 +- libs/{lua-5.3.1 => lua-5.3.2}/src/llex.h | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/llimits.h | 34 +- libs/{lua-5.3.1 => lua-5.3.2}/src/lmathlib.c | 6 +- libs/{lua-5.3.1 => lua-5.3.2}/src/lmem.c | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/lmem.h | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/loadlib.c | 4 +- libs/{lua-5.3.1 => lua-5.3.2}/src/lobject.c | 30 +- libs/{lua-5.3.1 => lua-5.3.2}/src/lobject.h | 86 ++-- libs/{lua-5.3.1 => lua-5.3.2}/src/lopcodes.c | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/lopcodes.h | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/loslib.c | 72 +-- libs/{lua-5.3.1 => lua-5.3.2}/src/lparser.c | 11 +- libs/{lua-5.3.1 => lua-5.3.2}/src/lparser.h | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/lprefix.h | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/lstate.c | 29 +- libs/{lua-5.3.1 => lua-5.3.2}/src/lstate.h | 14 +- libs/{lua-5.3.1 => lua-5.3.2}/src/lstring.c | 76 +-- libs/{lua-5.3.1 => lua-5.3.2}/src/lstring.h | 4 +- libs/{lua-5.3.1 => lua-5.3.2}/src/lstrlib.c | 181 ++++--- libs/{lua-5.3.1 => lua-5.3.2}/src/ltable.c | 78 +-- libs/{lua-5.3.1 => lua-5.3.2}/src/ltable.h | 7 +- libs/lua-5.3.2/src/ltablib.c | 449 ++++++++++++++++++ libs/{lua-5.3.1 => lua-5.3.2}/src/ltm.c | 19 +- libs/{lua-5.3.1 => lua-5.3.2}/src/ltm.h | 0 .../src/lua.dontcompile | 20 +- libs/{lua-5.3.1 => lua-5.3.2}/src/lua.h | 4 +- libs/{lua-5.3.1 => lua-5.3.2}/src/lua.hpp | 0 .../src/luac.dontcompile | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/luaconf.h | 65 ++- libs/{lua-5.3.1 => lua-5.3.2}/src/lualib.h | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/lundump.c | 22 +- libs/{lua-5.3.1 => lua-5.3.2}/src/lundump.h | 5 +- libs/{lua-5.3.1 => lua-5.3.2}/src/lutf8lib.c | 0 libs/{lua-5.3.1 => lua-5.3.2}/src/lvm.c | 217 +++++---- libs/lua-5.3.2/src/lvm.h | 114 +++++ libs/{lua-5.3.1 => lua-5.3.2}/src/lzio.c | 12 +- libs/{lua-5.3.1 => lua-5.3.2}/src/lzio.h | 3 +- 77 files changed, 1689 insertions(+), 1177 deletions(-) delete mode 100644 libs/lua-5.3.1/src/ltablib.c delete mode 100644 libs/lua-5.3.1/src/lvm.h rename libs/{lua-5.3.1 => lua-5.3.2}/Makefile (99%) rename libs/{lua-5.3.1 => lua-5.3.2}/README (70%) rename libs/{lua-5.3.1 => lua-5.3.2}/doc/contents.html (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/doc/index.css (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/doc/logo.gif (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/doc/lua.1 (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/doc/lua.css (85%) rename libs/{lua-5.3.1 => lua-5.3.2}/doc/luac.1 (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/doc/manual.css (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/doc/manual.html (98%) rename libs/{lua-5.3.1 => lua-5.3.2}/doc/osi-certified-72x60.png (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/doc/readme.html (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/Makefile (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lapi.c (91%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lapi.h (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lauxlib.c (92%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lauxlib.h (98%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lbaselib.c (92%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lbitlib.c (80%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lcode.c (99%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lcode.h (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lcorolib.c (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lctype.c (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lctype.h (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/ldblib.c (98%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/ldebug.c (98%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/ldebug.h (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/ldo.c (79%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/ldo.h (57%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/ldump.c (97%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lfunc.c (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lfunc.h (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lgc.c (97%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lgc.h (77%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/linit.c (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/liolib.c (96%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/llex.c (98%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/llex.h (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/llimits.h (89%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lmathlib.c (98%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lmem.c (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lmem.h (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/loadlib.c (99%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lobject.c (95%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lobject.h (93%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lopcodes.c (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lopcodes.h (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/loslib.c (82%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lparser.c (99%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lparser.h (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lprefix.h (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lstate.c (92%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lstate.h (93%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lstring.c (73%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lstring.h (87%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lstrlib.c (90%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/ltable.c (93%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/ltable.h (86%) create mode 100644 libs/lua-5.3.2/src/ltablib.c rename libs/{lua-5.3.1 => lua-5.3.2}/src/ltm.c (88%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/ltm.h (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lua.dontcompile (96%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lua.h (99%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lua.hpp (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/luac.dontcompile (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/luaconf.h (96%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lualib.h (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lundump.c (92%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lundump.h (78%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lutf8lib.c (100%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lvm.c (88%) create mode 100644 libs/lua-5.3.2/src/lvm.h rename libs/{lua-5.3.1 => lua-5.3.2}/src/lzio.c (79%) rename libs/{lua-5.3.1 => lua-5.3.2}/src/lzio.h (91%) diff --git a/Makefile b/Makefile index d7bd5ff..0b2d935 100644 --- a/Makefile +++ b/Makefile @@ -29,9 +29,9 @@ include $(DEVKITARM)/3ds_rules #--------------------------------------------------------------------------------- TARGET := ctruLua BUILD := build -SOURCES := source libs/lua-5.3.1/src +SOURCES := source libs/lua-5.3.2/src DATA := data -INCLUDES := include libs/lua-5.3.1/src libs/lzlib +INCLUDES := include libs/lua-5.3.2/src libs/lzlib #ROMFS := romfs APP_TITLE := ctruLua diff --git a/libs/lua-5.3.1/src/ltablib.c b/libs/lua-5.3.1/src/ltablib.c deleted file mode 100644 index a05c885..0000000 --- a/libs/lua-5.3.1/src/ltablib.c +++ /dev/null @@ -1,359 +0,0 @@ -/* -** $Id: ltablib.c,v 1.80 2015/01/13 16:27:29 roberto Exp $ -** Library for Table Manipulation -** See Copyright Notice in lua.h -*/ - -#define ltablib_c -#define LUA_LIB - -#include "lprefix.h" - - -#include -#include - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - - -/* -** Structure with table-access functions -*/ -typedef struct { - int (*geti) (lua_State *L, int idx, lua_Integer n); - void (*seti) (lua_State *L, int idx, lua_Integer n); -} TabA; - - -/* -** Check that 'arg' has a table and set access functions in 'ta' to raw -** or non-raw according to the presence of corresponding metamethods. -*/ -static void checktab (lua_State *L, int arg, TabA *ta) { - ta->geti = NULL; ta->seti = NULL; - if (lua_getmetatable(L, arg)) { - lua_pushliteral(L, "__index"); /* 'index' metamethod */ - if (lua_rawget(L, -2) != LUA_TNIL) - ta->geti = lua_geti; - lua_pushliteral(L, "__newindex"); /* 'newindex' metamethod */ - if (lua_rawget(L, -3) != LUA_TNIL) - ta->seti = lua_seti; - lua_pop(L, 3); /* pop metatable plus both metamethods */ - } - if (ta->geti == NULL || ta->seti == NULL) { - luaL_checktype(L, arg, LUA_TTABLE); /* must be table for raw methods */ - if (ta->geti == NULL) ta->geti = lua_rawgeti; - if (ta->seti == NULL) ta->seti = lua_rawseti; - } -} - - -#define aux_getn(L,n,ta) (checktab(L, n, ta), luaL_len(L, n)) - - -#if defined(LUA_COMPAT_MAXN) -static int maxn (lua_State *L) { - lua_Number max = 0; - luaL_checktype(L, 1, LUA_TTABLE); - lua_pushnil(L); /* first key */ - while (lua_next(L, 1)) { - lua_pop(L, 1); /* remove value */ - if (lua_type(L, -1) == LUA_TNUMBER) { - lua_Number v = lua_tonumber(L, -1); - if (v > max) max = v; - } - } - lua_pushnumber(L, max); - return 1; -} -#endif - - -static int tinsert (lua_State *L) { - TabA ta; - lua_Integer e = aux_getn(L, 1, &ta) + 1; /* first empty element */ - lua_Integer pos; /* where to insert new element */ - switch (lua_gettop(L)) { - case 2: { /* called with only 2 arguments */ - pos = e; /* insert new element at the end */ - break; - } - case 3: { - lua_Integer i; - pos = luaL_checkinteger(L, 2); /* 2nd argument is the position */ - luaL_argcheck(L, 1 <= pos && pos <= e, 2, "position out of bounds"); - for (i = e; i > pos; i--) { /* move up elements */ - (*ta.geti)(L, 1, i - 1); - (*ta.seti)(L, 1, i); /* t[i] = t[i - 1] */ - } - break; - } - default: { - return luaL_error(L, "wrong number of arguments to 'insert'"); - } - } - (*ta.seti)(L, 1, pos); /* t[pos] = v */ - return 0; -} - - -static int tremove (lua_State *L) { - TabA ta; - lua_Integer size = aux_getn(L, 1, &ta); - lua_Integer pos = luaL_optinteger(L, 2, size); - if (pos != size) /* validate 'pos' if given */ - luaL_argcheck(L, 1 <= pos && pos <= size + 1, 1, "position out of bounds"); - (*ta.geti)(L, 1, pos); /* result = t[pos] */ - for ( ; pos < size; pos++) { - (*ta.geti)(L, 1, pos + 1); - (*ta.seti)(L, 1, pos); /* t[pos] = t[pos + 1] */ - } - lua_pushnil(L); - (*ta.seti)(L, 1, pos); /* t[pos] = nil */ - return 1; -} - - -static int tmove (lua_State *L) { - TabA ta; - lua_Integer f = luaL_checkinteger(L, 2); - lua_Integer e = luaL_checkinteger(L, 3); - lua_Integer t = luaL_checkinteger(L, 4); - int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */ - if (e >= f) { /* otherwise, nothing to move */ - lua_Integer n, i; - ta.geti = (luaL_getmetafield(L, 1, "__index") == LUA_TNIL) - ? (luaL_checktype(L, 1, LUA_TTABLE), lua_rawgeti) - : lua_geti; - ta.seti = (luaL_getmetafield(L, tt, "__newindex") == LUA_TNIL) - ? (luaL_checktype(L, tt, LUA_TTABLE), lua_rawseti) - : lua_seti; - luaL_argcheck(L, f > 0 || e < LUA_MAXINTEGER + f, 3, - "too many elements to move"); - n = e - f + 1; /* number of elements to move */ - luaL_argcheck(L, t <= LUA_MAXINTEGER - n + 1, 4, - "destination wrap around"); - if (t > f) { - for (i = n - 1; i >= 0; i--) { - (*ta.geti)(L, 1, f + i); - (*ta.seti)(L, tt, t + i); - } - } - else { - for (i = 0; i < n; i++) { - (*ta.geti)(L, 1, f + i); - (*ta.seti)(L, tt, t + i); - } - } - } - lua_pushvalue(L, tt); /* return "to table" */ - return 1; -} - - -static void addfield (lua_State *L, luaL_Buffer *b, TabA *ta, lua_Integer i) { - (*ta->geti)(L, 1, i); - if (!lua_isstring(L, -1)) - luaL_error(L, "invalid value (%s) at index %d in table for 'concat'", - luaL_typename(L, -1), i); - luaL_addvalue(b); -} - - -static int tconcat (lua_State *L) { - TabA ta; - luaL_Buffer b; - size_t lsep; - lua_Integer i, last; - const char *sep = luaL_optlstring(L, 2, "", &lsep); - checktab(L, 1, &ta); - i = luaL_optinteger(L, 3, 1); - last = luaL_opt(L, luaL_checkinteger, 4, luaL_len(L, 1)); - luaL_buffinit(L, &b); - for (; i < last; i++) { - addfield(L, &b, &ta, i); - luaL_addlstring(&b, sep, lsep); - } - if (i == last) /* add last value (if interval was not empty) */ - addfield(L, &b, &ta, i); - luaL_pushresult(&b); - return 1; -} - - -/* -** {====================================================== -** Pack/unpack -** ======================================================= -*/ - -static int pack (lua_State *L) { - int i; - int n = lua_gettop(L); /* number of elements to pack */ - lua_createtable(L, n, 1); /* create result table */ - lua_insert(L, 1); /* put it at index 1 */ - for (i = n; i >= 1; i--) /* assign elements */ - lua_rawseti(L, 1, i); - lua_pushinteger(L, n); - lua_setfield(L, 1, "n"); /* t.n = number of elements */ - return 1; /* return table */ -} - - -static int unpack (lua_State *L) { - TabA ta; - lua_Integer i, e; - lua_Unsigned n; - checktab(L, 1, &ta); - i = luaL_optinteger(L, 2, 1); - e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1)); - if (i > e) return 0; /* empty range */ - n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */ - if (n >= (unsigned int)INT_MAX || !lua_checkstack(L, (int)(++n))) - return luaL_error(L, "too many results to unpack"); - do { /* must have at least one element */ - (*ta.geti)(L, 1, i); /* push arg[i..e] */ - } while (i++ < e); - - return (int)n; -} - -/* }====================================================== */ - - - -/* -** {====================================================== -** Quicksort -** (based on 'Algorithms in MODULA-3', Robert Sedgewick; -** Addison-Wesley, 1993.) -** ======================================================= -*/ - - -static void set2 (lua_State *L, TabA *ta, int i, int j) { - (*ta->seti)(L, 1, i); - (*ta->seti)(L, 1, j); -} - -static int sort_comp (lua_State *L, int a, int b) { - if (!lua_isnil(L, 2)) { /* function? */ - int res; - lua_pushvalue(L, 2); - lua_pushvalue(L, a-1); /* -1 to compensate function */ - lua_pushvalue(L, b-2); /* -2 to compensate function and 'a' */ - lua_call(L, 2, 1); - res = lua_toboolean(L, -1); - lua_pop(L, 1); - return res; - } - else /* a < b? */ - return lua_compare(L, a, b, LUA_OPLT); -} - -static void auxsort (lua_State *L, TabA *ta, int l, int u) { - while (l < u) { /* for tail recursion */ - int i, j; - /* sort elements a[l], a[(l+u)/2] and a[u] */ - (*ta->geti)(L, 1, l); - (*ta->geti)(L, 1, u); - if (sort_comp(L, -1, -2)) /* a[u] < a[l]? */ - set2(L, ta, l, u); /* swap a[l] - a[u] */ - else - lua_pop(L, 2); - if (u-l == 1) break; /* only 2 elements */ - i = (l+u)/2; - (*ta->geti)(L, 1, i); - (*ta->geti)(L, 1, l); - if (sort_comp(L, -2, -1)) /* a[i]geti)(L, 1, u); - if (sort_comp(L, -1, -2)) /* a[u]geti)(L, 1, i); /* Pivot */ - lua_pushvalue(L, -1); - (*ta->geti)(L, 1, u-1); - set2(L, ta, i, u-1); - /* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */ - i = l; j = u-1; - for (;;) { /* invariant: a[l..i] <= P <= a[j..u] */ - /* repeat ++i until a[i] >= P */ - while ((*ta->geti)(L, 1, ++i), sort_comp(L, -1, -2)) { - if (i>=u) luaL_error(L, "invalid order function for sorting"); - lua_pop(L, 1); /* remove a[i] */ - } - /* repeat --j until a[j] <= P */ - while ((*ta->geti)(L, 1, --j), sort_comp(L, -3, -1)) { - if (j<=l) luaL_error(L, "invalid order function for sorting"); - lua_pop(L, 1); /* remove a[j] */ - } - if (jgeti)(L, 1, u-1); - (*ta->geti)(L, 1, i); - set2(L, ta, u-1, i); /* swap pivot (a[u-1]) with a[i] */ - /* a[l..i-1] <= a[i] == P <= a[i+1..u] */ - /* adjust so that smaller half is in [j..i] and larger one in [l..u] */ - if (i-l < u-i) { - j=l; i=i-1; l=i+2; - } - else { - j=i+1; i=u; u=j-2; - } - auxsort(L, ta, j, i); /* call recursively the smaller one */ - } /* repeat the routine for the larger one */ -} - -static int sort (lua_State *L) { - TabA ta; - int n = (int)aux_getn(L, 1, &ta); - luaL_checkstack(L, 50, ""); /* assume array is smaller than 2^50 */ - if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */ - luaL_checktype(L, 2, LUA_TFUNCTION); - lua_settop(L, 2); /* make sure there are two arguments */ - auxsort(L, &ta, 1, n); - return 0; -} - -/* }====================================================== */ - - -static const luaL_Reg tab_funcs[] = { - {"concat", tconcat}, -#if defined(LUA_COMPAT_MAXN) - {"maxn", maxn}, -#endif - {"insert", tinsert}, - {"pack", pack}, - {"unpack", unpack}, - {"remove", tremove}, - {"move", tmove}, - {"sort", sort}, - {NULL, NULL} -}; - - -LUAMOD_API int luaopen_table (lua_State *L) { - luaL_newlib(L, tab_funcs); -#if defined(LUA_COMPAT_UNPACK) - /* _G.unpack = table.unpack */ - lua_getfield(L, -1, "unpack"); - lua_setglobal(L, "unpack"); -#endif - return 1; -} - diff --git a/libs/lua-5.3.1/src/lvm.h b/libs/lua-5.3.1/src/lvm.h deleted file mode 100644 index 0613826..0000000 --- a/libs/lua-5.3.1/src/lvm.h +++ /dev/null @@ -1,68 +0,0 @@ -/* -** $Id: lvm.h,v 2.35 2015/02/20 14:27:53 roberto Exp $ -** Lua virtual machine -** See Copyright Notice in lua.h -*/ - -#ifndef lvm_h -#define lvm_h - - -#include "ldo.h" -#include "lobject.h" -#include "ltm.h" - - -#if !defined(LUA_NOCVTN2S) -#define cvt2str(o) ttisnumber(o) -#else -#define cvt2str(o) 0 /* no conversion from numbers to strings */ -#endif - - -#if !defined(LUA_NOCVTS2N) -#define cvt2num(o) ttisstring(o) -#else -#define cvt2num(o) 0 /* no conversion from strings to numbers */ -#endif - - -/* -** You can define LUA_FLOORN2I if you want to convert floats to integers -** by flooring them (instead of raising an error if they are not -** integral values) -*/ -#if !defined(LUA_FLOORN2I) -#define LUA_FLOORN2I 0 -#endif - - -#define tonumber(o,n) \ - (ttisfloat(o) ? (*(n) = fltvalue(o), 1) : luaV_tonumber_(o,n)) - -#define tointeger(o,i) \ - (ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o,i,LUA_FLOORN2I)) - -#define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2)) - -#define luaV_rawequalobj(t1,t2) luaV_equalobj(NULL,t1,t2) - - -LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2); -LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r); -LUAI_FUNC int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r); -LUAI_FUNC int luaV_tonumber_ (const TValue *obj, lua_Number *n); -LUAI_FUNC int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode); -LUAI_FUNC void luaV_gettable (lua_State *L, const TValue *t, TValue *key, - StkId val); -LUAI_FUNC void luaV_settable (lua_State *L, const TValue *t, TValue *key, - StkId val); -LUAI_FUNC void luaV_finishOp (lua_State *L); -LUAI_FUNC void luaV_execute (lua_State *L); -LUAI_FUNC void luaV_concat (lua_State *L, int total); -LUAI_FUNC lua_Integer luaV_div (lua_State *L, lua_Integer x, lua_Integer y); -LUAI_FUNC lua_Integer luaV_mod (lua_State *L, lua_Integer x, lua_Integer y); -LUAI_FUNC lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y); -LUAI_FUNC void luaV_objlen (lua_State *L, StkId ra, const TValue *rb); - -#endif diff --git a/libs/lua-5.3.1/Makefile b/libs/lua-5.3.2/Makefile similarity index 99% rename from libs/lua-5.3.1/Makefile rename to libs/lua-5.3.2/Makefile index 5ee5601..e87e958 100644 --- a/libs/lua-5.3.1/Makefile +++ b/libs/lua-5.3.2/Makefile @@ -46,7 +46,7 @@ TO_MAN= lua.1 luac.1 # Lua version and release. V= 5.3 -R= $V.1 +R= $V.2 # Targets start here. all: $(PLAT) diff --git a/libs/lua-5.3.1/README b/libs/lua-5.3.2/README similarity index 70% rename from libs/lua-5.3.1/README rename to libs/lua-5.3.2/README index d6ae660..eb6247a 100644 --- a/libs/lua-5.3.1/README +++ b/libs/lua-5.3.2/README @@ -1,5 +1,5 @@ -This is Lua 5.3.1, released on 10 Jun 2015. +This is Lua 5.3.2, released on 25 Nov 2015. For installation instructions, license details, and further information about Lua, see doc/readme.html. diff --git a/libs/lua-5.3.1/doc/contents.html b/libs/lua-5.3.2/doc/contents.html similarity index 100% rename from libs/lua-5.3.1/doc/contents.html rename to libs/lua-5.3.2/doc/contents.html diff --git a/libs/lua-5.3.1/doc/index.css b/libs/lua-5.3.2/doc/index.css similarity index 100% rename from libs/lua-5.3.1/doc/index.css rename to libs/lua-5.3.2/doc/index.css diff --git a/libs/lua-5.3.1/doc/logo.gif b/libs/lua-5.3.2/doc/logo.gif similarity index 100% rename from libs/lua-5.3.1/doc/logo.gif rename to libs/lua-5.3.2/doc/logo.gif diff --git a/libs/lua-5.3.1/doc/lua.1 b/libs/lua-5.3.2/doc/lua.1 similarity index 100% rename from libs/lua-5.3.1/doc/lua.1 rename to libs/lua-5.3.2/doc/lua.1 diff --git a/libs/lua-5.3.1/doc/lua.css b/libs/lua-5.3.2/doc/lua.css similarity index 85% rename from libs/lua-5.3.1/doc/lua.css rename to libs/lua-5.3.2/doc/lua.css index b614f3c..eb20fec 100644 --- a/libs/lua-5.3.1/doc/lua.css +++ b/libs/lua-5.3.2/doc/lua.css @@ -131,3 +131,29 @@ table.columns td { p.logos a:link:hover, p.logos a:visited:hover { background-color: inherit ; } + +table.book { + border: none ; + border-spacing: 0 ; + border-collapse: collapse ; +} + +table.book td { + padding: 0 ; + vertical-align: top ; +} + +table.book td.cover { + padding-right: 1em ; +} + +table.book img { + border: solid #000080 1px ; +} + +table.book span { + font-size: small ; + text-align: left ; + display: block ; + margin-top: 0.25em ; +} diff --git a/libs/lua-5.3.1/doc/luac.1 b/libs/lua-5.3.2/doc/luac.1 similarity index 100% rename from libs/lua-5.3.1/doc/luac.1 rename to libs/lua-5.3.2/doc/luac.1 diff --git a/libs/lua-5.3.1/doc/manual.css b/libs/lua-5.3.2/doc/manual.css similarity index 100% rename from libs/lua-5.3.1/doc/manual.css rename to libs/lua-5.3.2/doc/manual.css diff --git a/libs/lua-5.3.1/doc/manual.html b/libs/lua-5.3.2/doc/manual.html similarity index 98% rename from libs/lua-5.3.1/doc/manual.html rename to libs/lua-5.3.2/doc/manual.html index f2eb313..60c4c98 100644 --- a/libs/lua-5.3.1/doc/manual.html +++ b/libs/lua-5.3.2/doc/manual.html @@ -35,7 +35,7 @@ Freely available under the terms of the

- + @@ -398,7 +398,7 @@ You can replace the metatable of tables using the setmetatable function. You cannot change the metatable of other types from Lua code (except by using the debug library (§6.10)); -you must use the C API for that. +you should use the C API for that.

@@ -589,7 +589,7 @@ The result of the call is always converted to a boolean. the <= (less equal) operation. Unlike other operations, -The less-equal operation can use two different events. +the less-equal operation can use two different events. First, Lua looks for the "__le" metamethod in both operands, like in the "lt" operation. If it cannot find such a metamethod, @@ -1051,7 +1051,8 @@ except as delimiters between names and keywords. (also called identifiers) in Lua can be any string of letters, digits, and underscores, -not beginning with a digit. +not beginning with a digit and +not being a reserved word. Identifiers are used to name variables, table fields, and labels. @@ -2706,7 +2707,9 @@ The first upvalue associated with a function is at index lua_upvalueindex(1), and so on. Any access to lua_upvalueindex(n), where n is greater than the number of upvalues of the -current function (but not greater than 256), +current function +(but not greater than 256, +which is one plus the maximum number of upvalues in a closure), produces an acceptable but invalid index. @@ -2971,6 +2974,7 @@ by looking only at its arguments The third field, x, tells whether the function may raise errors: '-' means the function never raises any error; +'m' means the function may raise memory errors; 'e' means the function may raise errors; 'v' means the function may raise an error on purpose. @@ -3143,7 +3147,8 @@ The function results are pushed onto the stack when the function returns. The number of results is adjusted to nresults, unless nresults is LUA_MULTRET. In this case, all results from the function are pushed. -Lua takes care that the returned values fit into the stack space. +Lua takes care that the returned values fit into the stack space, +but it does not ensure any extra space in the stack. The function results are pushed onto the stack in direct order (the first result is pushed first), so that after the call the last result is on the top of the stack. @@ -3253,14 +3258,15 @@ of numeric arguments and returns their average and their sum:

int lua_checkstack (lua_State *L, int n);

-Ensures that the stack has space for at least n extra slots. +Ensures that the stack has space for at least n extra slots +(that is, that you can safely push up to n values into it). It returns false if it cannot fulfill the request, either because it would cause the stack to be larger than a fixed maximum size (typically at least several thousand elements) or because it cannot allocate memory for the extra space. This function never shrinks the stack; -if the stack is already larger than the new size, +if the stack already has space for the extra slots, it is left unchanged. @@ -3345,7 +3351,7 @@ Values at other positions are not affected.


lua_createtable

-[-0, +1, e] +[-0, +1, m]

void lua_createtable (lua_State *L, int narr, int nrec);

@@ -3355,7 +3361,7 @@ will have as a sequence; parameter nrec is a hint for how many other elements the table will have. Lua may use these hints to preallocate memory for the new table. -This pre-allocation is useful for performance when you know in advance +This preallocation is useful for performance when you know in advance how many elements the table will have. Otherwise you can use the function lua_newtable. @@ -3364,7 +3370,7 @@ Otherwise you can use the function lua_newtable

lua_dump

-[-0, +0, e] +[-0, +0, –]

int lua_dump (lua_State *L,
                         lua_Writer writer,
                         void *data,
@@ -3978,7 +3984,7 @@ passes to the allocator in every call.
 
 
 

lua_newtable

-[-0, +1, e] +[-0, +1, m]

void lua_newtable (lua_State *L);

@@ -3990,7 +3996,7 @@ It is equivalent to lua_createtable(L, 0, 0).


lua_newthread

-[-0, +1, e] +[-0, +1, m]

lua_State *lua_newthread (lua_State *L);

@@ -4011,7 +4017,7 @@ like any Lua object.


lua_newuserdata

-[-0, +1, e] +[-0, +1, m]

void *lua_newuserdata (lua_State *L, size_t size);

@@ -4221,7 +4227,7 @@ Pushes a boolean value with value b onto the stack.


lua_pushcclosure

-[-n, +1, e] +[-n, +1, m]

void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);

@@ -4278,7 +4284,7 @@ and return its results (see lua_CFunction<


lua_pushfstring

-[-0, +1, e] +[-0, +1, m]

const char *lua_pushfstring (lua_State *L, const char *fmt, ...);

@@ -4358,7 +4364,7 @@ light userdata with the same C address.


lua_pushliteral

-[-0, +1, e] +[-0, +1, m]

const char *lua_pushliteral (lua_State *L, const char *s);

@@ -4370,7 +4376,7 @@ but should be used only when s is a literal string.


lua_pushlstring

-[-0, +1, e] +[-0, +1, m]

const char *lua_pushlstring (lua_State *L, const char *s, size_t len);

@@ -4413,7 +4419,7 @@ Pushes a float with value n onto the stack.


lua_pushstring

-[-0, +1, e] +[-0, +1, m]

const char *lua_pushstring (lua_State *L, const char *s);

@@ -4460,7 +4466,7 @@ onto the stack.


lua_pushvfstring

-[-0, +1, e] +[-0, +1, m]

const char *lua_pushvfstring (lua_State *L,
                               const char *fmt,
                               va_list argp);
@@ -4555,7 +4561,7 @@ for other values, it is 0.

lua_rawset

-[-2, +0, e] +[-2, +0, m]

void lua_rawset (lua_State *L, int index);

@@ -4567,7 +4573,7 @@ Similar to lua_settable, but does a raw


lua_rawseti

-[-1, +0, e] +[-1, +0, m]

void lua_rawseti (lua_State *L, int index, lua_Integer i);

@@ -4586,7 +4592,7 @@ that is, it does not invoke metamethods.


lua_rawsetp

-[-1, +0, e] +[-1, +0, m]

void lua_rawsetp (lua_State *L, int index, const void *p);

@@ -4989,13 +4995,13 @@ indicates whether the operation succeeded.


lua_tolstring

-[-0, +0, e] +[-0, +0, m]

const char *lua_tolstring (lua_State *L, int index, size_t *len);

Converts the Lua value at the given index to a C string. If len is not NULL, -it also sets *len with the string length. +it sets *len with the string length. The Lua value must be a string or a number; otherwise, the function returns NULL. If the value is a number, @@ -5006,7 +5012,7 @@ when lua_tolstring is applied to keys during a table traversal.)

-lua_tolstring returns a fully aligned pointer +lua_tolstring returns a pointer to a string inside the Lua state. This string always has a zero ('\0') after its last character (as in C), @@ -5075,7 +5081,7 @@ Typically this function is used only for hashing and debug information.


lua_tostring

-[-0, +0, e] +[-0, +0, m]

const char *lua_tostring (lua_State *L, int index);

@@ -5884,7 +5890,7 @@ in alphabetical order.


luaL_addchar

-[-?, +?, e] +[-?, +?, m]

void luaL_addchar (luaL_Buffer *B, char c);

@@ -5896,7 +5902,7 @@ Adds the byte c to the buffer B


luaL_addlstring

-[-?, +?, e] +[-?, +?, m]

void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l);

@@ -5910,7 +5916,7 @@ The string can contain embedded zeros.


luaL_addsize

-[-?, +?, e] +[-?, +?, –]

void luaL_addsize (luaL_Buffer *B, size_t n);

@@ -5923,7 +5929,7 @@ buffer area (see luaL_prepbuffer).


luaL_addstring

-[-?, +?, e] +[-?, +?, m]

void luaL_addstring (luaL_Buffer *B, const char *s);

@@ -5936,7 +5942,7 @@ to the buffer B


luaL_addvalue

-[-1, +?, e] +[-1, +?, m]

void luaL_addvalue (luaL_Buffer *B);

@@ -6074,7 +6080,7 @@ the buffer must be declared as a variable


luaL_buffinitsize

-[-?, +?, e] +[-?, +?, m]

char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz);

@@ -6325,7 +6331,7 @@ as return luaL_error(args).


luaL_execresult

-[-0, +3, e] +[-0, +3, m]

int luaL_execresult (lua_State *L, int stat);

@@ -6338,7 +6344,7 @@ process-related functions in the standard library


luaL_fileresult

-[-0, +(1|3), e] +[-0, +(1|3), m]

int luaL_fileresult (lua_State *L, int stat, const char *fname);

@@ -6351,7 +6357,7 @@ file-related functions in the standard library


luaL_getmetafield

-[-0, +(0|1), e] +[-0, +(0|1), m]

int luaL_getmetafield (lua_State *L, int obj, const char *e);

@@ -6366,7 +6372,7 @@ pushes nothing and returns LUA_TNIL.


luaL_getmetatable

-[-0, +1, –] +[-0, +1, m]

int luaL_getmetatable (lua_State *L, const char *tname);

@@ -6396,7 +6402,7 @@ and false if it creates a new table.


luaL_gsub

-[-0, +1, e] +[-0, +1, m]

const char *luaL_gsub (lua_State *L,
                        const char *s,
                        const char *p,
@@ -6531,7 +6537,7 @@ it does not run it.
 
 
 

luaL_newlib

-[-0, +1, e] +[-0, +1, m]

void luaL_newlib (lua_State *L, const luaL_Reg l[]);

@@ -6553,7 +6559,7 @@ not a pointer to it.


luaL_newlibtable

-[-0, +1, e] +[-0, +1, m]

void luaL_newlibtable (lua_State *L, const luaL_Reg l[]);

@@ -6574,7 +6580,7 @@ not a pointer to it.


luaL_newmetatable

-[-0, +1, e] +[-0, +1, m]

int luaL_newmetatable (lua_State *L, const char *tname);

@@ -6664,6 +6670,9 @@ Otherwise, raises an error.

If l is not NULL, fills the position *l with the result's length. +If the result is NULL +(only possible when returning d and d == NULL), +its length is considered zero. @@ -6702,7 +6711,7 @@ Otherwise, raises an error.


luaL_prepbuffer

-[-?, +?, e] +[-?, +?, m]

char *luaL_prepbuffer (luaL_Buffer *B);

@@ -6714,7 +6723,7 @@ with the predefined size LUAL_BUFFERSIZE

luaL_prepbuffsize

-[-?, +?, e] +[-?, +?, m]

char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz);

@@ -6730,7 +6739,7 @@ it to the buffer.


luaL_pushresult

-[-?, +1, e] +[-?, +1, m]

void luaL_pushresult (luaL_Buffer *B);

@@ -6742,7 +6751,7 @@ the top of the stack.


luaL_pushresultsize

-[-?, +1, e] +[-?, +1, m]

void luaL_pushresultsize (luaL_Buffer *B, size_t sz);

@@ -6753,7 +6762,7 @@ Equivalent to the sequence luaL_addsize


luaL_ref

-[-1, +0, e] +[-1, +0, m]

int luaL_ref (lua_State *L, int t);

@@ -6824,7 +6833,7 @@ Leaves a copy of the module on the stack.


luaL_setfuncs

-[-nup, +0, e] +[-nup, +0, m]

void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup);

@@ -6888,7 +6897,7 @@ this function receives the file handle as its sole argument and must return either true (in case of success) or nil plus an error message (in case of error). Once Lua calls this field, -the field value is changed to NULL +it changes the field value to NULL to signal that the handle is closed. @@ -6896,7 +6905,7 @@ to signal that the handle is closed.


luaL_testudata

-[-0, +0, e] +[-0, +0, m]

void *luaL_testudata (lua_State *L, int arg, const char *tname);

@@ -6932,7 +6941,7 @@ and uses the result of the call as its result.


luaL_traceback

-[-0, +1, e] +[-0, +1, m]

void luaL_traceback (lua_State *L, lua_State *L1, const char *msg,
                      int level);
@@ -6979,7 +6988,7 @@ If ref is LUA_NOREF or

luaL_where

-[-0, +1, e] +[-0, +1, m]

void luaL_where (lua_State *L, int lvl);

@@ -7476,7 +7485,8 @@ and select returns the total number of extra arguments it received.

Sets the metatable for the given table. -(You cannot change the metatable of other types from Lua, only from C.) +(To change the metatable of other types from Lua code, +you must use the debug library (§6.10).) If metatable is nil, removes the metatable of the given table. If the original metatable has a "__metatable" field, @@ -7557,8 +7567,11 @@ and "userdata".


_VERSION

+ + +

A global variable (not a function) that -holds a string containing the current interpreter version. +holds a string containing the running Lua version. The current value of this variable is "Lua 5.3". @@ -8194,9 +8207,11 @@ Options c, d, i, o, u, X, and x expect an integer. Option q expects a string. -Option s expects a string without embedded zeros; +Option s expects a string; if its argument is not a string, it is converted to one following the same rules of tostring. +If the option has any modifier (flags, width, length), +the string argument should not contain embedded zeros.

@@ -8392,6 +8407,11 @@ The default value for sep is the empty string Returns the empty string if n is not positive. +

+(Note that it is very easy to exhaust the memory of your machine +with a single call to this function.) + +

@@ -8963,14 +8983,23 @@ If comp is given, then it must be a function that receives two list elements and returns true when the first element must come before the second in the final order -(so that not comp(list[i+1],list[i]) will be true after the sort). +(so that, after the sort, +i < j implies not comp(list[j],list[i])). If comp is not given, then the standard Lua operator < is used instead. +

+Note that the comp function must define +a strict partial order over the elements in the list; +that is, it must be asymmetric and transitive. +Otherwise, no valid sort may be possible. + +

The sort algorithm is not stable; -that is, elements considered equal by the given order +that is, elements not comparable by the given order +(e.g., equal elements) may have their relative positions changed by the sort. @@ -9222,14 +9251,13 @@ in the range [0,1). When called with two integers m and n, math.random returns a pseudo-random integer with uniform distribution in the range [m, n]. -(The value m-n cannot be negative and must fit in a Lua integer.) +(The value n-m cannot be negative and must fit in a Lua integer.) The call math.random(n) is equivalent to math.random(1,n).

This function is an interface to the underling pseudo-random generator function provided by C. -No guarantees can be given for its statistical properties. @@ -9397,7 +9425,7 @@ instead of returning an error code.

-


io.lines ([filename ···])

+

io.lines ([filename, ···])

@@ -9771,7 +9799,7 @@ then the date is formatted in Coordinated Universal Time. After this optional character, if format is the string "*t", then date returns a table with the following fields: -year (four digits), month (1–12), day (1–31), +year, month (1–12), day (1–31), hour (0–23), min (0–59), sec (0–61), wday (weekday, Sunday is 1), yday (day of the year), @@ -9789,8 +9817,8 @@ formatted according to the same rules as the ISO C function strftime<

When called without arguments, date returns a reasonable date and time representation that depends on -the host system and on the current locale -(that is, os.date() is equivalent to os.date("%c")). +the host system and on the current locale. +(More specifically, os.date() is equivalent to os.date("%c").)

@@ -10797,10 +10825,10 @@ and LiteralString, see §3.1.)

diff --git a/libs/lua-5.3.1/doc/osi-certified-72x60.png b/libs/lua-5.3.2/doc/osi-certified-72x60.png similarity index 100% rename from libs/lua-5.3.1/doc/osi-certified-72x60.png rename to libs/lua-5.3.2/doc/osi-certified-72x60.png diff --git a/libs/lua-5.3.1/doc/readme.html b/libs/lua-5.3.2/doc/readme.html similarity index 100% rename from libs/lua-5.3.1/doc/readme.html rename to libs/lua-5.3.2/doc/readme.html diff --git a/libs/lua-5.3.1/src/Makefile b/libs/lua-5.3.2/src/Makefile similarity index 100% rename from libs/lua-5.3.1/src/Makefile rename to libs/lua-5.3.2/src/Makefile diff --git a/libs/lua-5.3.1/src/lapi.c b/libs/lua-5.3.2/src/lapi.c similarity index 91% rename from libs/lua-5.3.1/src/lapi.c rename to libs/lua-5.3.2/src/lapi.c index fea9eb2..9a6a3fb 100644 --- a/libs/lua-5.3.1/src/lapi.c +++ b/libs/lua-5.3.2/src/lapi.c @@ -1,5 +1,5 @@ /* -** $Id: lapi.c,v 2.249 2015/04/06 12:23:48 roberto Exp $ +** $Id: lapi.c,v 2.257 2015/11/02 18:48:07 roberto Exp $ ** Lua API ** See Copyright Notice in lua.h */ @@ -121,11 +121,11 @@ LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) { lua_lock(to); api_checknelems(from, n); api_check(from, G(from) == G(to), "moving among independent states"); - api_check(from, to->ci->top - to->top >= n, "not enough elements to move"); + api_check(from, to->ci->top - to->top >= n, "stack overflow"); from->top -= n; for (i = 0; i < n; i++) { setobj2s(to, to->top, from->top + i); - api_incr_top(to); + to->top++; /* stack already checked by previous 'api_check' */ } lua_unlock(to); } @@ -471,11 +471,16 @@ LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) { } +/* +** Pushes on the stack a string with given length. Avoid using 's' when +** 'len' == 0 (as 's' can be NULL in that case), due to later use of +** 'memcmp' and 'memcpy'. +*/ LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { TString *ts; lua_lock(L); luaC_checkGC(L); - ts = luaS_newlstr(L, s, len); + ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len); setsvalue2s(L, L->top, ts); api_incr_top(L); lua_unlock(L); @@ -579,19 +584,30 @@ LUA_API int lua_pushthread (lua_State *L) { */ -LUA_API int lua_getglobal (lua_State *L, const char *name) { - Table *reg = hvalue(&G(L)->l_registry); - const TValue *gt; /* global table */ - lua_lock(L); - gt = luaH_getint(reg, LUA_RIDX_GLOBALS); - setsvalue2s(L, L->top, luaS_new(L, name)); - api_incr_top(L); - luaV_gettable(L, gt, L->top - 1, L->top - 1); +static int auxgetstr (lua_State *L, const TValue *t, const char *k) { + const TValue *aux; + TString *str = luaS_new(L, k); + if (luaV_fastget(L, t, str, aux, luaH_getstr)) { + setobj2s(L, L->top, aux); + api_incr_top(L); + } + else { + setsvalue2s(L, L->top, str); + api_incr_top(L); + luaV_finishget(L, t, L->top - 1, L->top - 1, aux); + } lua_unlock(L); return ttnov(L->top - 1); } +LUA_API int lua_getglobal (lua_State *L, const char *name) { + Table *reg = hvalue(&G(L)->l_registry); + lua_lock(L); + return auxgetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name); +} + + LUA_API int lua_gettable (lua_State *L, int idx) { StkId t; lua_lock(L); @@ -603,24 +619,25 @@ LUA_API int lua_gettable (lua_State *L, int idx) { LUA_API int lua_getfield (lua_State *L, int idx, const char *k) { - StkId t; lua_lock(L); - t = index2addr(L, idx); - setsvalue2s(L, L->top, luaS_new(L, k)); - api_incr_top(L); - luaV_gettable(L, t, L->top - 1, L->top - 1); - lua_unlock(L); - return ttnov(L->top - 1); + return auxgetstr(L, index2addr(L, idx), k); } LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) { StkId t; + const TValue *aux; lua_lock(L); t = index2addr(L, idx); - setivalue(L->top, n); - api_incr_top(L); - luaV_gettable(L, t, L->top - 1, L->top - 1); + if (luaV_fastget(L, t, n, aux, luaH_getint)) { + setobj2s(L, L->top, aux); + api_incr_top(L); + } + else { + setivalue(L->top, n); + api_incr_top(L); + luaV_finishget(L, t, L->top - 1, L->top - 1, aux); + } lua_unlock(L); return ttnov(L->top - 1); } @@ -719,18 +736,29 @@ LUA_API int lua_getuservalue (lua_State *L, int idx) { ** set functions (stack -> Lua) */ +/* +** t[k] = value at the top of the stack (where 'k' is a string) +*/ +static void auxsetstr (lua_State *L, const TValue *t, const char *k) { + const TValue *aux; + TString *str = luaS_new(L, k); + api_checknelems(L, 1); + if (luaV_fastset(L, t, str, aux, luaH_getstr, L->top - 1)) + L->top--; /* pop value */ + else { + setsvalue2s(L, L->top, str); /* push 'str' (to make it a TValue) */ + api_incr_top(L); + luaV_finishset(L, t, L->top - 1, L->top - 2, aux); + L->top -= 2; /* pop value and key */ + } + lua_unlock(L); /* lock done by caller */ +} + LUA_API void lua_setglobal (lua_State *L, const char *name) { Table *reg = hvalue(&G(L)->l_registry); - const TValue *gt; /* global table */ - lua_lock(L); - api_checknelems(L, 1); - gt = luaH_getint(reg, LUA_RIDX_GLOBALS); - setsvalue2s(L, L->top, luaS_new(L, name)); - api_incr_top(L); - luaV_settable(L, gt, L->top - 1, L->top - 2); - L->top -= 2; /* pop value and key */ - lua_unlock(L); + lua_lock(L); /* unlock done in 'auxsetstr' */ + auxsetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name); } @@ -746,42 +774,40 @@ LUA_API void lua_settable (lua_State *L, int idx) { LUA_API void lua_setfield (lua_State *L, int idx, const char *k) { - StkId t; - lua_lock(L); - api_checknelems(L, 1); - t = index2addr(L, idx); - setsvalue2s(L, L->top, luaS_new(L, k)); - api_incr_top(L); - luaV_settable(L, t, L->top - 1, L->top - 2); - L->top -= 2; /* pop value and key */ - lua_unlock(L); + lua_lock(L); /* unlock done in 'auxsetstr' */ + auxsetstr(L, index2addr(L, idx), k); } LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) { StkId t; + const TValue *aux; lua_lock(L); api_checknelems(L, 1); t = index2addr(L, idx); - setivalue(L->top, n); - api_incr_top(L); - luaV_settable(L, t, L->top - 1, L->top - 2); - L->top -= 2; /* pop value and key */ + if (luaV_fastset(L, t, n, aux, luaH_getint, L->top - 1)) + L->top--; /* pop value */ + else { + setivalue(L->top, n); + api_incr_top(L); + luaV_finishset(L, t, L->top - 1, L->top - 2, aux); + L->top -= 2; /* pop value and key */ + } lua_unlock(L); } LUA_API void lua_rawset (lua_State *L, int idx) { StkId o; - Table *t; + TValue *slot; lua_lock(L); api_checknelems(L, 2); o = index2addr(L, idx); api_check(L, ttistable(o), "table expected"); - t = hvalue(o); - setobj2t(L, luaH_set(L, t, L->top-2), L->top-1); - invalidateTMcache(t); - luaC_barrierback(L, t, L->top-1); + slot = luaH_set(L, hvalue(o), L->top - 2); + setobj2t(L, slot, L->top - 1); + invalidateTMcache(hvalue(o)); + luaC_barrierback(L, hvalue(o), L->top-1); L->top -= 2; lua_unlock(L); } @@ -789,14 +815,12 @@ LUA_API void lua_rawset (lua_State *L, int idx) { LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) { StkId o; - Table *t; lua_lock(L); api_checknelems(L, 1); o = index2addr(L, idx); api_check(L, ttistable(o), "table expected"); - t = hvalue(o); - luaH_setint(L, t, n, L->top - 1); - luaC_barrierback(L, t, L->top-1); + luaH_setint(L, hvalue(o), n, L->top - 1); + luaC_barrierback(L, hvalue(o), L->top-1); L->top--; lua_unlock(L); } @@ -804,16 +828,15 @@ LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) { LUA_API void lua_rawsetp (lua_State *L, int idx, const void *p) { StkId o; - Table *t; - TValue k; + TValue k, *slot; lua_lock(L); api_checknelems(L, 1); o = index2addr(L, idx); api_check(L, ttistable(o), "table expected"); - t = hvalue(o); setpvalue(&k, cast(void *, p)); - setobj2t(L, luaH_set(L, t, &k), L->top - 1); - luaC_barrierback(L, t, L->top - 1); + slot = luaH_set(L, hvalue(o), &k); + setobj2t(L, slot, L->top - 1); + luaC_barrierback(L, hvalue(o), L->top - 1); L->top--; lua_unlock(L); } @@ -895,10 +918,10 @@ LUA_API void lua_callk (lua_State *L, int nargs, int nresults, if (k != NULL && L->nny == 0) { /* need to prepare continuation? */ L->ci->u.c.k = k; /* save continuation */ L->ci->u.c.ctx = ctx; /* save context */ - luaD_call(L, func, nresults, 1); /* do the call */ + luaD_call(L, func, nresults); /* do the call */ } else /* no continuation or no yieldable */ - luaD_call(L, func, nresults, 0); /* just do the call */ + luaD_callnoyield(L, func, nresults); /* just do the call */ adjustresults(L, nresults); lua_unlock(L); } @@ -916,7 +939,7 @@ struct CallS { /* data to 'f_call' */ static void f_call (lua_State *L, void *ud) { struct CallS *c = cast(struct CallS *, ud); - luaD_call(L, c->func, c->nresults, 0); + luaD_callnoyield(L, c->func, c->nresults); } @@ -954,7 +977,7 @@ LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, L->errfunc = func; setoah(ci->callstatus, L->allowhook); /* save value of 'allowhook' */ ci->callstatus |= CIST_YPCALL; /* function can do error recovery */ - luaD_call(L, c.func, nresults, 1); /* do the call */ + luaD_call(L, c.func, nresults); /* do the call */ ci->callstatus &= ~CIST_YPCALL; L->errfunc = ci->u.c.old_errfunc; status = LUA_OK; /* if it is here, there were no errors */ @@ -1043,7 +1066,7 @@ LUA_API int lua_gc (lua_State *L, int what, int data) { } case LUA_GCSTEP: { l_mem debt = 1; /* =1 to signal that it did an actual step */ - int oldrunning = g->gcrunning; + lu_byte oldrunning = g->gcrunning; g->gcrunning = 1; /* allow GC to run */ if (data == 0) { luaE_setdebt(g, -GCSTEPSIZE); /* to do a "small" step */ diff --git a/libs/lua-5.3.1/src/lapi.h b/libs/lua-5.3.2/src/lapi.h similarity index 100% rename from libs/lua-5.3.1/src/lapi.h rename to libs/lua-5.3.2/src/lapi.h diff --git a/libs/lua-5.3.1/src/lauxlib.c b/libs/lua-5.3.2/src/lauxlib.c similarity index 92% rename from libs/lua-5.3.1/src/lauxlib.c rename to libs/lua-5.3.2/src/lauxlib.c index b8bace7..5d362c3 100644 --- a/libs/lua-5.3.1/src/lauxlib.c +++ b/libs/lua-5.3.2/src/lauxlib.c @@ -1,5 +1,5 @@ /* -** $Id: lauxlib.c,v 1.280 2015/02/03 17:38:24 roberto Exp $ +** $Id: lauxlib.c,v 1.284 2015/11/19 19:16:22 roberto Exp $ ** Auxiliary functions for building Lua libraries ** See Copyright Notice in lua.h */ @@ -33,8 +33,8 @@ */ -#define LEVELS1 12 /* size of the first part of the stack */ -#define LEVELS2 10 /* size of the second part of the stack */ +#define LEVELS1 10 /* size of the first part of the stack */ +#define LEVELS2 11 /* size of the second part of the stack */ @@ -107,7 +107,7 @@ static void pushfuncname (lua_State *L, lua_Debug *ar) { } -static int countlevels (lua_State *L) { +static int lastlevel (lua_State *L) { lua_Debug ar; int li = 1, le = 1; /* find an upper bound */ @@ -126,14 +126,16 @@ LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, int level) { lua_Debug ar; int top = lua_gettop(L); - int numlevels = countlevels(L1); - int mark = (numlevels > LEVELS1 + LEVELS2) ? LEVELS1 : 0; - if (msg) lua_pushfstring(L, "%s\n", msg); + int last = lastlevel(L1); + int n1 = (last - level > LEVELS1 + LEVELS2) ? LEVELS1 : -1; + if (msg) + lua_pushfstring(L, "%s\n", msg); + luaL_checkstack(L, 10, NULL); lua_pushliteral(L, "stack traceback:"); while (lua_getstack(L1, level++, &ar)) { - if (level == mark) { /* too many levels? */ + if (n1-- == 0) { /* too many levels? */ lua_pushliteral(L, "\n\t..."); /* add a '...' */ - level = numlevels - LEVELS2; /* and skip to last ones */ + level = last - LEVELS2 + 1; /* and skip to last ones */ } else { lua_getinfo(L1, "Slnt", &ar); @@ -289,7 +291,7 @@ LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { if (luaL_getmetatable(L, tname) != LUA_TNIL) /* name already in use? */ return 0; /* leave previous value on top, but return 0 */ lua_pop(L, 1); - lua_newtable(L); /* create metatable */ + lua_createtable(L, 0, 2); /* create metatable */ lua_pushstring(L, tname); lua_setfield(L, -2, "__name"); /* metatable.__name = tname */ lua_pushvalue(L, -1); @@ -435,6 +437,47 @@ LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int arg, ** ======================================================= */ +/* userdata to box arbitrary data */ +typedef struct UBox { + void *box; + size_t bsize; +} UBox; + + +static void *resizebox (lua_State *L, int idx, size_t newsize) { + void *ud; + lua_Alloc allocf = lua_getallocf(L, &ud); + UBox *box = (UBox *)lua_touserdata(L, idx); + void *temp = allocf(ud, box->box, box->bsize, newsize); + if (temp == NULL && newsize > 0) { /* allocation error? */ + resizebox(L, idx, 0); /* free buffer */ + luaL_error(L, "not enough memory for buffer allocation"); + } + box->box = temp; + box->bsize = newsize; + return temp; +} + + +static int boxgc (lua_State *L) { + resizebox(L, 1, 0); + return 0; +} + + +static void *newbox (lua_State *L, size_t newsize) { + UBox *box = (UBox *)lua_newuserdata(L, sizeof(UBox)); + box->box = NULL; + box->bsize = 0; + if (luaL_newmetatable(L, "LUABOX")) { /* creating metatable? */ + lua_pushcfunction(L, boxgc); + lua_setfield(L, -2, "__gc"); /* metatable.__gc = boxgc */ + } + lua_setmetatable(L, -2); + return resizebox(L, -1, newsize); +} + + /* ** check whether buffer is using a userdata on the stack as a temporary ** buffer @@ -455,11 +498,12 @@ LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) { if (newsize < B->n || newsize - B->n < sz) luaL_error(L, "buffer too large"); /* create larger buffer */ - newbuff = (char *)lua_newuserdata(L, newsize * sizeof(char)); - /* move content to new buffer */ - memcpy(newbuff, B->b, B->n * sizeof(char)); if (buffonstack(B)) - lua_remove(L, -2); /* remove old buffer */ + newbuff = (char *)resizebox(L, -1, newsize); + else { /* no buffer yet */ + newbuff = (char *)newbox(L, newsize); + memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */ + } B->b = newbuff; B->size = newsize; } @@ -468,9 +512,11 @@ LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) { LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { - char *b = luaL_prepbuffsize(B, l); - memcpy(b, s, l * sizeof(char)); - luaL_addsize(B, l); + if (l > 0) { /* avoid 'memcpy' when 's' can be NULL */ + char *b = luaL_prepbuffsize(B, l); + memcpy(b, s, l * sizeof(char)); + luaL_addsize(B, l); + } } @@ -482,8 +528,10 @@ LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { LUALIB_API void luaL_pushresult (luaL_Buffer *B) { lua_State *L = B->L; lua_pushlstring(L, B->b, B->n); - if (buffonstack(B)) - lua_remove(L, -2); /* remove old buffer */ + if (buffonstack(B)) { + resizebox(L, -2, 0); /* delete old buffer */ + lua_remove(L, -2); /* remove its header from the stack */ + } } @@ -605,7 +653,7 @@ static int errfile (lua_State *L, const char *what, int fnameindex) { static int skipBOM (LoadF *lf) { - const char *p = "\xEF\xBB\xBF"; /* Utf8 BOM mark */ + const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */ int c; lf->n = 0; do { diff --git a/libs/lua-5.3.1/src/lauxlib.h b/libs/lua-5.3.2/src/lauxlib.h similarity index 98% rename from libs/lua-5.3.1/src/lauxlib.h rename to libs/lua-5.3.2/src/lauxlib.h index 0bac246..ddb7c22 100644 --- a/libs/lua-5.3.1/src/lauxlib.h +++ b/libs/lua-5.3.2/src/lauxlib.h @@ -1,5 +1,5 @@ /* -** $Id: lauxlib.h,v 1.128 2014/10/29 16:11:17 roberto Exp $ +** $Id: lauxlib.h,v 1.129 2015/11/23 11:29:43 roberto Exp $ ** Auxiliary functions for building Lua libraries ** See Copyright Notice in lua.h */ @@ -65,7 +65,7 @@ LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def, LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname); LUALIB_API int (luaL_execresult) (lua_State *L, int stat); -/* pre-defined references */ +/* predefined references */ #define LUA_NOREF (-2) #define LUA_REFNIL (-1) diff --git a/libs/lua-5.3.1/src/lbaselib.c b/libs/lua-5.3.2/src/lbaselib.c similarity index 92% rename from libs/lua-5.3.1/src/lbaselib.c rename to libs/lua-5.3.2/src/lbaselib.c index 9a15124..861823d 100644 --- a/libs/lua-5.3.1/src/lbaselib.c +++ b/libs/lua-5.3.2/src/lbaselib.c @@ -1,5 +1,5 @@ /* -** $Id: lbaselib.c,v 1.310 2015/03/28 19:14:47 roberto Exp $ +** $Id: lbaselib.c,v 1.312 2015/10/29 15:21:04 roberto Exp $ ** Basic library ** See Copyright Notice in lua.h */ @@ -86,8 +86,8 @@ static int luaB_tonumber (lua_State *L) { const char *s; lua_Integer n = 0; /* to avoid warnings */ lua_Integer base = luaL_checkinteger(L, 2); - luaL_checktype(L, 1, LUA_TSTRING); /* before 'luaL_checklstring'! */ - s = luaL_checklstring(L, 1, &l); + luaL_checktype(L, 1, LUA_TSTRING); /* no numbers as strings */ + s = lua_tolstring(L, 1, &l); luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); if (b_str2int(s, (int)base, &n) == s + l) { lua_pushinteger(L, n); @@ -198,12 +198,10 @@ static int luaB_collectgarbage (lua_State *L) { } -/* -** This function has all type names as upvalues, to maximize performance. -*/ static int luaB_type (lua_State *L) { - luaL_checkany(L, 1); - lua_pushvalue(L, lua_upvalueindex(lua_type(L, 1) + 1)); + int t = lua_type(L, 1); + luaL_argcheck(L, t != LUA_TNONE, 1, "value expected"); + lua_pushstring(L, lua_typename(L, t)); return 1; } @@ -243,18 +241,7 @@ static int luaB_pairs (lua_State *L) { /* -** Traversal function for 'ipairs' for raw tables -*/ -static int ipairsaux_raw (lua_State *L) { - lua_Integer i = luaL_checkinteger(L, 2) + 1; - luaL_checktype(L, 1, LUA_TTABLE); - lua_pushinteger(L, i); - return (lua_rawgeti(L, 1, i) == LUA_TNIL) ? 1 : 2; -} - - -/* -** Traversal function for 'ipairs' for tables with metamethods +** Traversal function for 'ipairs' */ static int ipairsaux (lua_State *L) { lua_Integer i = luaL_checkinteger(L, 2) + 1; @@ -269,13 +256,11 @@ static int ipairsaux (lua_State *L) { ** that can affect the traversal. */ static int luaB_ipairs (lua_State *L) { - lua_CFunction iter = (luaL_getmetafield(L, 1, "__index") != LUA_TNIL) - ? ipairsaux : ipairsaux_raw; #if defined(LUA_COMPAT_IPAIRS) - return pairsmeta(L, "__ipairs", 1, iter); + return pairsmeta(L, "__ipairs", 1, ipairsaux); #else luaL_checkany(L, 1); - lua_pushcfunction(L, iter); /* iteration function */ + lua_pushcfunction(L, ipairsaux); /* iteration function */ lua_pushvalue(L, 1); /* state */ lua_pushinteger(L, 0); /* initial value */ return 3; @@ -490,9 +475,9 @@ static const luaL_Reg base_funcs[] = { {"setmetatable", luaB_setmetatable}, {"tonumber", luaB_tonumber}, {"tostring", luaB_tostring}, + {"type", luaB_type}, {"xpcall", luaB_xpcall}, /* placeholders */ - {"type", NULL}, {"_G", NULL}, {"_VERSION", NULL}, {NULL, NULL} @@ -500,7 +485,6 @@ static const luaL_Reg base_funcs[] = { LUAMOD_API int luaopen_base (lua_State *L) { - int i; /* open lib into global table */ lua_pushglobaltable(L); luaL_setfuncs(L, base_funcs, 0); @@ -510,11 +494,6 @@ LUAMOD_API int luaopen_base (lua_State *L) { /* set global _VERSION */ lua_pushliteral(L, LUA_VERSION); lua_setfield(L, -2, "_VERSION"); - /* set function 'type' with proper upvalues */ - for (i = 0; i < LUA_NUMTAGS; i++) /* push all type names as upvalues */ - lua_pushstring(L, lua_typename(L, i)); - lua_pushcclosure(L, luaB_type, LUA_NUMTAGS); - lua_setfield(L, -2, "type"); return 1; } diff --git a/libs/lua-5.3.1/src/lbitlib.c b/libs/lua-5.3.2/src/lbitlib.c similarity index 80% rename from libs/lua-5.3.1/src/lbitlib.c rename to libs/lua-5.3.2/src/lbitlib.c index 15d5f0c..1cb1d5b 100644 --- a/libs/lua-5.3.1/src/lbitlib.c +++ b/libs/lua-5.3.2/src/lbitlib.c @@ -1,5 +1,5 @@ /* -** $Id: lbitlib.c,v 1.28 2014/11/02 19:19:04 roberto Exp $ +** $Id: lbitlib.c,v 1.30 2015/11/11 19:08:09 roberto Exp $ ** Standard library for bitwise operations ** See Copyright Notice in lua.h */ @@ -19,6 +19,10 @@ #if defined(LUA_COMPAT_BITLIB) /* { */ +#define pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n)) +#define checkunsigned(L,i) ((lua_Unsigned)luaL_checkinteger(L,i)) + + /* number of bits to consider in a number */ #if !defined(LUA_NBITS) #define LUA_NBITS 32 @@ -46,14 +50,14 @@ static lua_Unsigned andaux (lua_State *L) { int i, n = lua_gettop(L); lua_Unsigned r = ~(lua_Unsigned)0; for (i = 1; i <= n; i++) - r &= luaL_checkunsigned(L, i); + r &= checkunsigned(L, i); return trim(r); } static int b_and (lua_State *L) { lua_Unsigned r = andaux(L); - lua_pushunsigned(L, r); + pushunsigned(L, r); return 1; } @@ -69,8 +73,8 @@ static int b_or (lua_State *L) { int i, n = lua_gettop(L); lua_Unsigned r = 0; for (i = 1; i <= n; i++) - r |= luaL_checkunsigned(L, i); - lua_pushunsigned(L, trim(r)); + r |= checkunsigned(L, i); + pushunsigned(L, trim(r)); return 1; } @@ -79,15 +83,15 @@ static int b_xor (lua_State *L) { int i, n = lua_gettop(L); lua_Unsigned r = 0; for (i = 1; i <= n; i++) - r ^= luaL_checkunsigned(L, i); - lua_pushunsigned(L, trim(r)); + r ^= checkunsigned(L, i); + pushunsigned(L, trim(r)); return 1; } static int b_not (lua_State *L) { - lua_Unsigned r = ~luaL_checkunsigned(L, 1); - lua_pushunsigned(L, trim(r)); + lua_Unsigned r = ~checkunsigned(L, 1); + pushunsigned(L, trim(r)); return 1; } @@ -104,23 +108,23 @@ static int b_shift (lua_State *L, lua_Unsigned r, lua_Integer i) { else r <<= i; r = trim(r); } - lua_pushunsigned(L, r); + pushunsigned(L, r); return 1; } static int b_lshift (lua_State *L) { - return b_shift(L, luaL_checkunsigned(L, 1), luaL_checkinteger(L, 2)); + return b_shift(L, checkunsigned(L, 1), luaL_checkinteger(L, 2)); } static int b_rshift (lua_State *L) { - return b_shift(L, luaL_checkunsigned(L, 1), -luaL_checkinteger(L, 2)); + return b_shift(L, checkunsigned(L, 1), -luaL_checkinteger(L, 2)); } static int b_arshift (lua_State *L) { - lua_Unsigned r = luaL_checkunsigned(L, 1); + lua_Unsigned r = checkunsigned(L, 1); lua_Integer i = luaL_checkinteger(L, 2); if (i < 0 || !(r & ((lua_Unsigned)1 << (LUA_NBITS - 1)))) return b_shift(L, r, -i); @@ -128,19 +132,19 @@ static int b_arshift (lua_State *L) { if (i >= LUA_NBITS) r = ALLONES; else r = trim((r >> i) | ~(trim(~(lua_Unsigned)0) >> i)); /* add signal bit */ - lua_pushunsigned(L, r); + pushunsigned(L, r); return 1; } } static int b_rot (lua_State *L, lua_Integer d) { - lua_Unsigned r = luaL_checkunsigned(L, 1); + lua_Unsigned r = checkunsigned(L, 1); int i = d & (LUA_NBITS - 1); /* i = d % NBITS */ r = trim(r); if (i != 0) /* avoid undefined shift of LUA_NBITS when i == 0 */ r = (r << i) | (r >> (LUA_NBITS - i)); - lua_pushunsigned(L, trim(r)); + pushunsigned(L, trim(r)); return 1; } @@ -175,23 +179,22 @@ static int fieldargs (lua_State *L, int farg, int *width) { static int b_extract (lua_State *L) { int w; - lua_Unsigned r = trim(luaL_checkunsigned(L, 1)); + lua_Unsigned r = trim(checkunsigned(L, 1)); int f = fieldargs(L, 2, &w); r = (r >> f) & mask(w); - lua_pushunsigned(L, r); + pushunsigned(L, r); return 1; } static int b_replace (lua_State *L) { int w; - lua_Unsigned r = trim(luaL_checkunsigned(L, 1)); - lua_Unsigned v = luaL_checkunsigned(L, 2); + lua_Unsigned r = trim(checkunsigned(L, 1)); + lua_Unsigned v = trim(checkunsigned(L, 2)); int f = fieldargs(L, 3, &w); - int m = mask(w); - v &= m; /* erase bits outside given width */ - r = (r & ~(m << f)) | (v << f); - lua_pushunsigned(L, r); + lua_Unsigned m = mask(w); + r = (r & ~(m << f)) | ((v & m) << f); + pushunsigned(L, r); return 1; } diff --git a/libs/lua-5.3.1/src/lcode.c b/libs/lua-5.3.2/src/lcode.c similarity index 99% rename from libs/lua-5.3.1/src/lcode.c rename to libs/lua-5.3.2/src/lcode.c index d6f0fcd..7c6918f 100644 --- a/libs/lua-5.3.1/src/lcode.c +++ b/libs/lua-5.3.2/src/lcode.c @@ -1,5 +1,5 @@ /* -** $Id: lcode.c,v 2.101 2015/04/29 18:24:11 roberto Exp $ +** $Id: lcode.c,v 2.103 2015/11/19 19:16:22 roberto Exp $ ** Code generator for Lua ** See Copyright Notice in lua.h */ @@ -37,7 +37,7 @@ static int tonumeral(expdesc *e, TValue *v) { - if (e->t != NO_JUMP || e->f != NO_JUMP) + if (hasjumps(e)) return 0; /* not a numeral */ switch (e->k) { case VKINT: @@ -816,7 +816,7 @@ static void codeexpval (FuncState *fs, OpCode op, freeexp(fs, e1); } e1->u.info = luaK_codeABC(fs, op, 0, o1, o2); /* generate opcode */ - e1->k = VRELOCABLE; /* all those operations are relocable */ + e1->k = VRELOCABLE; /* all those operations are relocatable */ luaK_fixline(fs, line); } } diff --git a/libs/lua-5.3.1/src/lcode.h b/libs/lua-5.3.2/src/lcode.h similarity index 100% rename from libs/lua-5.3.1/src/lcode.h rename to libs/lua-5.3.2/src/lcode.h diff --git a/libs/lua-5.3.1/src/lcorolib.c b/libs/lua-5.3.2/src/lcorolib.c similarity index 100% rename from libs/lua-5.3.1/src/lcorolib.c rename to libs/lua-5.3.2/src/lcorolib.c diff --git a/libs/lua-5.3.1/src/lctype.c b/libs/lua-5.3.2/src/lctype.c similarity index 100% rename from libs/lua-5.3.1/src/lctype.c rename to libs/lua-5.3.2/src/lctype.c diff --git a/libs/lua-5.3.1/src/lctype.h b/libs/lua-5.3.2/src/lctype.h similarity index 100% rename from libs/lua-5.3.1/src/lctype.h rename to libs/lua-5.3.2/src/lctype.h diff --git a/libs/lua-5.3.1/src/ldblib.c b/libs/lua-5.3.2/src/ldblib.c similarity index 98% rename from libs/lua-5.3.1/src/ldblib.c rename to libs/lua-5.3.2/src/ldblib.c index 9151458..786f6cd 100644 --- a/libs/lua-5.3.1/src/ldblib.c +++ b/libs/lua-5.3.2/src/ldblib.c @@ -1,5 +1,5 @@ /* -** $Id: ldblib.c,v 1.149 2015/02/19 17:06:21 roberto Exp $ +** $Id: ldblib.c,v 1.151 2015/11/23 11:29:43 roberto Exp $ ** Interface from Lua to its debug API ** See Copyright Notice in lua.h */ @@ -28,8 +28,8 @@ static const int HOOKKEY = 0; /* -** If L1 != L, L1 can be in any state, and therefore there is no -** garanties about its stack space; any push in L1 must be +** If L1 != L, L1 can be in any state, and therefore there are no +** guarantees about its stack space; any push in L1 must be ** checked. */ static void checkstack (lua_State *L, lua_State *L1, int n) { diff --git a/libs/lua-5.3.1/src/ldebug.c b/libs/lua-5.3.2/src/ldebug.c similarity index 98% rename from libs/lua-5.3.1/src/ldebug.c rename to libs/lua-5.3.2/src/ldebug.c index f76582c..9bd86d0 100644 --- a/libs/lua-5.3.1/src/ldebug.c +++ b/libs/lua-5.3.2/src/ldebug.c @@ -1,5 +1,5 @@ /* -** $Id: ldebug.c,v 2.115 2015/05/22 17:45:56 roberto Exp $ +** $Id: ldebug.c,v 2.117 2015/11/02 18:48:07 roberto Exp $ ** Debug Interface ** See Copyright Notice in lua.h */ @@ -618,7 +618,7 @@ l_noret luaG_errormsg (lua_State *L) { setobjs2s(L, L->top, L->top - 1); /* move argument */ setobjs2s(L, L->top - 1, errfunc); /* push function */ L->top++; /* assume EXTRA_STACK */ - luaD_call(L, L->top - 2, 1, 0); /* call it */ + luaD_callnoyield(L, L->top - 2, 1); /* call it */ } luaD_throw(L, LUA_ERRRUN); } @@ -640,9 +640,11 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) { void luaG_traceexec (lua_State *L) { CallInfo *ci = L->ci; lu_byte mask = L->hookmask; - int counthook = ((mask & LUA_MASKCOUNT) && L->hookcount == 0); + int counthook = (--L->hookcount == 0 && (mask & LUA_MASKCOUNT)); if (counthook) resethookcount(L); /* reset count */ + else if (!(mask & LUA_MASKLINE)) + return; /* no line hook and count != 0; nothing to be done */ if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */ ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */ return; /* do not call hook again (VM yielded, so it did not move) */ diff --git a/libs/lua-5.3.1/src/ldebug.h b/libs/lua-5.3.2/src/ldebug.h similarity index 100% rename from libs/lua-5.3.1/src/ldebug.h rename to libs/lua-5.3.2/src/ldebug.h diff --git a/libs/lua-5.3.1/src/ldo.c b/libs/lua-5.3.2/src/ldo.c similarity index 79% rename from libs/lua-5.3.1/src/ldo.c rename to libs/lua-5.3.2/src/ldo.c index 5c93a25..95efd56 100644 --- a/libs/lua-5.3.1/src/ldo.c +++ b/libs/lua-5.3.2/src/ldo.c @@ -1,5 +1,5 @@ /* -** $Id: ldo.c,v 2.138 2015/05/22 17:48:19 roberto Exp $ +** $Id: ldo.c,v 2.150 2015/11/19 19:16:22 roberto Exp $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ @@ -150,6 +150,11 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { /* }====================================================== */ +/* +** {================================================================== +** Stack reallocation +** =================================================================== +*/ static void correctstack (lua_State *L, TValue *oldstack) { CallInfo *ci; UpVal *up; @@ -221,14 +226,22 @@ void luaD_shrinkstack (lua_State *L) { luaE_freeCI(L); /* free all CIs (list grew because of an error) */ else luaE_shrinkCI(L); /* shrink list */ - if (inuse > LUAI_MAXSTACK || /* still handling stack overflow? */ - goodsize >= L->stacksize) /* would grow instead of shrink? */ - condmovestack(L); /* don't change stack (change only for debugging) */ - else + if (inuse <= LUAI_MAXSTACK && /* not handling stack overflow? */ + goodsize < L->stacksize) /* trying to shrink? */ luaD_reallocstack(L, goodsize); /* shrink it */ + else + condmovestack(L,,); /* don't change stack (change only for debugging) */ } +void luaD_inctop (lua_State *L) { + luaD_checkstack(L, 1); + L->top++; +} + +/* }================================================================== */ + + void luaD_hook (lua_State *L, int event, int line) { lua_Hook hook = L->hook; if (hook && L->allowhook) { @@ -273,15 +286,15 @@ static StkId adjust_varargs (lua_State *L, Proto *p, int actual) { int i; int nfixargs = p->numparams; StkId base, fixed; - lua_assert(actual >= nfixargs); /* move fixed parameters to final position */ - luaD_checkstack(L, p->maxstacksize); /* check again for new 'base' */ fixed = L->top - actual; /* first fixed argument */ base = L->top; /* final position of first argument */ - for (i=0; itop++, fixed + i); - setnilvalue(fixed + i); + setnilvalue(fixed + i); /* erase original copy (for GC) */ } + for (; i < nfixargs; i++) + setnilvalue(L->top++); /* complete missing arguments */ return base; } @@ -308,26 +321,36 @@ static void tryfuncTM (lua_State *L, StkId func) { #define next_ci(L) (L->ci = (L->ci->next ? L->ci->next : luaE_extendCI(L))) +/* macro to check stack size, preserving 'p' */ +#define checkstackp(L,n,p) \ + luaD_checkstackaux(L, n, \ + ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \ + luaC_checkGC(L), /* stack grow uses memory */ \ + p = restorestack(L, t__)) /* 'pos' part: restore 'p' */ + + /* -** returns true if function has been executed (C function) +** Prepares a function call: checks the stack, creates a new CallInfo +** entry, fills in the relevant information, calls hook if needed. +** If function is a C function, does the call, too. (Otherwise, leave +** the execution ('luaV_execute') to the caller, to allow stackless +** calls.) Returns true iff function has been executed (C function). */ int luaD_precall (lua_State *L, StkId func, int nresults) { lua_CFunction f; CallInfo *ci; - int n; /* number of arguments (Lua) or returns (C) */ - ptrdiff_t funcr = savestack(L, func); switch (ttype(func)) { + case LUA_TCCL: /* C closure */ + f = clCvalue(func)->f; + goto Cfunc; case LUA_TLCF: /* light C function */ f = fvalue(func); - goto Cfunc; - case LUA_TCCL: { /* C closure */ - f = clCvalue(func)->f; - Cfunc: - luaC_checkGC(L); /* stack grow uses memory */ - luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ + Cfunc: { + int n; /* number of returns */ + checkstackp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ ci = next_ci(L); /* now 'enter' new function */ ci->nresults = nresults; - ci->func = restorestack(L, funcr); + ci->func = func; ci->top = L->top + LUA_MINSTACK; lua_assert(ci->top <= L->stack_last); ci->callstatus = 0; @@ -337,41 +360,36 @@ int luaD_precall (lua_State *L, StkId func, int nresults) { n = (*f)(L); /* do the actual call */ lua_lock(L); api_checknelems(L, n); - luaD_poscall(L, L->top - n, n); + luaD_poscall(L, ci, L->top - n, n); return 1; } case LUA_TLCL: { /* Lua function: prepare its call */ StkId base; Proto *p = clLvalue(func)->p; - n = cast_int(L->top - func) - 1; /* number of real arguments */ - luaC_checkGC(L); /* stack grow uses memory */ - luaD_checkstack(L, p->maxstacksize); - for (; n < p->numparams; n++) - setnilvalue(L->top++); /* complete missing arguments */ - if (!p->is_vararg) { - func = restorestack(L, funcr); + int n = cast_int(L->top - func) - 1; /* number of real arguments */ + int fsize = p->maxstacksize; /* frame size */ + checkstackp(L, fsize, func); + if (p->is_vararg != 1) { /* do not use vararg? */ + for (; n < p->numparams; n++) + setnilvalue(L->top++); /* complete missing arguments */ base = func + 1; } - else { + else base = adjust_varargs(L, p, n); - func = restorestack(L, funcr); /* previous call can change stack */ - } ci = next_ci(L); /* now 'enter' new function */ ci->nresults = nresults; ci->func = func; ci->u.l.base = base; - ci->top = base + p->maxstacksize; + L->top = ci->top = base + fsize; lua_assert(ci->top <= L->stack_last); ci->u.l.savedpc = p->code; /* starting point */ ci->callstatus = CIST_LUA; - L->top = ci->top; if (L->hookmask & LUA_MASKCALL) callhook(L, ci); return 0; } default: { /* not a function */ - luaD_checkstack(L, 1); /* ensure space for metamethod */ - func = restorestack(L, funcr); /* previous call may change stack */ + checkstackp(L, 1, func); /* ensure space for metamethod */ tryfuncTM(L, func); /* try to get '__call' metamethod */ return luaD_precall(L, func, nresults); /* now it must be a function */ } @@ -379,10 +397,57 @@ int luaD_precall (lua_State *L, StkId func, int nresults) { } -int luaD_poscall (lua_State *L, StkId firstResult, int nres) { +/* +** Given 'nres' results at 'firstResult', move 'wanted' of them to 'res'. +** Handle most typical cases (zero results for commands, one result for +** expressions, multiple results for tail calls/single parameters) +** separated. +*/ +static int moveresults (lua_State *L, const TValue *firstResult, StkId res, + int nres, int wanted) { + switch (wanted) { /* handle typical cases separately */ + case 0: break; /* nothing to move */ + case 1: { /* one result needed */ + if (nres == 0) /* no results? */ + firstResult = luaO_nilobject; /* adjust with nil */ + setobjs2s(L, res, firstResult); /* move it to proper place */ + break; + } + case LUA_MULTRET: { + int i; + for (i = 0; i < nres; i++) /* move all results to correct place */ + setobjs2s(L, res + i, firstResult + i); + L->top = res + nres; + return 0; /* wanted == LUA_MULTRET */ + } + default: { + int i; + if (wanted <= nres) { /* enough results? */ + for (i = 0; i < wanted; i++) /* move wanted results to correct place */ + setobjs2s(L, res + i, firstResult + i); + } + else { /* not enough results; use all of them plus nils */ + for (i = 0; i < nres; i++) /* move all results to correct place */ + setobjs2s(L, res + i, firstResult + i); + for (; i < wanted; i++) /* complete wanted number of results */ + setnilvalue(res + i); + } + break; + } + } + L->top = res + wanted; /* top points after the last result */ + return 1; +} + + +/* +** Finishes a function call: calls hook if necessary, removes CallInfo, +** moves current number of results to proper place; returns 0 iff call +** wanted multiple (variable number of) results. +*/ +int luaD_poscall (lua_State *L, CallInfo *ci, StkId firstResult, int nres) { StkId res; - int wanted, i; - CallInfo *ci = L->ci; + int wanted = ci->nresults; if (L->hookmask & (LUA_MASKRET | LUA_MASKLINE)) { if (L->hookmask & LUA_MASKRET) { ptrdiff_t fr = savestack(L, firstResult); /* hook may change stack */ @@ -392,15 +457,24 @@ int luaD_poscall (lua_State *L, StkId firstResult, int nres) { L->oldpc = ci->previous->u.l.savedpc; /* 'oldpc' for caller function */ } res = ci->func; /* res == final position of 1st result */ - wanted = ci->nresults; L->ci = ci->previous; /* back to caller */ - /* move results to correct place */ - for (i = wanted; i != 0 && nres-- > 0; i--) - setobjs2s(L, res++, firstResult++); - while (i-- > 0) - setnilvalue(res++); - L->top = res; - return (wanted - LUA_MULTRET); /* 0 iff wanted == LUA_MULTRET */ + /* move results to proper place */ + return moveresults(L, firstResult, res, nres, wanted); +} + + +/* +** Check appropriate error for stack overflow ("regular" overflow or +** overflow while handling stack overflow). If 'nCalls' is larger than +** LUAI_MAXCCALLS (which means it is handling a "regular" overflow) but +** smaller than 9/8 of LUAI_MAXCCALLS, does not report an error (to +** allow overflow handling to work) +*/ +static void stackerror (lua_State *L) { + if (L->nCcalls == LUAI_MAXCCALLS) + luaG_runerror(L, "C stack overflow"); + else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3))) + luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ } @@ -410,21 +484,25 @@ int luaD_poscall (lua_State *L, StkId firstResult, int nres) { ** When returns, all the results are on the stack, starting at the original ** function position. */ -void luaD_call (lua_State *L, StkId func, int nResults, int allowyield) { - if (++L->nCcalls >= LUAI_MAXCCALLS) { - if (L->nCcalls == LUAI_MAXCCALLS) - luaG_runerror(L, "C stack overflow"); - else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3))) - luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ - } - if (!allowyield) L->nny++; +void luaD_call (lua_State *L, StkId func, int nResults) { + if (++L->nCcalls >= LUAI_MAXCCALLS) + stackerror(L); if (!luaD_precall(L, func, nResults)) /* is a Lua function? */ luaV_execute(L); /* call it */ - if (!allowyield) L->nny--; L->nCcalls--; } +/* +** Similar to 'luaD_call', but does not allow yields during the call +*/ +void luaD_callnoyield (lua_State *L, StkId func, int nResults) { + L->nny++; + luaD_call(L, func, nResults); + L->nny--; +} + + /* ** Completes the execution of an interrupted C function, calling its ** continuation function. @@ -449,7 +527,7 @@ static void finishCcall (lua_State *L, int status) { lua_lock(L); api_checknelems(L, n); /* finish 'luaD_precall' */ - luaD_poscall(L, L->top - n, n); + luaD_poscall(L, ci, L->top - n, n); } @@ -560,7 +638,7 @@ static void resume (lua_State *L, void *ud) { api_checknelems(L, n); firstArg = L->top - n; /* yield results come from continuation */ } - luaD_poscall(L, firstArg, n); /* finish 'luaD_precall' */ + luaD_poscall(L, ci, firstArg, n); /* finish 'luaD_precall' */ } unroll(L, NULL); /* run continuation */ } @@ -570,7 +648,7 @@ static void resume (lua_State *L, void *ud) { LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs) { int status; - int oldnny = L->nny; /* save "number of non-yieldable" calls */ + unsigned short oldnny = L->nny; /* save "number of non-yieldable" calls */ lua_lock(L); luai_userstateresume(L, nargs); L->nCcalls = (from) ? from->nCcalls + 1 : 1; @@ -684,7 +762,7 @@ static void f_parser (lua_State *L, void *ud) { int c = zgetc(p->z); /* read first character */ if (c == LUA_SIGNATURE[0]) { checkmode(L, p->mode, "binary"); - cl = luaU_undump(L, p->z, &p->buff, p->name); + cl = luaU_undump(L, p->z, p->name); } else { checkmode(L, p->mode, "text"); diff --git a/libs/lua-5.3.1/src/ldo.h b/libs/lua-5.3.2/src/ldo.h similarity index 57% rename from libs/lua-5.3.1/src/ldo.h rename to libs/lua-5.3.2/src/ldo.h index edade65..80582dc 100644 --- a/libs/lua-5.3.1/src/ldo.h +++ b/libs/lua-5.3.2/src/ldo.h @@ -1,5 +1,5 @@ /* -** $Id: ldo.h,v 2.22 2015/05/22 17:48:19 roberto Exp $ +** $Id: ldo.h,v 2.28 2015/11/23 11:29:43 roberto Exp $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ @@ -13,11 +13,21 @@ #include "lzio.h" -#define luaD_checkstack(L,n) if (L->stack_last - L->top <= (n)) \ - luaD_growstack(L, n); else condmovestack(L); +/* +** Macro to check stack size and grow stack if needed. Parameters +** 'pre'/'pos' allow the macro to preserve a pointer into the +** stack across reallocations, doing the work only when needed. +** 'condmovestack' is used in heavy tests to force a stack reallocation +** at every check. +*/ +#define luaD_checkstackaux(L,n,pre,pos) \ + if (L->stack_last - L->top <= (n)) \ + { pre; luaD_growstack(L, n); pos; } else { condmovestack(L,pre,pos); } + +/* In general, 'pre'/'pos' are empty (nothing to save) */ +#define luaD_checkstack(L,n) luaD_checkstackaux(L,n,,) -#define incr_top(L) {L->top++; luaD_checkstack(L,0);} #define savestack(L,p) ((char *)(p) - (char *)L->stack) #define restorestack(L,n) ((TValue *)((char *)L->stack + (n))) @@ -30,14 +40,16 @@ LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, const char *mode); LUAI_FUNC void luaD_hook (lua_State *L, int event, int line); LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults); -LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults, - int allowyield); +LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); +LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t oldtop, ptrdiff_t ef); -LUAI_FUNC int luaD_poscall (lua_State *L, StkId firstResult, int nres); +LUAI_FUNC int luaD_poscall (lua_State *L, CallInfo *ci, StkId firstResult, + int nres); LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize); LUAI_FUNC void luaD_growstack (lua_State *L, int n); LUAI_FUNC void luaD_shrinkstack (lua_State *L); +LUAI_FUNC void luaD_inctop (lua_State *L); LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode); LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); diff --git a/libs/lua-5.3.1/src/ldump.c b/libs/lua-5.3.2/src/ldump.c similarity index 97% rename from libs/lua-5.3.1/src/ldump.c rename to libs/lua-5.3.2/src/ldump.c index 4c04812..016e300 100644 --- a/libs/lua-5.3.1/src/ldump.c +++ b/libs/lua-5.3.2/src/ldump.c @@ -1,5 +1,5 @@ /* -** $Id: ldump.c,v 2.36 2015/03/30 15:43:51 roberto Exp $ +** $Id: ldump.c,v 2.37 2015/10/08 15:53:49 roberto Exp $ ** save precompiled Lua chunks ** See Copyright Notice in lua.h */ @@ -38,7 +38,7 @@ typedef struct { static void DumpBlock (const void *b, size_t size, DumpState *D) { - if (D->status == 0) { + if (D->status == 0 && size > 0) { lua_unlock(D->L); D->status = (*D->writer)(D->L, b, size, D->data); lua_lock(D->L); diff --git a/libs/lua-5.3.1/src/lfunc.c b/libs/lua-5.3.2/src/lfunc.c similarity index 100% rename from libs/lua-5.3.1/src/lfunc.c rename to libs/lua-5.3.2/src/lfunc.c diff --git a/libs/lua-5.3.1/src/lfunc.h b/libs/lua-5.3.2/src/lfunc.h similarity index 100% rename from libs/lua-5.3.1/src/lfunc.h rename to libs/lua-5.3.2/src/lfunc.h diff --git a/libs/lua-5.3.1/src/lgc.c b/libs/lua-5.3.2/src/lgc.c similarity index 97% rename from libs/lua-5.3.1/src/lgc.c rename to libs/lua-5.3.2/src/lgc.c index 973c269..49d8ecb 100644 --- a/libs/lua-5.3.1/src/lgc.c +++ b/libs/lua-5.3.2/src/lgc.c @@ -1,5 +1,5 @@ /* -** $Id: lgc.c,v 2.205 2015/03/25 13:42:19 roberto Exp $ +** $Id: lgc.c,v 2.210 2015/11/03 18:10:44 roberto Exp $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -114,8 +114,13 @@ static void reallymarkobject (global_State *g, GCObject *o); /* -** if key is not marked, mark its entry as dead (therefore removing it -** from the table) +** If key is not marked, mark its entry as dead. This allows key to be +** collected, but keeps its entry in the table. A dead node is needed +** when Lua looks up for a key (it may be part of a chain) and when +** traversing a weak table (key might be removed from the table during +** traversal). Other places never manipulate dead keys, because its +** associated nil value is enough to signal that the entry is logically +** empty. */ static void removeentry (Node *n) { lua_assert(ttisnil(gval(n))); @@ -542,7 +547,8 @@ static lu_mem traversethread (global_State *g, lua_State *th) { } else if (g->gckind != KGC_EMERGENCY) luaD_shrinkstack(th); /* do not change stack in emergency cycle */ - return (sizeof(lua_State) + sizeof(TValue) * th->stacksize); + return (sizeof(lua_State) + sizeof(TValue) * th->stacksize + + sizeof(CallInfo) * th->nci); } @@ -769,12 +775,11 @@ static GCObject **sweeptolive (lua_State *L, GCObject **p, int *n) { */ /* -** If possible, free concatenation buffer and shrink string table +** If possible, shrink string table */ static void checkSizes (lua_State *L, global_State *g) { if (g->gckind != KGC_EMERGENCY) { l_mem olddebt = g->GCdebt; - luaZ_freebuffer(L, &g->buff); /* free concatenation buffer */ if (g->strt.nuse < g->strt.size / 4) /* string table too big? */ luaS_resize(L, g->strt.size / 2); /* shrink it a little */ g->GCestimate += g->GCdebt - olddebt; /* update estimate */ @@ -797,7 +802,7 @@ static GCObject *udata2finalize (global_State *g) { static void dothecall (lua_State *L, void *ud) { UNUSED(ud); - luaD_call(L, L->top - 2, 0, 0); + luaD_callnoyield(L, L->top - 2, 0); } @@ -1114,9 +1119,12 @@ void luaC_runtilstate (lua_State *L, int statesmask) { static l_mem getdebt (global_State *g) { l_mem debt = g->GCdebt; int stepmul = g->gcstepmul; - debt = (debt / STEPMULADJ) + 1; - debt = (debt < MAX_LMEM / stepmul) ? debt * stepmul : MAX_LMEM; - return debt; + if (debt <= 0) return 0; /* minimal debt */ + else { + debt = (debt / STEPMULADJ) + 1; + debt = (debt < MAX_LMEM / stepmul) ? debt * stepmul : MAX_LMEM; + return debt; + } } /* diff --git a/libs/lua-5.3.1/src/lgc.h b/libs/lua-5.3.2/src/lgc.h similarity index 77% rename from libs/lua-5.3.1/src/lgc.h rename to libs/lua-5.3.2/src/lgc.h index 0eedf84..1775ca4 100644 --- a/libs/lua-5.3.1/src/lgc.h +++ b/libs/lua-5.3.2/src/lgc.h @@ -1,5 +1,5 @@ /* -** $Id: lgc.h,v 2.86 2014/10/25 11:50:46 roberto Exp $ +** $Id: lgc.h,v 2.90 2015/10/21 18:15:15 roberto Exp $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -101,26 +101,35 @@ #define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS) -#define luaC_condGC(L,c) \ - {if (G(L)->GCdebt > 0) {c;}; condchangemem(L);} -#define luaC_checkGC(L) luaC_condGC(L, luaC_step(L);) +/* +** Does one step of collection when debt becomes positive. 'pre'/'pos' +** allows some adjustments to be done only when needed. macro +** 'condchangemem' is used only for heavy tests (forcing a full +** GC cycle on every opportunity) +*/ +#define luaC_condGC(L,pre,pos) \ + { if (G(L)->GCdebt > 0) { pre; luaC_step(L); pos;}; \ + condchangemem(L,pre,pos); } + +/* more often than not, 'pre'/'pos' are empty */ +#define luaC_checkGC(L) luaC_condGC(L,,) -#define luaC_barrier(L,p,v) { \ - if (iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) \ - luaC_barrier_(L,obj2gco(p),gcvalue(v)); } +#define luaC_barrier(L,p,v) ( \ + (iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \ + luaC_barrier_(L,obj2gco(p),gcvalue(v)) : cast_void(0)) -#define luaC_barrierback(L,p,v) { \ - if (iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) \ - luaC_barrierback_(L,p); } +#define luaC_barrierback(L,p,v) ( \ + (iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \ + luaC_barrierback_(L,p) : cast_void(0)) -#define luaC_objbarrier(L,p,o) { \ - if (isblack(p) && iswhite(o)) \ - luaC_barrier_(L,obj2gco(p),obj2gco(o)); } +#define luaC_objbarrier(L,p,o) ( \ + (isblack(p) && iswhite(o)) ? \ + luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0)) -#define luaC_upvalbarrier(L,uv) \ - { if (iscollectable((uv)->v) && !upisopen(uv)) \ - luaC_upvalbarrier_(L,uv); } +#define luaC_upvalbarrier(L,uv) ( \ + (iscollectable((uv)->v) && !upisopen(uv)) ? \ + luaC_upvalbarrier_(L,uv) : cast_void(0)) LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o); LUAI_FUNC void luaC_freeallobjects (lua_State *L); diff --git a/libs/lua-5.3.1/src/linit.c b/libs/lua-5.3.2/src/linit.c similarity index 100% rename from libs/lua-5.3.1/src/linit.c rename to libs/lua-5.3.2/src/linit.c diff --git a/libs/lua-5.3.1/src/liolib.c b/libs/lua-5.3.2/src/liolib.c similarity index 96% rename from libs/lua-5.3.1/src/liolib.c rename to libs/lua-5.3.2/src/liolib.c index 193cac6..a91ba39 100644 --- a/libs/lua-5.3.1/src/liolib.c +++ b/libs/lua-5.3.2/src/liolib.c @@ -1,5 +1,5 @@ /* -** $Id: liolib.c,v 2.144 2015/04/03 18:41:57 roberto Exp $ +** $Id: liolib.c,v 2.148 2015/11/23 11:36:11 roberto Exp $ ** Standard I/O (and system) library ** See Copyright Notice in lua.h */ @@ -23,18 +23,24 @@ #include "lualib.h" -#if !defined(l_checkmode) + /* -** Check whether 'mode' matches '[rwa]%+?b?'. ** Change this macro to accept other modes for 'fopen' besides ** the standard ones. */ +#if !defined(l_checkmode) + +/* accepted extensions to 'mode' in 'fopen' */ +#if !defined(L_MODEEXT) +#define L_MODEEXT "b" +#endif + +/* Check whether 'mode' matches '[rwa]%+?[L_MODEEXT]*' */ #define l_checkmode(mode) \ (*mode != '\0' && strchr("rwa", *(mode++)) != NULL && \ - (*mode != '+' || ++mode) && /* skip if char is '+' */ \ - (*mode != 'b' || ++mode) && /* skip if char is 'b' */ \ - (*mode == '\0')) + (*mode != '+' || (++mode, 1)) && /* skip if char is '+' */ \ + (strspn(mode, L_MODEEXT) == strlen(mode))) #endif @@ -176,7 +182,7 @@ static FILE *tofile (lua_State *L) { /* ** When creating file handles, always creates a 'closed' file handle ** before opening the actual file; so, if there is a memory error, the -** file is not left opened. +** handle is in a consistent state. */ static LStream *newprefile (lua_State *L) { LStream *p = (LStream *)lua_newuserdata(L, sizeof(LStream)); @@ -318,8 +324,15 @@ static int io_output (lua_State *L) { static int io_readline (lua_State *L); +/* +** maximum number of arguments to 'f:lines'/'io.lines' (it + 3 must fit +** in the limit for upvalues of a closure) +*/ +#define MAXARGLINE 250 + static void aux_lines (lua_State *L, int toclose) { int n = lua_gettop(L) - 1; /* number of arguments to read */ + luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments"); lua_pushinteger(L, n); /* number of arguments to read */ lua_pushboolean(L, toclose); /* close/not close file when finished */ lua_rotate(L, 2, 2); /* move 'n' and 'toclose' to their positions */ @@ -462,7 +475,7 @@ static int read_line (lua_State *L, FILE *f, int chop) { int c = '\0'; luaL_buffinit(L, &b); while (c != EOF && c != '\n') { /* repeat until end of line */ - char *buff = luaL_prepbuffer(&b); /* pre-allocate buffer */ + char *buff = luaL_prepbuffer(&b); /* preallocate buffer */ int i = 0; l_lockfile(f); /* no memory errors can happen inside the lock */ while (i < LUAL_BUFFERSIZE && (c = l_getc(f)) != EOF && c != '\n') @@ -483,7 +496,7 @@ static void read_all (lua_State *L, FILE *f) { luaL_Buffer b; luaL_buffinit(L, &b); do { /* read file in chunks of LUAL_BUFFERSIZE bytes */ - char *p = luaL_prepbuffsize(&b, LUAL_BUFFERSIZE); + char *p = luaL_prepbuffer(&b); nr = fread(p, sizeof(char), LUAL_BUFFERSIZE, f); luaL_addsize(&b, nr); } while (nr == LUAL_BUFFERSIZE); diff --git a/libs/lua-5.3.1/src/llex.c b/libs/lua-5.3.2/src/llex.c similarity index 98% rename from libs/lua-5.3.1/src/llex.c rename to libs/lua-5.3.2/src/llex.c index c35bd55..16ea3eb 100644 --- a/libs/lua-5.3.1/src/llex.c +++ b/libs/lua-5.3.2/src/llex.c @@ -1,5 +1,5 @@ /* -** $Id: llex.c,v 2.93 2015/05/22 17:45:56 roberto Exp $ +** $Id: llex.c,v 2.95 2015/11/19 19:16:22 roberto Exp $ ** Lexical Analyzer ** See Copyright Notice in lua.h */ @@ -220,8 +220,6 @@ static void buffreplace (LexState *ls, char from, char to) { } -#define buff2num(b,o) (luaO_str2num(luaZ_buffer(b), o) != 0) - /* ** in case of format error, try to change decimal point separator to ** the one defined in the current locale and check again @@ -230,7 +228,7 @@ static void trydecpoint (LexState *ls, TValue *o) { char old = ls->decpoint; ls->decpoint = lua_getlocaledecpoint(); buffreplace(ls, old, ls->decpoint); /* try new decimal separator */ - if (!buff2num(ls->buff, o)) { + if (luaO_str2num(luaZ_buffer(ls->buff), o) == 0) { /* format error with correct decimal point: no more options */ buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */ lexerror(ls, "malformed number", TK_FLT); @@ -262,7 +260,7 @@ static int read_numeral (LexState *ls, SemInfo *seminfo) { } save(ls, '\0'); buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */ - if (!buff2num(ls->buff, &obj)) /* format error? */ + if (luaO_str2num(luaZ_buffer(ls->buff), &obj) == 0) /* format error? */ trydecpoint(ls, &obj); /* try to update decimal point separator */ if (ttisinteger(&obj)) { seminfo->i = ivalue(&obj); @@ -277,7 +275,7 @@ static int read_numeral (LexState *ls, SemInfo *seminfo) { /* -** skip a sequence '[=*[' or ']=*]'; if sequence is wellformed, return +** skip a sequence '[=*[' or ']=*]'; if sequence is well formed, return ** its number of '='s; otherwise, return a negative number (-1 iff there ** are no '='s after initial bracket) */ diff --git a/libs/lua-5.3.1/src/llex.h b/libs/lua-5.3.2/src/llex.h similarity index 100% rename from libs/lua-5.3.1/src/llex.h rename to libs/lua-5.3.2/src/llex.h diff --git a/libs/lua-5.3.1/src/llimits.h b/libs/lua-5.3.2/src/llimits.h similarity index 89% rename from libs/lua-5.3.1/src/llimits.h rename to libs/lua-5.3.2/src/llimits.h index 277c724..f21377f 100644 --- a/libs/lua-5.3.1/src/llimits.h +++ b/libs/lua-5.3.2/src/llimits.h @@ -1,5 +1,5 @@ /* -** $Id: llimits.h,v 1.135 2015/06/09 14:21:00 roberto Exp $ +** $Id: llimits.h,v 1.141 2015/11/19 19:16:22 roberto Exp $ ** Limits, basic types, and some other 'installation-dependent' definitions ** See Copyright Notice in lua.h */ @@ -64,7 +64,13 @@ typedef unsigned char lu_byte; #if defined(LUAI_USER_ALIGNMENT_T) typedef LUAI_USER_ALIGNMENT_T L_Umaxalign; #else -typedef union { double u; void *s; lua_Integer i; long l; } L_Umaxalign; +typedef union { + lua_Number n; + double u; + void *s; + lua_Integer i; + long l; +} L_Umaxalign; #endif @@ -78,7 +84,7 @@ typedef LUAI_UACINT l_uacInt; #if defined(lua_assert) #define check_exp(c,e) (lua_assert(c), (e)) /* to avoid problems with conditions too long */ -#define lua_longassert(c) { if (!(c)) lua_assert(0); } +#define lua_longassert(c) ((c) ? (void)0 : lua_assert(0)) #else #define lua_assert(c) ((void)0) #define check_exp(c,e) (e) @@ -184,10 +190,13 @@ typedef unsigned long Instruction; /* -** Size of cache for strings in the API (better be a prime) +** Size of cache for strings in the API. 'N' is the number of +** sets (better be a prime) and "M" is the size of each set (M == 1 +** makes a direct cache.) */ -#if !defined(STRCACHE_SIZE) -#define STRCACHE_SIZE 127 +#if !defined(STRCACHE_N) +#define STRCACHE_N 53 +#define STRCACHE_M 2 #endif @@ -198,7 +207,7 @@ typedef unsigned long Instruction; /* -** macros that are executed whenether program enters the Lua core +** macros that are executed whenever program enters the Lua core ** ('lua_lock') and leaves the core ('lua_unlock') */ #if !defined(lua_lock) @@ -297,17 +306,18 @@ typedef unsigned long Instruction; ** macro to control inclusion of some hard tests on stack reallocation */ #if !defined(HARDSTACKTESTS) -#define condmovestack(L) ((void)0) +#define condmovestack(L,pre,pos) ((void)0) #else /* realloc stack keeping its size */ -#define condmovestack(L) luaD_reallocstack((L), (L)->stacksize) +#define condmovestack(L,pre,pos) \ + { int sz_ = (L)->stacksize; pre; luaD_reallocstack((L), sz_); pos; } #endif #if !defined(HARDMEMTESTS) -#define condchangemem(L) condmovestack(L) +#define condchangemem(L,pre,pos) ((void)0) #else -#define condchangemem(L) \ - ((void)(!(G(L)->gcrunning) || (luaC_fullgc(L, 0), 1))) +#define condchangemem(L,pre,pos) \ + { if (G(L)->gcrunning) { pre; luaC_fullgc(L, 0); pos; } } #endif #endif diff --git a/libs/lua-5.3.1/src/lmathlib.c b/libs/lua-5.3.2/src/lmathlib.c similarity index 98% rename from libs/lua-5.3.1/src/lmathlib.c rename to libs/lua-5.3.2/src/lmathlib.c index 4f2ec60..94815f1 100644 --- a/libs/lua-5.3.1/src/lmathlib.c +++ b/libs/lua-5.3.2/src/lmathlib.c @@ -1,5 +1,5 @@ /* -** $Id: lmathlib.c,v 1.115 2015/03/12 14:04:04 roberto Exp $ +** $Id: lmathlib.c,v 1.117 2015/10/02 15:39:23 roberto Exp $ ** Standard mathematical library ** See Copyright Notice in lua.h */ @@ -39,7 +39,7 @@ static int math_abs (lua_State *L) { if (lua_isinteger(L, 1)) { lua_Integer n = lua_tointeger(L, 1); - if (n < 0) n = (lua_Integer)(0u - n); + if (n < 0) n = (lua_Integer)(0u - (lua_Unsigned)n); lua_pushinteger(L, n); } else @@ -273,7 +273,7 @@ static int math_random (lua_State *L) { static int math_randomseed (lua_State *L) { l_srand((unsigned int)(lua_Integer)luaL_checknumber(L, 1)); - (void)rand(); /* discard first value to avoid undesirable correlations */ + (void)l_rand(); /* discard first value to avoid undesirable correlations */ return 0; } diff --git a/libs/lua-5.3.1/src/lmem.c b/libs/lua-5.3.2/src/lmem.c similarity index 100% rename from libs/lua-5.3.1/src/lmem.c rename to libs/lua-5.3.2/src/lmem.c diff --git a/libs/lua-5.3.1/src/lmem.h b/libs/lua-5.3.2/src/lmem.h similarity index 100% rename from libs/lua-5.3.1/src/lmem.h rename to libs/lua-5.3.2/src/lmem.h diff --git a/libs/lua-5.3.1/src/loadlib.c b/libs/lua-5.3.2/src/loadlib.c similarity index 99% rename from libs/lua-5.3.1/src/loadlib.c rename to libs/lua-5.3.2/src/loadlib.c index bbf8f67..7911928 100644 --- a/libs/lua-5.3.1/src/loadlib.c +++ b/libs/lua-5.3.2/src/loadlib.c @@ -1,5 +1,5 @@ /* -** $Id: loadlib.c,v 1.126 2015/02/16 13:14:33 roberto Exp $ +** $Id: loadlib.c,v 1.127 2015/11/23 11:30:45 roberto Exp $ ** Dynamic library loader for Lua ** See Copyright Notice in lua.h ** @@ -732,7 +732,7 @@ static void createsearcherstable (lua_State *L) { int i; /* create 'searchers' table */ lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0); - /* fill it with pre-defined searchers */ + /* fill it with predefined searchers */ for (i=0; searchers[i] != NULL; i++) { lua_pushvalue(L, -2); /* set 'package' as upvalue for all searchers */ lua_pushcclosure(L, searchers[i], 1); diff --git a/libs/lua-5.3.1/src/lobject.c b/libs/lua-5.3.2/src/lobject.c similarity index 95% rename from libs/lua-5.3.1/src/lobject.c rename to libs/lua-5.3.2/src/lobject.c index 6c53b98..e24723f 100644 --- a/libs/lua-5.3.1/src/lobject.c +++ b/libs/lua-5.3.2/src/lobject.c @@ -1,5 +1,5 @@ /* -** $Id: lobject.c,v 2.104 2015/04/11 18:30:08 roberto Exp $ +** $Id: lobject.c,v 2.108 2015/11/02 16:09:30 roberto Exp $ ** Some generic functions over Lua objects ** See Copyright Notice in lua.h */ @@ -55,9 +55,7 @@ int luaO_int2fb (unsigned int x) { /* converts back */ int luaO_fb2int (int x) { - int e = (x >> 3) & 0x1f; - if (e == 0) return x; - else return ((x & 7) + 8) << (e - 1); + return (x < 8) ? x : ((x & 7) + 8) << ((x >> 3) - 1); } @@ -333,9 +331,9 @@ void luaO_tostring (lua_State *L, StkId obj) { size_t len; lua_assert(ttisnumber(obj)); if (ttisinteger(obj)) - len = lua_integer2str(buff, ivalue(obj)); + len = lua_integer2str(buff, sizeof(buff), ivalue(obj)); else { - len = lua_number2str(buff, fltvalue(obj)); + len = lua_number2str(buff, sizeof(buff), fltvalue(obj)); #if !defined(LUA_COMPAT_FLOATSTRING) if (buff[strspn(buff, "-0123456789")] == '\0') { /* looks like an int? */ buff[len++] = lua_getlocaledecpoint(); @@ -348,7 +346,8 @@ void luaO_tostring (lua_State *L, StkId obj) { static void pushstr (lua_State *L, const char *str, size_t l) { - setsvalue2s(L, L->top++, luaS_newlstr(L, str, l)); + setsvalue2s(L, L->top, luaS_newlstr(L, str, l)); + luaD_inctop(L); } @@ -359,7 +358,6 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { for (;;) { const char *e = strchr(fmt, '%'); if (e == NULL) break; - luaD_checkstack(L, 2); /* fmt + item */ pushstr(L, fmt, e - fmt); switch (*(e+1)) { case 's': { @@ -377,23 +375,23 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { break; } case 'd': { - setivalue(L->top++, va_arg(argp, int)); - luaO_tostring(L, L->top - 1); - break; + setivalue(L->top, va_arg(argp, int)); + goto top2str; } case 'I': { - setivalue(L->top++, cast(lua_Integer, va_arg(argp, l_uacInt))); - luaO_tostring(L, L->top - 1); - break; + setivalue(L->top, cast(lua_Integer, va_arg(argp, l_uacInt))); + goto top2str; } case 'f': { - setfltvalue(L->top++, cast_num(va_arg(argp, l_uacNumber))); + setfltvalue(L->top, cast_num(va_arg(argp, l_uacNumber))); + top2str: + luaD_inctop(L); luaO_tostring(L, L->top - 1); break; } case 'p': { char buff[4*sizeof(void *) + 8]; /* should be enough space for a '%p' */ - int l = sprintf(buff, "%p", va_arg(argp, void *)); + int l = l_sprintf(buff, sizeof(buff), "%p", va_arg(argp, void *)); pushstr(L, buff, l); break; } diff --git a/libs/lua-5.3.1/src/lobject.h b/libs/lua-5.3.2/src/lobject.h similarity index 93% rename from libs/lua-5.3.1/src/lobject.h rename to libs/lua-5.3.2/src/lobject.h index 9230b7a..2d52b41 100644 --- a/libs/lua-5.3.1/src/lobject.h +++ b/libs/lua-5.3.2/src/lobject.h @@ -1,5 +1,5 @@ /* -** $Id: lobject.h,v 2.111 2015/06/09 14:21:42 roberto Exp $ +** $Id: lobject.h,v 2.116 2015/11/03 18:33:10 roberto Exp $ ** Type definitions for Lua objects ** See Copyright Notice in lua.h */ @@ -19,8 +19,8 @@ /* ** Extra tags for non-values */ -#define LUA_TPROTO LUA_NUMTAGS -#define LUA_TDEADKEY (LUA_NUMTAGS+1) +#define LUA_TPROTO LUA_NUMTAGS /* function prototypes */ +#define LUA_TDEADKEY (LUA_NUMTAGS+1) /* removed keys in tables */ /* ** number of all possible tags (including LUA_TNONE but excluding DEADKEY) @@ -88,22 +88,32 @@ struct GCObject { -/* -** Union of all Lua values -*/ -typedef union Value Value; - - - /* ** Tagged Values. This is the basic representation of values in Lua, ** an actual value plus a tag with its type. */ +/* +** Union of all Lua values +*/ +typedef union Value { + GCObject *gc; /* collectable objects */ + void *p; /* light userdata */ + int b; /* booleans */ + lua_CFunction f; /* light C functions */ + lua_Integer i; /* integer numbers */ + lua_Number n; /* float numbers */ +} Value; + + #define TValuefields Value value_; int tt_ -typedef struct lua_TValue TValue; + +typedef struct lua_TValue { + TValuefields; +} TValue; + /* macro defining a nil value */ @@ -177,9 +187,9 @@ typedef struct lua_TValue TValue; /* Macros for internal tests */ #define righttt(obj) (ttype(obj) == gcvalue(obj)->tt) -#define checkliveness(g,obj) \ +#define checkliveness(L,obj) \ lua_longassert(!iscollectable(obj) || \ - (righttt(obj) && !isdead(g,gcvalue(obj)))) + (righttt(obj) && (L == NULL || !isdead(G(L),gcvalue(obj))))) /* Macros to set values */ @@ -215,32 +225,32 @@ typedef struct lua_TValue TValue; #define setsvalue(L,obj,x) \ { TValue *io = (obj); TString *x_ = (x); \ val_(io).gc = obj2gco(x_); settt_(io, ctb(x_->tt)); \ - checkliveness(G(L),io); } + checkliveness(L,io); } #define setuvalue(L,obj,x) \ { TValue *io = (obj); Udata *x_ = (x); \ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TUSERDATA)); \ - checkliveness(G(L),io); } + checkliveness(L,io); } #define setthvalue(L,obj,x) \ { TValue *io = (obj); lua_State *x_ = (x); \ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TTHREAD)); \ - checkliveness(G(L),io); } + checkliveness(L,io); } #define setclLvalue(L,obj,x) \ { TValue *io = (obj); LClosure *x_ = (x); \ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TLCL)); \ - checkliveness(G(L),io); } + checkliveness(L,io); } #define setclCvalue(L,obj,x) \ { TValue *io = (obj); CClosure *x_ = (x); \ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TCCL)); \ - checkliveness(G(L),io); } + checkliveness(L,io); } #define sethvalue(L,obj,x) \ { TValue *io = (obj); Table *x_ = (x); \ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TTABLE)); \ - checkliveness(G(L),io); } + checkliveness(L,io); } #define setdeadvalue(obj) settt_(obj, LUA_TDEADKEY) @@ -248,7 +258,7 @@ typedef struct lua_TValue TValue; #define setobj(L,obj1,obj2) \ { TValue *io1=(obj1); *io1 = *(obj2); \ - (void)L; checkliveness(G(L),io1); } + (void)L; checkliveness(L,io1); } /* @@ -264,12 +274,13 @@ typedef struct lua_TValue TValue; #define setptvalue2s setptvalue /* from table to same table */ #define setobjt2t setobj -/* to table */ -#define setobj2t setobj /* to new object */ #define setobj2n setobj #define setsvalue2n setsvalue +/* to table (define it as an expression to be used in macros) */ +#define setobj2t(L,o1,o2) ((void)L, *(o1)=*(o2), checkliveness(L,(o1))) + @@ -280,21 +291,6 @@ typedef struct lua_TValue TValue; */ -union Value { - GCObject *gc; /* collectable objects */ - void *p; /* light userdata */ - int b; /* booleans */ - lua_CFunction f; /* light C functions */ - lua_Integer i; /* integer numbers */ - lua_Number n; /* float numbers */ -}; - - -struct lua_TValue { - TValuefields; -}; - - typedef TValue *StkId; /* index to stack elements */ @@ -329,9 +325,9 @@ typedef union UTString { ** Get the actual string (array of bytes) from a 'TString'. ** (Access to 'extra' ensures that value is really a 'TString'.) */ -#define getaddrstr(ts) (cast(char *, (ts)) + sizeof(UTString)) #define getstr(ts) \ - check_exp(sizeof((ts)->extra), cast(const char*, getaddrstr(ts))) + check_exp(sizeof((ts)->extra), cast(char *, (ts)) + sizeof(UTString)) + /* get the actual string (array of bytes) from a Lua value */ #define svalue(o) getstr(tsvalue(o)) @@ -375,13 +371,13 @@ typedef union UUdata { #define setuservalue(L,u,o) \ { const TValue *io=(o); Udata *iu = (u); \ iu->user_ = io->value_; iu->ttuv_ = rttype(io); \ - checkliveness(G(L),io); } + checkliveness(L,io); } #define getuservalue(L,u,o) \ { TValue *io=(o); const Udata *iu = (u); \ io->value_ = iu->user_; settt_(io, iu->ttuv_); \ - checkliveness(G(L),io); } + checkliveness(L,io); } /* @@ -411,7 +407,7 @@ typedef struct LocVar { typedef struct Proto { CommonHeader; lu_byte numparams; /* number of fixed parameters */ - lu_byte is_vararg; + lu_byte is_vararg; /* 2: declared vararg; 1: uses vararg */ lu_byte maxstacksize; /* number of registers needed by this function */ int sizeupvalues; /* size of 'upvalues' */ int sizek; /* size of 'k' */ @@ -419,8 +415,8 @@ typedef struct Proto { int sizelineinfo; int sizep; /* size of 'p' */ int sizelocvars; - int linedefined; - int lastlinedefined; + int linedefined; /* debug information */ + int lastlinedefined; /* debug information */ TValue *k; /* constants used by the function */ Instruction *code; /* opcodes */ struct Proto **p; /* functions defined inside the function */ @@ -489,7 +485,7 @@ typedef union TKey { #define setnodekey(L,key,obj) \ { TKey *k_=(key); const TValue *io_=(obj); \ k_->nk.value_ = io_->value_; k_->nk.tt_ = io_->tt_; \ - (void)L; checkliveness(G(L),io_); } + (void)L; checkliveness(L,io_); } typedef struct Node { diff --git a/libs/lua-5.3.1/src/lopcodes.c b/libs/lua-5.3.2/src/lopcodes.c similarity index 100% rename from libs/lua-5.3.1/src/lopcodes.c rename to libs/lua-5.3.2/src/lopcodes.c diff --git a/libs/lua-5.3.1/src/lopcodes.h b/libs/lua-5.3.2/src/lopcodes.h similarity index 100% rename from libs/lua-5.3.1/src/lopcodes.h rename to libs/lua-5.3.2/src/lopcodes.h diff --git a/libs/lua-5.3.1/src/loslib.c b/libs/lua-5.3.2/src/loslib.c similarity index 82% rename from libs/lua-5.3.1/src/loslib.c rename to libs/lua-5.3.2/src/loslib.c index cb8a3c3..7dae533 100644 --- a/libs/lua-5.3.1/src/loslib.c +++ b/libs/lua-5.3.2/src/loslib.c @@ -1,5 +1,5 @@ /* -** $Id: loslib.c,v 1.57 2015/04/10 17:41:04 roberto Exp $ +** $Id: loslib.c,v 1.60 2015/11/19 19:16:22 roberto Exp $ ** Standard Operating System library ** See Copyright Notice in lua.h */ @@ -54,7 +54,12 @@ */ #define l_timet lua_Integer #define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t)) -#define l_checktime(L,a) ((time_t)luaL_checkinteger(L,a)) + +static time_t l_checktime (lua_State *L, int arg) { + lua_Integer t = luaL_checkinteger(L, arg); + luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds"); + return (time_t)t; +} #endif /* } */ @@ -198,17 +203,29 @@ static int getboolfield (lua_State *L, const char *key) { } -static int getfield (lua_State *L, const char *key, int d) { - int res, isnum; - lua_getfield(L, -1, key); - res = (int)lua_tointegerx(L, -1, &isnum); - if (!isnum) { - if (d < 0) +/* maximum value for date fields (to avoid arithmetic overflows with 'int') */ +#if !defined(L_MAXDATEFIELD) +#define L_MAXDATEFIELD (INT_MAX / 2) +#endif + +static int getfield (lua_State *L, const char *key, int d, int delta) { + int isnum; + int t = lua_getfield(L, -1, key); + lua_Integer res = lua_tointegerx(L, -1, &isnum); + if (!isnum) { /* field is not a number? */ + if (t != LUA_TNIL) /* some other value? */ + return luaL_error(L, "field '%s' not an integer", key); + else if (d < 0) /* absent field; no default? */ return luaL_error(L, "field '%s' missing in date table", key); res = d; } + else { + if (!(-L_MAXDATEFIELD <= res && res <= L_MAXDATEFIELD)) + return luaL_error(L, "field '%s' out-of-bounds", key); + res -= delta; + } lua_pop(L, 1); - return res; + return (int)res; } @@ -236,6 +253,10 @@ static const char *checkoption (lua_State *L, const char *conv, char *buff) { } +/* maximum size for an individual 'strftime' item */ +#define SIZETIMEFMT 250 + + static int os_date (lua_State *L) { const char *s = luaL_optstring(L, 1, "%c"); time_t t = luaL_opt(L, l_checktime, 2, time(NULL)); @@ -247,8 +268,8 @@ static int os_date (lua_State *L) { else stm = l_localtime(&t, &tmr); if (stm == NULL) /* invalid date? */ - lua_pushnil(L); - else if (strcmp(s, "*t") == 0) { + luaL_error(L, "time result cannot be represented in this installation"); + if (strcmp(s, "*t") == 0) { lua_createtable(L, 0, 9); /* 9 = number of fields */ setfield(L, "sec", stm->tm_sec); setfield(L, "min", stm->tm_min); @@ -266,14 +287,14 @@ static int os_date (lua_State *L) { cc[0] = '%'; luaL_buffinit(L, &b); while (*s) { - if (*s != '%') /* no conversion specifier? */ + if (*s != '%') /* not a conversion specifier? */ luaL_addchar(&b, *s++); else { size_t reslen; - char buff[200]; /* should be big enough for any conversion result */ + char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT); s = checkoption(L, s + 1, cc); - reslen = strftime(buff, sizeof(buff), cc, stm); - luaL_addlstring(&b, buff, reslen); + reslen = strftime(buff, SIZETIMEFMT, cc, stm); + luaL_addsize(&b, reslen); } } luaL_pushresult(&b); @@ -290,21 +311,18 @@ static int os_time (lua_State *L) { struct tm ts; luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 1); /* make sure table is at the top */ - ts.tm_sec = getfield(L, "sec", 0); - ts.tm_min = getfield(L, "min", 0); - ts.tm_hour = getfield(L, "hour", 12); - ts.tm_mday = getfield(L, "day", -1); - ts.tm_mon = getfield(L, "month", -1) - 1; - ts.tm_year = getfield(L, "year", -1) - 1900; + ts.tm_sec = getfield(L, "sec", 0, 0); + ts.tm_min = getfield(L, "min", 0, 0); + ts.tm_hour = getfield(L, "hour", 12, 0); + ts.tm_mday = getfield(L, "day", -1, 0); + ts.tm_mon = getfield(L, "month", -1, 1); + ts.tm_year = getfield(L, "year", -1, 1900); ts.tm_isdst = getboolfield(L, "isdst"); t = mktime(&ts); } - if (t != (time_t)(l_timet)t) - luaL_error(L, "time result cannot be represented in this Lua installation"); - else if (t == (time_t)(-1)) - lua_pushnil(L); - else - l_pushtime(L, t); + if (t != (time_t)(l_timet)t || t == (time_t)(-1)) + luaL_error(L, "time result cannot be represented in this installation"); + l_pushtime(L, t); return 1; } diff --git a/libs/lua-5.3.1/src/lparser.c b/libs/lua-5.3.2/src/lparser.c similarity index 99% rename from libs/lua-5.3.1/src/lparser.c rename to libs/lua-5.3.2/src/lparser.c index 9a54dfc..282a6b1 100644 --- a/libs/lua-5.3.1/src/lparser.c +++ b/libs/lua-5.3.2/src/lparser.c @@ -1,5 +1,5 @@ /* -** $Id: lparser.c,v 2.147 2014/12/27 20:31:43 roberto Exp $ +** $Id: lparser.c,v 2.149 2015/11/02 16:09:30 roberto Exp $ ** Lua Parser ** See Copyright Notice in lua.h */ @@ -760,7 +760,7 @@ static void parlist (LexState *ls) { } case TK_DOTS: { /* param -> '...' */ luaX_next(ls); - f->is_vararg = 1; + f->is_vararg = 2; /* declared vararg */ break; } default: luaX_syntaxerror(ls, " or '...' expected"); @@ -956,6 +956,7 @@ static void simpleexp (LexState *ls, expdesc *v) { FuncState *fs = ls->fs; check_condition(ls, fs->f->is_vararg, "cannot use '...' outside a vararg function"); + fs->f->is_vararg = 1; /* function actually uses vararg */ init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0)); break; } @@ -1610,7 +1611,7 @@ static void mainfunc (LexState *ls, FuncState *fs) { BlockCnt bl; expdesc v; open_func(ls, fs, &bl); - fs->f->is_vararg = 1; /* main function is always vararg */ + fs->f->is_vararg = 2; /* main function is always declared vararg */ init_exp(&v, VLOCAL, 0); /* create and... */ newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */ luaX_next(ls); /* read first token */ @@ -1626,10 +1627,10 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, FuncState funcstate; LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */ setclLvalue(L, L->top, cl); /* anchor it (to avoid being collected) */ - incr_top(L); + luaD_inctop(L); lexstate.h = luaH_new(L); /* create table for scanner */ sethvalue(L, L->top, lexstate.h); /* anchor it */ - incr_top(L); + luaD_inctop(L); funcstate.f = cl->p = luaF_newproto(L); funcstate.f->source = luaS_new(L, name); /* create and anchor TString */ lua_assert(iswhite(funcstate.f)); /* do not need barrier here */ diff --git a/libs/lua-5.3.1/src/lparser.h b/libs/lua-5.3.2/src/lparser.h similarity index 100% rename from libs/lua-5.3.1/src/lparser.h rename to libs/lua-5.3.2/src/lparser.h diff --git a/libs/lua-5.3.1/src/lprefix.h b/libs/lua-5.3.2/src/lprefix.h similarity index 100% rename from libs/lua-5.3.1/src/lprefix.h rename to libs/lua-5.3.2/src/lprefix.h diff --git a/libs/lua-5.3.1/src/lstate.c b/libs/lua-5.3.2/src/lstate.c similarity index 92% rename from libs/lua-5.3.1/src/lstate.c rename to libs/lua-5.3.2/src/lstate.c index 12e51d2..9194ac3 100644 --- a/libs/lua-5.3.1/src/lstate.c +++ b/libs/lua-5.3.2/src/lstate.c @@ -1,5 +1,5 @@ /* -** $Id: lstate.c,v 2.128 2015/03/04 13:31:21 roberto Exp $ +** $Id: lstate.c,v 2.133 2015/11/13 12:16:51 roberto Exp $ ** Global State ** See Copyright Notice in lua.h */ @@ -76,7 +76,7 @@ typedef struct LG { */ #define addbuff(b,p,e) \ { size_t t = cast(size_t, e); \ - memcpy(buff + p, &t, sizeof(t)); p += sizeof(t); } + memcpy(b + p, &t, sizeof(t)); p += sizeof(t); } static unsigned int makeseed (lua_State *L) { char buff[4 * sizeof(size_t)]; @@ -93,10 +93,14 @@ static unsigned int makeseed (lua_State *L) { /* ** set GCdebt to a new value keeping the value (totalbytes + GCdebt) -** invariant +** invariant (and avoiding underflows in 'totalbytes') */ void luaE_setdebt (global_State *g, l_mem debt) { - g->totalbytes -= (debt - g->GCdebt); + l_mem tb = gettotalbytes(g); + lua_assert(tb > 0); + if (debt < tb - MAX_LMEM) + debt = tb - MAX_LMEM; /* will make 'totalbytes == MAX_LMEM' */ + g->totalbytes = tb - debt; g->GCdebt = debt; } @@ -107,6 +111,7 @@ CallInfo *luaE_extendCI (lua_State *L) { L->ci->next = ci; ci->previous = L->ci; ci->next = NULL; + L->nci++; return ci; } @@ -121,6 +126,7 @@ void luaE_freeCI (lua_State *L) { while ((ci = next) != NULL) { next = ci->next; luaM_free(L, ci); + L->nci--; } } @@ -130,13 +136,14 @@ void luaE_freeCI (lua_State *L) { */ void luaE_shrinkCI (lua_State *L) { CallInfo *ci = L->ci; - while (ci->next != NULL) { /* while there is 'next' */ - CallInfo *next2 = ci->next->next; /* next's next */ - if (next2 == NULL) break; - luaM_free(L, ci->next); /* remove next */ + CallInfo *next2; /* next's next */ + /* while there are two nexts */ + while (ci->next != NULL && (next2 = ci->next->next) != NULL) { + luaM_free(L, ci->next); /* free next */ + L->nci--; ci->next = next2; /* remove 'next' from the list */ next2->previous = ci; - ci = next2; + ci = next2; /* keep next's next */ } } @@ -166,6 +173,7 @@ static void freestack (lua_State *L) { return; /* stack not completely built yet */ L->ci = &L->base_ci; /* free the entire 'ci' list */ luaE_freeCI(L); + lua_assert(L->nci == 0); luaM_freearray(L, L->stack, L->stacksize); /* free stack array */ } @@ -214,6 +222,7 @@ static void preinit_thread (lua_State *L, global_State *g) { G(L) = g; L->stack = NULL; L->ci = NULL; + L->nci = 0; L->stacksize = 0; L->twups = L; /* thread has no upvalues */ L->errorJmp = NULL; @@ -237,7 +246,6 @@ static void close_state (lua_State *L) { if (g->version) /* closing a fully built state? */ luai_userstateclose(L); luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size); - luaZ_freebuffer(L, &g->buff); freestack(L); lua_assert(gettotalbytes(g) == sizeof(LG)); (*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */ @@ -306,7 +314,6 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { g->strt.size = g->strt.nuse = 0; g->strt.hash = NULL; setnilvalue(&g->l_registry); - luaZ_initbuffer(L, &g->buff); g->panic = NULL; g->version = NULL; g->gcstate = GCSpause; diff --git a/libs/lua-5.3.1/src/lstate.h b/libs/lua-5.3.2/src/lstate.h similarity index 93% rename from libs/lua-5.3.1/src/lstate.h rename to libs/lua-5.3.2/src/lstate.h index eefc217..65c914d 100644 --- a/libs/lua-5.3.1/src/lstate.h +++ b/libs/lua-5.3.2/src/lstate.h @@ -1,5 +1,5 @@ /* -** $Id: lstate.h,v 2.122 2015/06/01 16:34:37 roberto Exp $ +** $Id: lstate.h,v 2.128 2015/11/13 12:16:51 roberto Exp $ ** Global State ** See Copyright Notice in lua.h */ @@ -89,8 +89,8 @@ typedef struct CallInfo { #define CIST_OAH (1<<0) /* original value of 'allowhook' */ #define CIST_LUA (1<<1) /* call is running a Lua function */ #define CIST_HOOKED (1<<2) /* call is running a debug hook */ -#define CIST_REENTRY (1<<3) /* call is running on same invocation of - luaV_execute of previous call */ +#define CIST_FRESH (1<<3) /* call is running on a fresh invocation + of luaV_execute */ #define CIST_YPCALL (1<<4) /* call is a yieldable protected call */ #define CIST_TAIL (1<<5) /* call was tail called */ #define CIST_HOOKYIELD (1<<6) /* last hook called yielded */ @@ -109,7 +109,7 @@ typedef struct CallInfo { typedef struct global_State { lua_Alloc frealloc; /* function to reallocate memory */ void *ud; /* auxiliary data to 'frealloc' */ - lu_mem totalbytes; /* number of bytes currently allocated - GCdebt */ + l_mem totalbytes; /* number of bytes currently allocated - GCdebt */ l_mem GCdebt; /* bytes allocated not yet compensated by the collector */ lu_mem GCmemtrav; /* memory traversed by the GC */ lu_mem GCestimate; /* an estimate of the non-garbage memory in use */ @@ -131,7 +131,6 @@ typedef struct global_State { GCObject *tobefnz; /* list of userdata to be GC */ GCObject *fixedgc; /* list of objects not to be collected */ struct lua_State *twups; /* list of threads with open upvalues */ - Mbuffer buff; /* temporary buffer for string concatenation */ unsigned int gcfinnum; /* number of finalizers to call in each GC step */ int gcpause; /* size of pause between successive GCs */ int gcstepmul; /* GC 'granularity' */ @@ -141,7 +140,7 @@ typedef struct global_State { TString *memerrmsg; /* memory-error message */ TString *tmname[TM_N]; /* array with tag-method names */ struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */ - TString *strcache[STRCACHE_SIZE][1]; /* cache for strings in API */ + TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ } global_State; @@ -150,6 +149,7 @@ typedef struct global_State { */ struct lua_State { CommonHeader; + unsigned short nci; /* number of items in 'ci' list */ lu_byte status; StkId top; /* first free slot in the stack */ global_State *l_G; @@ -212,7 +212,7 @@ union GCUnion { /* actual number of total bytes allocated */ -#define gettotalbytes(g) ((g)->totalbytes + (g)->GCdebt) +#define gettotalbytes(g) cast(lu_mem, (g)->totalbytes + (g)->GCdebt) LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); diff --git a/libs/lua-5.3.1/src/lstring.c b/libs/lua-5.3.2/src/lstring.c similarity index 73% rename from libs/lua-5.3.1/src/lstring.c rename to libs/lua-5.3.2/src/lstring.c index 5e0e3c4..9351766 100644 --- a/libs/lua-5.3.1/src/lstring.c +++ b/libs/lua-5.3.2/src/lstring.c @@ -1,5 +1,5 @@ /* -** $Id: lstring.c,v 2.49 2015/06/01 16:34:37 roberto Exp $ +** $Id: lstring.c,v 2.56 2015/11/23 11:32:51 roberto Exp $ ** String table (keeps all strings handled by Lua) ** See Copyright Notice in lua.h */ @@ -48,14 +48,23 @@ int luaS_eqlngstr (TString *a, TString *b) { unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) { unsigned int h = seed ^ cast(unsigned int, l); - size_t l1; size_t step = (l >> LUAI_HASHLIMIT) + 1; - for (l1 = l; l1 >= step; l1 -= step) - h = h ^ ((h<<5) + (h>>2) + cast_byte(str[l1 - 1])); + for (; l >= step; l -= step) + h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1])); return h; } +unsigned int luaS_hashlongstr (TString *ts) { + lua_assert(ts->tt == LUA_TLNGSTR); + if (ts->extra == 0) { /* no hash? */ + ts->hash = luaS_hash(getstr(ts), ts->u.lnglen, ts->hash); + ts->extra = 1; /* now it has its hash */ + } + return ts->hash; +} + + /* ** resizes the string table */ @@ -92,11 +101,12 @@ void luaS_resize (lua_State *L, int newsize) { ** a non-collectable string.) */ void luaS_clearcache (global_State *g) { - int i; - for (i = 0; i < STRCACHE_SIZE; i++) { - if (iswhite(g->strcache[i][0])) /* will entry be collected? */ - g->strcache[i][0] = g->memerrmsg; /* replace it with something fixed */ - } + int i, j; + for (i = 0; i < STRCACHE_N; i++) + for (j = 0; j < STRCACHE_M; j++) { + if (iswhite(g->strcache[i][j])) /* will entry be collected? */ + g->strcache[i][j] = g->memerrmsg; /* replace it with something fixed */ + } } @@ -105,13 +115,14 @@ void luaS_clearcache (global_State *g) { */ void luaS_init (lua_State *L) { global_State *g = G(L); - int i; + int i, j; luaS_resize(L, MINSTRTABSIZE); /* initial size of string table */ /* pre-create memory-error message */ g->memerrmsg = luaS_newliteral(L, MEMERRMSG); luaC_fix(L, obj2gco(g->memerrmsg)); /* it should never be collected */ - for (i = 0; i < STRCACHE_SIZE; i++) /* fill cache with valid strings */ - g->strcache[i][0] = g->memerrmsg; + for (i = 0; i < STRCACHE_N; i++) /* fill cache with valid strings */ + for (j = 0; j < STRCACHE_M; j++) + g->strcache[i][j] = g->memerrmsg; } @@ -119,8 +130,7 @@ void luaS_init (lua_State *L) { /* ** creates a new string object */ -static TString *createstrobj (lua_State *L, const char *str, size_t l, - int tag, unsigned int h) { +static TString *createstrobj (lua_State *L, size_t l, int tag, unsigned int h) { TString *ts; GCObject *o; size_t totalsize; /* total size of TString object */ @@ -129,8 +139,14 @@ static TString *createstrobj (lua_State *L, const char *str, size_t l, ts = gco2ts(o); ts->hash = h; ts->extra = 0; - memcpy(getaddrstr(ts), str, l * sizeof(char)); - getaddrstr(ts)[l] = '\0'; /* ending 0 */ + getstr(ts)[l] = '\0'; /* ending 0 */ + return ts; +} + + +TString *luaS_createlngstrobj (lua_State *L, size_t l) { + TString *ts = createstrobj(L, l, LUA_TLNGSTR, G(L)->seed); + ts->u.lnglen = l; return ts; } @@ -153,6 +169,7 @@ static TString *internshrstr (lua_State *L, const char *str, size_t l) { global_State *g = G(L); unsigned int h = luaS_hash(str, l, g->seed); TString **list = &g->strt.hash[lmod(h, g->strt.size)]; + lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */ for (ts = *list; ts != NULL; ts = ts->u.hnext) { if (l == ts->shrlen && (memcmp(str, getstr(ts), l * sizeof(char)) == 0)) { @@ -166,7 +183,8 @@ static TString *internshrstr (lua_State *L, const char *str, size_t l) { luaS_resize(L, g->strt.size * 2); list = &g->strt.hash[lmod(h, g->strt.size)]; /* recompute with new size */ } - ts = createstrobj(L, str, l, LUA_TSHRSTR, h); + ts = createstrobj(L, l, LUA_TSHRSTR, h); + memcpy(getstr(ts), str, l * sizeof(char)); ts->shrlen = cast_byte(l); ts->u.hnext = *list; *list = ts; @@ -183,10 +201,10 @@ TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { return internshrstr(L, str, l); else { TString *ts; - if (l + 1 > (MAX_SIZE - sizeof(TString))/sizeof(char)) + if (l >= (MAX_SIZE - sizeof(TString))/sizeof(char)) luaM_toobig(L); - ts = createstrobj(L, str, l, LUA_TLNGSTR, G(L)->seed); - ts->u.lnglen = l; + ts = luaS_createlngstrobj(L, l); + memcpy(getstr(ts), str, l * sizeof(char)); return ts; } } @@ -199,15 +217,19 @@ TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { ** check hits. */ TString *luaS_new (lua_State *L, const char *str) { - unsigned int i = point2uint(str) % STRCACHE_SIZE; /* hash */ + unsigned int i = point2uint(str) % STRCACHE_N; /* hash */ + int j; TString **p = G(L)->strcache[i]; - if (strcmp(str, getstr(p[0])) == 0) /* hit? */ - return p[0]; /* that it is */ - else { /* normal route */ - TString *s = luaS_newlstr(L, str, strlen(str)); - p[0] = s; - return s; + for (j = 0; j < STRCACHE_M; j++) { + if (strcmp(str, getstr(p[j])) == 0) /* hit? */ + return p[j]; /* that is it */ } + /* normal route */ + for (j = STRCACHE_M - 1; j > 0; j--) + p[j] = p[j - 1]; /* move out last element */ + /* new element is first in the list */ + p[0] = luaS_newlstr(L, str, strlen(str)); + return p[0]; } diff --git a/libs/lua-5.3.1/src/lstring.h b/libs/lua-5.3.2/src/lstring.h similarity index 87% rename from libs/lua-5.3.1/src/lstring.h rename to libs/lua-5.3.2/src/lstring.h index e746f5f..27efd20 100644 --- a/libs/lua-5.3.1/src/lstring.h +++ b/libs/lua-5.3.2/src/lstring.h @@ -1,5 +1,5 @@ /* -** $Id: lstring.h,v 1.59 2015/03/25 13:42:19 roberto Exp $ +** $Id: lstring.h,v 1.61 2015/11/03 15:36:01 roberto Exp $ ** String table (keep all strings handled by Lua) ** See Copyright Notice in lua.h */ @@ -34,6 +34,7 @@ LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed); +LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts); LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b); LUAI_FUNC void luaS_resize (lua_State *L, int newsize); LUAI_FUNC void luaS_clearcache (global_State *g); @@ -42,6 +43,7 @@ LUAI_FUNC void luaS_remove (lua_State *L, TString *ts); LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s); LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); LUAI_FUNC TString *luaS_new (lua_State *L, const char *str); +LUAI_FUNC TString *luaS_createlngstrobj (lua_State *L, size_t l); #endif diff --git a/libs/lua-5.3.1/src/lstrlib.c b/libs/lua-5.3.2/src/lstrlib.c similarity index 90% rename from libs/lua-5.3.1/src/lstrlib.c rename to libs/lua-5.3.2/src/lstrlib.c index 19c350d..fe30e34 100644 --- a/libs/lua-5.3.1/src/lstrlib.c +++ b/libs/lua-5.3.2/src/lstrlib.c @@ -1,5 +1,5 @@ /* -** $Id: lstrlib.c,v 1.229 2015/05/20 17:39:23 roberto Exp $ +** $Id: lstrlib.c,v 1.239 2015/11/25 16:28:17 roberto Exp $ ** Standard library for string operations and pattern-matching ** See Copyright Notice in lua.h */ @@ -41,8 +41,10 @@ ** Some sizes are better limited to fit in 'int', but must also fit in ** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.) */ +#define MAX_SIZET ((size_t)(~(size_t)0)) + #define MAXSIZE \ - (sizeof(size_t) < sizeof(int) ? (~(size_t)0) : (size_t)(INT_MAX)) + (sizeof(size_t) < sizeof(int) ? MAX_SIZET : (size_t)(INT_MAX)) @@ -208,11 +210,12 @@ static int str_dump (lua_State *L) { typedef struct MatchState { - int matchdepth; /* control for recursive depth (to avoid C stack overflow) */ const char *src_init; /* init of source string */ const char *src_end; /* end ('\0') of source string */ const char *p_end; /* end ('\0') of pattern */ lua_State *L; + size_t nrep; /* limit to avoid non-linear complexity */ + int matchdepth; /* control for recursive depth (to avoid C stack overflow) */ int level; /* total number of captures (finished or unfinished) */ struct { const char *init; @@ -231,6 +234,17 @@ static const char *match (MatchState *ms, const char *s, const char *p); #endif +/* +** parameters to control the maximum number of operators handled in +** a match (to avoid non-linear complexity). The maximum will be: +** (subject length) * A_REPS + B_REPS +*/ +#if !defined(A_REPS) +#define A_REPS 4 +#define B_REPS 100000 +#endif + + #define L_ESC '%' #define SPECIALS "^$*+?.([%-" @@ -488,6 +502,8 @@ static const char *match (MatchState *ms, const char *s, const char *p) { s = NULL; /* fail */ } else { /* matched once */ + if (ms->nrep-- == 0) + luaL_error(ms->L, "pattern too complex"); switch (*ep) { /* handle optional suffix */ case '?': { /* optional */ const char *res; @@ -584,6 +600,26 @@ static int nospecials (const char *p, size_t l) { } +static void prepstate (MatchState *ms, lua_State *L, + const char *s, size_t ls, const char *p, size_t lp) { + ms->L = L; + ms->matchdepth = MAXCCALLS; + ms->src_init = s; + ms->src_end = s + ls; + ms->p_end = p + lp; + if (ls < (MAX_SIZET - B_REPS) / A_REPS) + ms->nrep = A_REPS * ls + B_REPS; + else /* overflow (very long subject) */ + ms->nrep = MAX_SIZET; /* no limit */ +} + + +static void reprepstate (MatchState *ms) { + ms->level = 0; + lua_assert(ms->matchdepth == MAXCCALLS); +} + + static int str_find_aux (lua_State *L, int find) { size_t ls, lp; const char *s = luaL_checklstring(L, 1, &ls); @@ -611,15 +647,10 @@ static int str_find_aux (lua_State *L, int find) { if (anchor) { p++; lp--; /* skip anchor character */ } - ms.L = L; - ms.matchdepth = MAXCCALLS; - ms.src_init = s; - ms.src_end = s + ls; - ms.p_end = p + lp; + prepstate(&ms, L, s, ls, p, lp); do { const char *res; - ms.level = 0; - lua_assert(ms.matchdepth == MAXCCALLS); + reprepstate(&ms); if ((res=match(&ms, s1, p)) != NULL) { if (find) { lua_pushinteger(L, (s1 - s) + 1); /* start */ @@ -646,29 +677,26 @@ static int str_match (lua_State *L) { } +/* state for 'gmatch' */ +typedef struct GMatchState { + const char *src; /* current position */ + const char *p; /* pattern */ + MatchState ms; /* match state */ +} GMatchState; + + static int gmatch_aux (lua_State *L) { - MatchState ms; - size_t ls, lp; - const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls); - const char *p = lua_tolstring(L, lua_upvalueindex(2), &lp); + GMatchState *gm = (GMatchState *)lua_touserdata(L, lua_upvalueindex(3)); const char *src; - ms.L = L; - ms.matchdepth = MAXCCALLS; - ms.src_init = s; - ms.src_end = s+ls; - ms.p_end = p + lp; - for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3)); - src <= ms.src_end; - src++) { + for (src = gm->src; src <= gm->ms.src_end; src++) { const char *e; - ms.level = 0; - lua_assert(ms.matchdepth == MAXCCALLS); - if ((e = match(&ms, src, p)) != NULL) { - lua_Integer newstart = e-s; - if (e == src) newstart++; /* empty match? go at least one position */ - lua_pushinteger(L, newstart); - lua_replace(L, lua_upvalueindex(3)); - return push_captures(&ms, src, e); + reprepstate(&gm->ms); + if ((e = match(&gm->ms, src, gm->p)) != NULL) { + if (e == src) /* empty match? */ + gm->src =src + 1; /* go at least one position */ + else + gm->src = e; + return push_captures(&gm->ms, src, e); } } return 0; /* not found */ @@ -676,10 +704,14 @@ static int gmatch_aux (lua_State *L) { static int gmatch (lua_State *L) { - luaL_checkstring(L, 1); - luaL_checkstring(L, 2); - lua_settop(L, 2); - lua_pushinteger(L, 0); + size_t ls, lp; + const char *s = luaL_checklstring(L, 1, &ls); + const char *p = luaL_checklstring(L, 2, &lp); + GMatchState *gm; + lua_settop(L, 2); /* keep them on closure to avoid being collected */ + gm = (GMatchState *)lua_newuserdata(L, sizeof(GMatchState)); + prepstate(&gm->ms, L, s, ls, p, lp); + gm->src = s; gm->p = p; lua_pushcclosure(L, gmatch_aux, 3); return 1; } @@ -761,17 +793,11 @@ static int str_gsub (lua_State *L) { if (anchor) { p++; lp--; /* skip anchor character */ } - ms.L = L; - ms.matchdepth = MAXCCALLS; - ms.src_init = src; - ms.src_end = src+srcl; - ms.p_end = p + lp; + prepstate(&ms, L, src, srcl, p, lp); while (n < max_s) { const char *e; - ms.level = 0; - lua_assert(ms.matchdepth == MAXCCALLS); - e = match(&ms, src, p); - if (e) { + reprepstate(&ms); + if ((e = match(&ms, src, p)) != NULL) { n++; add_value(&ms, &b, src, e, tr); } @@ -830,13 +856,12 @@ static lua_Number adddigit (char *buff, int n, lua_Number x) { } -static int num2straux (char *buff, lua_Number x) { +static int num2straux (char *buff, int sz, lua_Number x) { if (x != x || x == HUGE_VAL || x == -HUGE_VAL) /* inf or NaN? */ - return sprintf(buff, LUA_NUMBER_FMT, x); /* equal to '%g' */ + return l_sprintf(buff, sz, LUA_NUMBER_FMT, x); /* equal to '%g' */ else if (x == 0) { /* can be -0... */ - sprintf(buff, LUA_NUMBER_FMT, x); - strcat(buff, "x0p+0"); /* reuses '0/-0' from 'sprintf'... */ - return strlen(buff); + /* create "0" or "-0" followed by exponent */ + return l_sprintf(buff, sz, LUA_NUMBER_FMT "x0p+0", x); } else { int e; @@ -855,15 +880,16 @@ static int num2straux (char *buff, lua_Number x) { m = adddigit(buff, n++, m * 16); } while (m > 0); } - n += sprintf(buff + n, "p%+d", e); /* add exponent */ + n += l_sprintf(buff + n, sz - n, "p%+d", e); /* add exponent */ + lua_assert(n < sz); return n; } } -static int lua_number2strx (lua_State *L, char *buff, const char *fmt, - lua_Number x) { - int n = num2straux(buff, x); +static int lua_number2strx (lua_State *L, char *buff, int sz, + const char *fmt, lua_Number x) { + int n = num2straux(buff, sz, x); if (fmt[SIZELENMOD] == 'A') { int i; for (i = 0; i < n; i++) @@ -879,10 +905,12 @@ static int lua_number2strx (lua_State *L, char *buff, const char *fmt, /* ** Maximum size of each formatted item. This maximum size is produced -** by format('%.99f', minfloat), and is equal to 99 + 2 ('-' and '.') + -** number of decimal digits to represent minfloat. +** by format('%.99f', -maxfloat), and is equal to 99 + 3 ('-', '.', +** and '\0') + number of decimal digits to represent maxfloat (which +** is maximum exponent + 1). (99+3+1 then rounded to 120 for "extra +** expenses", such as locale-dependent stuff) */ -#define MAX_ITEM (120 + l_mathlim(MAX_10_EXP)) +#define MAX_ITEM (120 + l_mathlim(MAX_10_EXP)) /* valid flags in a format specification */ @@ -906,9 +934,9 @@ static void addquoted (lua_State *L, luaL_Buffer *b, int arg) { else if (*s == '\0' || iscntrl(uchar(*s))) { char buff[10]; if (!isdigit(uchar(*(s+1)))) - sprintf(buff, "\\%d", (int)uchar(*s)); + l_sprintf(buff, sizeof(buff), "\\%d", (int)uchar(*s)); else - sprintf(buff, "\\%03d", (int)uchar(*s)); + l_sprintf(buff, sizeof(buff), "\\%03d", (int)uchar(*s)); luaL_addstring(b, buff); } else @@ -975,24 +1003,25 @@ static int str_format (lua_State *L) { strfrmt = scanformat(L, strfrmt, form); switch (*strfrmt++) { case 'c': { - nb = sprintf(buff, form, (int)luaL_checkinteger(L, arg)); + nb = l_sprintf(buff, MAX_ITEM, form, (int)luaL_checkinteger(L, arg)); break; } case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { lua_Integer n = luaL_checkinteger(L, arg); addlenmod(form, LUA_INTEGER_FRMLEN); - nb = sprintf(buff, form, n); + nb = l_sprintf(buff, MAX_ITEM, form, n); break; } case 'a': case 'A': addlenmod(form, LUA_NUMBER_FRMLEN); - nb = lua_number2strx(L, buff, form, luaL_checknumber(L, arg)); + nb = lua_number2strx(L, buff, MAX_ITEM, form, + luaL_checknumber(L, arg)); break; case 'e': case 'E': case 'f': case 'g': case 'G': { addlenmod(form, LUA_NUMBER_FRMLEN); - nb = sprintf(buff, form, luaL_checknumber(L, arg)); + nb = l_sprintf(buff, MAX_ITEM, form, luaL_checknumber(L, arg)); break; } case 'q': { @@ -1002,14 +1031,18 @@ static int str_format (lua_State *L) { case 's': { size_t l; const char *s = luaL_tolstring(L, arg, &l); - if (!strchr(form, '.') && l >= 100) { - /* no precision and string is too long to be formatted; - keep original string */ - luaL_addvalue(&b); - } + if (form[2] == '\0') /* no modifiers? */ + luaL_addvalue(&b); /* keep entire string */ else { - nb = sprintf(buff, form, s); - lua_pop(L, 1); /* remove result from 'luaL_tolstring' */ + luaL_argcheck(L, l == strlen(s), arg, "string contains zeros"); + if (!strchr(form, '.') && l >= 100) { + /* no precision and string is too long to be formatted */ + luaL_addvalue(&b); /* keep entire string */ + } + else { /* format the string into 'buff' */ + nb = l_sprintf(buff, MAX_ITEM, form, s); + lua_pop(L, 1); /* remove result from 'luaL_tolstring' */ + } } break; } @@ -1018,6 +1051,7 @@ static int str_format (lua_State *L) { *(strfrmt - 1)); } } + lua_assert(nb < MAX_ITEM); luaL_addsize(&b, nb); } } @@ -1309,8 +1343,13 @@ static int str_pack (lua_State *L) { case Kchar: { /* fixed-size string */ size_t len; const char *s = luaL_checklstring(L, arg, &len); - luaL_argcheck(L, len == (size_t)size, arg, "wrong length"); - luaL_addlstring(&b, s, size); + if ((size_t)size <= len) /* string larger than (or equal to) needed? */ + luaL_addlstring(&b, s, size); /* truncate string to asked size */ + else { /* string smaller than needed */ + luaL_addlstring(&b, s, len); /* add it all */ + while (len++ < (size_t)size) /* pad extra space */ + luaL_addchar(&b, LUA_PACKPADBYTE); + } break; } case Kstring: { /* strings with length count */ @@ -1360,7 +1399,7 @@ static int str_packsize (lua_State *L) { case Kstring: /* strings with length count */ case Kzstr: /* zero-terminated string */ luaL_argerror(L, 1, "variable-length format"); - break; + /* call never return, but to avoid warnings: *//* FALLTHROUGH */ default: break; } } diff --git a/libs/lua-5.3.1/src/ltable.c b/libs/lua-5.3.2/src/ltable.c similarity index 93% rename from libs/lua-5.3.1/src/ltable.c rename to libs/lua-5.3.2/src/ltable.c index 04f2a34..7e15b71 100644 --- a/libs/lua-5.3.1/src/ltable.c +++ b/libs/lua-5.3.2/src/ltable.c @@ -1,5 +1,5 @@ /* -** $Id: ltable.c,v 2.111 2015/06/09 14:21:13 roberto Exp $ +** $Id: ltable.c,v 2.117 2015/11/19 19:16:22 roberto Exp $ ** Lua tables (hash) ** See Copyright Notice in lua.h */ @@ -85,7 +85,7 @@ static const Node dummynode_ = { /* ** Hash for floating-point numbers. ** The main computation should be just -** n = frepx(n, &i); return (n * INT_MAX) + i +** n = frexp(n, &i); return (n * INT_MAX) + i ** but there are some numerical subtleties. ** In a two-complement representation, INT_MAX does not has an exact ** representation as a float, but INT_MIN does; because the absolute @@ -101,7 +101,7 @@ static int l_hashfloat (lua_Number n) { lua_Integer ni; n = l_mathop(frexp)(n, &i) * -cast_num(INT_MIN); if (!lua_numbertointeger(n, &ni)) { /* is 'n' inf/-inf/NaN? */ - lua_assert(luai_numisnan(n) || l_mathop(fabs)(n) == HUGE_VAL); + lua_assert(luai_numisnan(n) || l_mathop(fabs)(n) == cast_num(HUGE_VAL)); return 0; } else { /* normal case */ @@ -124,14 +124,8 @@ static Node *mainposition (const Table *t, const TValue *key) { return hashmod(t, l_hashfloat(fltvalue(key))); case LUA_TSHRSTR: return hashstr(t, tsvalue(key)); - case LUA_TLNGSTR: { - TString *s = tsvalue(key); - if (s->extra == 0) { /* no hash? */ - s->hash = luaS_hash(getstr(s), s->u.lnglen, s->hash); - s->extra = 1; /* now it has its hash */ - } - return hashstr(t, tsvalue(key)); - } + case LUA_TLNGSTR: + return hashpow2(t, luaS_hashlongstr(tsvalue(key))); case LUA_TBOOLEAN: return hashboolean(t, bvalue(key)); case LUA_TLIGHTUSERDATA: @@ -139,6 +133,7 @@ static Node *mainposition (const Table *t, const TValue *key) { case LUA_TLCF: return hashpointer(t, fvalue(key)); default: + lua_assert(!ttisdeadkey(key)); return hashpointer(t, gcvalue(key)); } } @@ -463,7 +458,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) { Node *f = getfreepos(t); /* get a free place */ if (f == NULL) { /* cannot find a free place? */ rehash(L, t, key); /* grow table */ - /* whatever called 'newkey' takes care of TM cache and GC barrier */ + /* whatever called 'newkey' takes care of TM cache */ return luaH_set(L, t, key); /* insert key into grown table */ } lua_assert(!isdummy(f)); @@ -501,7 +496,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) { */ const TValue *luaH_getint (Table *t, lua_Integer key) { /* (1 <= key && key <= t->sizearray) */ - if (l_castS2U(key - 1) < t->sizearray) + if (l_castS2U(key) - 1 < t->sizearray) return &t->array[key - 1]; else { Node *n = hashint(t, key); @@ -513,7 +508,7 @@ const TValue *luaH_getint (Table *t, lua_Integer key) { if (nx == 0) break; n += nx; } - }; + } return luaO_nilobject; } } @@ -522,7 +517,7 @@ const TValue *luaH_getint (Table *t, lua_Integer key) { /* ** search function for short strings */ -const TValue *luaH_getstr (Table *t, TString *key) { +const TValue *luaH_getshortstr (Table *t, TString *key) { Node *n = hashstr(t, key); lua_assert(key->tt == LUA_TSHRSTR); for (;;) { /* check whether 'key' is somewhere in the chain */ @@ -531,11 +526,41 @@ const TValue *luaH_getstr (Table *t, TString *key) { return gval(n); /* that's it */ else { int nx = gnext(n); - if (nx == 0) break; + if (nx == 0) + return luaO_nilobject; /* not found */ n += nx; } - }; - return luaO_nilobject; + } +} + + +/* +** "Generic" get version. (Not that generic: not valid for integers, +** which may be in array part, nor for floats with integral values.) +*/ +static const TValue *getgeneric (Table *t, const TValue *key) { + Node *n = mainposition(t, key); + for (;;) { /* check whether 'key' is somewhere in the chain */ + if (luaV_rawequalobj(gkey(n), key)) + return gval(n); /* that's it */ + else { + int nx = gnext(n); + if (nx == 0) + return luaO_nilobject; /* not found */ + n += nx; + } + } +} + + +const TValue *luaH_getstr (Table *t, TString *key) { + if (key->tt == LUA_TSHRSTR) + return luaH_getshortstr(t, key); + else { /* for long strings, use generic case */ + TValue ko; + setsvalue(cast(lua_State *, NULL), &ko, key); + return getgeneric(t, &ko); + } } @@ -544,7 +569,7 @@ const TValue *luaH_getstr (Table *t, TString *key) { */ const TValue *luaH_get (Table *t, const TValue *key) { switch (ttype(key)) { - case LUA_TSHRSTR: return luaH_getstr(t, tsvalue(key)); + case LUA_TSHRSTR: return luaH_getshortstr(t, tsvalue(key)); case LUA_TNUMINT: return luaH_getint(t, ivalue(key)); case LUA_TNIL: return luaO_nilobject; case LUA_TNUMFLT: { @@ -553,19 +578,8 @@ const TValue *luaH_get (Table *t, const TValue *key) { return luaH_getint(t, k); /* use specialized version */ /* else... */ } /* FALLTHROUGH */ - default: { - Node *n = mainposition(t, key); - for (;;) { /* check whether 'key' is somewhere in the chain */ - if (luaV_rawequalobj(gkey(n), key)) - return gval(n); /* that's it */ - else { - int nx = gnext(n); - if (nx == 0) break; - n += nx; - } - }; - return luaO_nilobject; - } + default: + return getgeneric(t, key); } } diff --git a/libs/lua-5.3.1/src/ltable.h b/libs/lua-5.3.2/src/ltable.h similarity index 86% rename from libs/lua-5.3.1/src/ltable.h rename to libs/lua-5.3.2/src/ltable.h index 53d2551..213cc13 100644 --- a/libs/lua-5.3.1/src/ltable.h +++ b/libs/lua-5.3.2/src/ltable.h @@ -1,5 +1,5 @@ /* -** $Id: ltable.h,v 2.20 2014/09/04 18:15:29 roberto Exp $ +** $Id: ltable.h,v 2.21 2015/11/03 15:47:30 roberto Exp $ ** Lua tables (hash) ** See Copyright Notice in lua.h */ @@ -18,6 +18,10 @@ /* 'const' to avoid wrong writings that can mess up field 'next' */ #define gkey(n) cast(const TValue*, (&(n)->i_key.tvk)) +/* +** writable version of 'gkey'; allows updates to individual fields, +** but not to the whole (which has incompatible type) +*/ #define wgkey(n) (&(n)->i_key.nk) #define invalidateTMcache(t) ((t)->flags = 0) @@ -31,6 +35,7 @@ LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key); LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value); +LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key); LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key); LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key); LUAI_FUNC TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key); diff --git a/libs/lua-5.3.2/src/ltablib.c b/libs/lua-5.3.2/src/ltablib.c new file mode 100644 index 0000000..b3c9a7c --- /dev/null +++ b/libs/lua-5.3.2/src/ltablib.c @@ -0,0 +1,449 @@ +/* +** $Id: ltablib.c,v 1.90 2015/11/25 12:48:57 roberto Exp $ +** Library for Table Manipulation +** See Copyright Notice in lua.h +*/ + +#define ltablib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include +#include +#include + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +/* +** Operations that an object must define to mimic a table +** (some functions only need some of them) +*/ +#define TAB_R 1 /* read */ +#define TAB_W 2 /* write */ +#define TAB_L 4 /* length */ +#define TAB_RW (TAB_R | TAB_W) /* read/write */ + + +#define aux_getn(L,n,w) (checktab(L, n, (w) | TAB_L), luaL_len(L, n)) + + +static int checkfield (lua_State *L, const char *key, int n) { + lua_pushstring(L, key); + return (lua_rawget(L, -n) != LUA_TNIL); +} + + +/* +** Check that 'arg' either is a table or can behave like one (that is, +** has a metatable with the required metamethods) +*/ +static void checktab (lua_State *L, int arg, int what) { + if (lua_type(L, arg) != LUA_TTABLE) { /* is it not a table? */ + int n = 1; /* number of elements to pop */ + if (lua_getmetatable(L, arg) && /* must have metatable */ + (!(what & TAB_R) || checkfield(L, "__index", ++n)) && + (!(what & TAB_W) || checkfield(L, "__newindex", ++n)) && + (!(what & TAB_L) || checkfield(L, "__len", ++n))) { + lua_pop(L, n); /* pop metatable and tested metamethods */ + } + else + luaL_argerror(L, arg, "table expected"); /* force an error */ + } +} + + +#if defined(LUA_COMPAT_MAXN) +static int maxn (lua_State *L) { + lua_Number max = 0; + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushnil(L); /* first key */ + while (lua_next(L, 1)) { + lua_pop(L, 1); /* remove value */ + if (lua_type(L, -1) == LUA_TNUMBER) { + lua_Number v = lua_tonumber(L, -1); + if (v > max) max = v; + } + } + lua_pushnumber(L, max); + return 1; +} +#endif + + +static int tinsert (lua_State *L) { + lua_Integer e = aux_getn(L, 1, TAB_RW) + 1; /* first empty element */ + lua_Integer pos; /* where to insert new element */ + switch (lua_gettop(L)) { + case 2: { /* called with only 2 arguments */ + pos = e; /* insert new element at the end */ + break; + } + case 3: { + lua_Integer i; + pos = luaL_checkinteger(L, 2); /* 2nd argument is the position */ + luaL_argcheck(L, 1 <= pos && pos <= e, 2, "position out of bounds"); + for (i = e; i > pos; i--) { /* move up elements */ + lua_geti(L, 1, i - 1); + lua_seti(L, 1, i); /* t[i] = t[i - 1] */ + } + break; + } + default: { + return luaL_error(L, "wrong number of arguments to 'insert'"); + } + } + lua_seti(L, 1, pos); /* t[pos] = v */ + return 0; +} + + +static int tremove (lua_State *L) { + lua_Integer size = aux_getn(L, 1, TAB_RW); + lua_Integer pos = luaL_optinteger(L, 2, size); + if (pos != size) /* validate 'pos' if given */ + luaL_argcheck(L, 1 <= pos && pos <= size + 1, 1, "position out of bounds"); + lua_geti(L, 1, pos); /* result = t[pos] */ + for ( ; pos < size; pos++) { + lua_geti(L, 1, pos + 1); + lua_seti(L, 1, pos); /* t[pos] = t[pos + 1] */ + } + lua_pushnil(L); + lua_seti(L, 1, pos); /* t[pos] = nil */ + return 1; +} + + +/* +** Copy elements (1[f], ..., 1[e]) into (tt[t], tt[t+1], ...). Whenever +** possible, copy in increasing order, which is better for rehashing. +** "possible" means destination after original range, or smaller +** than origin, or copying to another table. +*/ +static int tmove (lua_State *L) { + lua_Integer f = luaL_checkinteger(L, 2); + lua_Integer e = luaL_checkinteger(L, 3); + lua_Integer t = luaL_checkinteger(L, 4); + int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */ + checktab(L, 1, TAB_R); + checktab(L, tt, TAB_W); + if (e >= f) { /* otherwise, nothing to move */ + lua_Integer n, i; + luaL_argcheck(L, f > 0 || e < LUA_MAXINTEGER + f, 3, + "too many elements to move"); + n = e - f + 1; /* number of elements to move */ + luaL_argcheck(L, t <= LUA_MAXINTEGER - n + 1, 4, + "destination wrap around"); + if (t > e || t <= f || tt != 1) { + for (i = 0; i < n; i++) { + lua_geti(L, 1, f + i); + lua_seti(L, tt, t + i); + } + } + else { + for (i = n - 1; i >= 0; i--) { + lua_geti(L, 1, f + i); + lua_seti(L, tt, t + i); + } + } + } + lua_pushvalue(L, tt); /* return "to table" */ + return 1; +} + + +static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) { + lua_geti(L, 1, i); + if (!lua_isstring(L, -1)) + luaL_error(L, "invalid value (%s) at index %d in table for 'concat'", + luaL_typename(L, -1), i); + luaL_addvalue(b); +} + + +static int tconcat (lua_State *L) { + luaL_Buffer b; + lua_Integer last = aux_getn(L, 1, TAB_R); + size_t lsep; + const char *sep = luaL_optlstring(L, 2, "", &lsep); + lua_Integer i = luaL_optinteger(L, 3, 1); + last = luaL_opt(L, luaL_checkinteger, 4, last); + luaL_buffinit(L, &b); + for (; i < last; i++) { + addfield(L, &b, i); + luaL_addlstring(&b, sep, lsep); + } + if (i == last) /* add last value (if interval was not empty) */ + addfield(L, &b, i); + luaL_pushresult(&b); + return 1; +} + + +/* +** {====================================================== +** Pack/unpack +** ======================================================= +*/ + +static int pack (lua_State *L) { + int i; + int n = lua_gettop(L); /* number of elements to pack */ + lua_createtable(L, n, 1); /* create result table */ + lua_insert(L, 1); /* put it at index 1 */ + for (i = n; i >= 1; i--) /* assign elements */ + lua_seti(L, 1, i); + lua_pushinteger(L, n); + lua_setfield(L, 1, "n"); /* t.n = number of elements */ + return 1; /* return table */ +} + + +static int unpack (lua_State *L) { + lua_Unsigned n; + lua_Integer i = luaL_optinteger(L, 2, 1); + lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1)); + if (i > e) return 0; /* empty range */ + n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */ + if (n >= (unsigned int)INT_MAX || !lua_checkstack(L, (int)(++n))) + return luaL_error(L, "too many results to unpack"); + for (; i < e; i++) { /* push arg[i..e - 1] (to avoid overflows) */ + lua_geti(L, 1, i); + } + lua_geti(L, 1, e); /* push last element */ + return (int)n; +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** Quicksort +** (based on 'Algorithms in MODULA-3', Robert Sedgewick; +** Addison-Wesley, 1993.) +** ======================================================= +*/ + + +/* +** Produce a "random" 'unsigned int' to randomize pivot choice. This +** macro is used only when 'sort' detects a big imbalance in the result +** of a partition. (If you don't want/need this "randomness", ~0 is a +** good choice.) +*/ +#if !defined(l_randomizePivot) /* { */ + +#include + +/* size of 'e' measured in number of 'unsigned int's */ +#define sof(e) (sizeof(e) / sizeof(unsigned int)) + +/* +** Use 'time' and 'clock' as sources of "randomness". Because we don't +** know the types 'clock_t' and 'time_t', we cannot cast them to +** anything without risking overflows. A safe way to use their values +** is to copy them to an array of a known type and use the array values. +*/ +static unsigned int l_randomizePivot (void) { + clock_t c = clock(); + time_t t = time(NULL); + unsigned int buff[sof(c) + sof(t)]; + unsigned int i, rnd = 0; + memcpy(buff, &c, sof(c) * sizeof(unsigned int)); + memcpy(buff + sof(c), &t, sof(t) * sizeof(unsigned int)); + for (i = 0; i < sof(buff); i++) + rnd += buff[i]; + return rnd; +} + +#endif /* } */ + + +/* arrays larger than 'RANLIMIT' may use randomized pivots */ +#define RANLIMIT 100u + + +static void set2 (lua_State *L, unsigned int i, unsigned int j) { + lua_seti(L, 1, i); + lua_seti(L, 1, j); +} + + +/* +** Return true iff value at stack index 'a' is less than the value at +** index 'b' (according to the order of the sort). +*/ +static int sort_comp (lua_State *L, int a, int b) { + if (lua_isnil(L, 2)) /* no function? */ + return lua_compare(L, a, b, LUA_OPLT); /* a < b */ + else { /* function */ + int res; + lua_pushvalue(L, 2); /* push function */ + lua_pushvalue(L, a-1); /* -1 to compensate function */ + lua_pushvalue(L, b-2); /* -2 to compensate function and 'a' */ + lua_call(L, 2, 1); /* call function */ + res = lua_toboolean(L, -1); /* get result */ + lua_pop(L, 1); /* pop result */ + return res; + } +} + + +/* +** Does the partition: Pivot P is at the top of the stack. +** precondition: a[lo] <= P == a[up-1] <= a[up], +** so it only needs to do the partition from lo + 1 to up - 2. +** Pos-condition: a[lo .. i - 1] <= a[i] == P <= a[i + 1 .. up] +** returns 'i'. +*/ +static unsigned int partition (lua_State *L, unsigned int lo, + unsigned int up) { + unsigned int i = lo; /* will be incremented before first use */ + unsigned int j = up - 1; /* will be decremented before first use */ + /* loop invariant: a[lo .. i] <= P <= a[j .. up] */ + for (;;) { + /* next loop: repeat ++i while a[i] < P */ + while (lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) { + if (i == up - 1) /* a[i] < P but a[up - 1] == P ?? */ + luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[i] */ + } + /* after the loop, a[i] >= P and a[lo .. i - 1] < P */ + /* next loop: repeat --j while P < a[j] */ + while (lua_geti(L, 1, --j), sort_comp(L, -3, -1)) { + if (j < i) /* j < i but a[j] > P ?? */ + luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[j] */ + } + /* after the loop, a[j] <= P and a[j + 1 .. up] >= P */ + if (j < i) { /* no elements out of place? */ + /* a[lo .. i - 1] <= P <= a[j + 1 .. i .. up] */ + lua_pop(L, 1); /* pop a[j] */ + /* swap pivot (a[up - 1]) with a[i] to satisfy pos-condition */ + set2(L, up - 1, i); + return i; + } + /* otherwise, swap a[i] - a[j] to restore invariant and repeat */ + set2(L, i, j); + } +} + + +/* +** Choose an element in the middle (2nd-3th quarters) of [lo,up] +** "randomized" by 'rnd' +*/ +static unsigned int choosePivot (unsigned int lo, unsigned int up, + unsigned int rnd) { + unsigned int r4 = (unsigned int)(up - lo) / 4u; /* range/4 */ + unsigned int p = rnd % (r4 * 2) + (lo + r4); + lua_assert(lo + r4 <= p && p <= up - r4); + return p; +} + + +/* +** QuickSort algorithm (recursive function) +*/ +static void auxsort (lua_State *L, unsigned int lo, unsigned int up, + unsigned int rnd) { + while (lo < up) { /* loop for tail recursion */ + unsigned int p; /* Pivot index */ + unsigned int n; /* to be used later */ + /* sort elements 'lo', 'p', and 'up' */ + lua_geti(L, 1, lo); + lua_geti(L, 1, up); + if (sort_comp(L, -1, -2)) /* a[up] < a[lo]? */ + set2(L, lo, up); /* swap a[lo] - a[up] */ + else + lua_pop(L, 2); /* remove both values */ + if (up - lo == 1) /* only 2 elements? */ + return; /* already sorted */ + if (up - lo < RANLIMIT || rnd == 0) /* small interval or no randomize? */ + p = (lo + up)/2; /* middle element is a good pivot */ + else /* for larger intervals, it is worth a random pivot */ + p = choosePivot(lo, up, rnd); + lua_geti(L, 1, p); + lua_geti(L, 1, lo); + if (sort_comp(L, -2, -1)) /* a[p] < a[lo]? */ + set2(L, p, lo); /* swap a[p] - a[lo] */ + else { + lua_pop(L, 1); /* remove a[lo] */ + lua_geti(L, 1, up); + if (sort_comp(L, -1, -2)) /* a[up] < a[p]? */ + set2(L, p, up); /* swap a[up] - a[p] */ + else + lua_pop(L, 2); + } + if (up - lo == 2) /* only 3 elements? */ + return; /* already sorted */ + lua_geti(L, 1, p); /* get middle element (Pivot) */ + lua_pushvalue(L, -1); /* push Pivot */ + lua_geti(L, 1, up - 1); /* push a[up - 1] */ + set2(L, p, up - 1); /* swap Pivot (a[p]) with a[up - 1] */ + p = partition(L, lo, up); + /* a[lo .. p - 1] <= a[p] == P <= a[p + 1 .. up] */ + if (p - lo < up - p) { /* lower interval is smaller? */ + auxsort(L, lo, p - 1, rnd); /* call recursively for lower interval */ + n = p - lo; /* size of smaller interval */ + lo = p + 1; /* tail call for [p + 1 .. up] (upper interval) */ + } + else { + auxsort(L, p + 1, up, rnd); /* call recursively for upper interval */ + n = up - p; /* size of smaller interval */ + up = p - 1; /* tail call for [lo .. p - 1] (lower interval) */ + } + if ((up - lo) / 128u > n) /* partition too imbalanced? */ + rnd = l_randomizePivot(); /* try a new randomization */ + } /* tail call auxsort(L, lo, up, rnd) */ +} + + +static int sort (lua_State *L) { + lua_Integer n = aux_getn(L, 1, TAB_RW); + if (n > 1) { /* non-trivial interval? */ + luaL_argcheck(L, n < INT_MAX, 1, "array too big"); + luaL_checkstack(L, 40, ""); /* assume array is smaller than 2^40 */ + if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */ + luaL_checktype(L, 2, LUA_TFUNCTION); /* must be a function */ + lua_settop(L, 2); /* make sure there are two arguments */ + auxsort(L, 1, (unsigned int)n, 0u); + } + return 0; +} + +/* }====================================================== */ + + +static const luaL_Reg tab_funcs[] = { + {"concat", tconcat}, +#if defined(LUA_COMPAT_MAXN) + {"maxn", maxn}, +#endif + {"insert", tinsert}, + {"pack", pack}, + {"unpack", unpack}, + {"remove", tremove}, + {"move", tmove}, + {"sort", sort}, + {NULL, NULL} +}; + + +LUAMOD_API int luaopen_table (lua_State *L) { + luaL_newlib(L, tab_funcs); +#if defined(LUA_COMPAT_UNPACK) + /* _G.unpack = table.unpack */ + lua_getfield(L, -1, "unpack"); + lua_setglobal(L, "unpack"); +#endif + return 1; +} + diff --git a/libs/lua-5.3.1/src/ltm.c b/libs/lua-5.3.2/src/ltm.c similarity index 88% rename from libs/lua-5.3.1/src/ltm.c rename to libs/lua-5.3.2/src/ltm.c index c38e5c3..22b4df3 100644 --- a/libs/lua-5.3.1/src/ltm.c +++ b/libs/lua-5.3.2/src/ltm.c @@ -1,5 +1,5 @@ /* -** $Id: ltm.c,v 2.34 2015/03/30 15:42:27 roberto Exp $ +** $Id: ltm.c,v 2.36 2015/11/03 15:47:30 roberto Exp $ ** Tag methods ** See Copyright Notice in lua.h */ @@ -57,7 +57,7 @@ void luaT_init (lua_State *L) { ** tag methods */ const TValue *luaT_gettm (Table *events, TMS event, TString *ename) { - const TValue *tm = luaH_getstr(events, ename); + const TValue *tm = luaH_getshortstr(events, ename); lua_assert(event <= TM_EQ); if (ttisnil(tm)) { /* no tag method? */ events->flags |= cast_byte(1u<mt[ttnov(o)]; } - return (mt ? luaH_getstr(mt, G(L)->tmname[event]) : luaO_nilobject); + return (mt ? luaH_getshortstr(mt, G(L)->tmname[event]) : luaO_nilobject); } void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, const TValue *p2, TValue *p3, int hasres) { ptrdiff_t result = savestack(L, p3); - setobj2s(L, L->top++, f); /* push function (assume EXTRA_STACK) */ - setobj2s(L, L->top++, p1); /* 1st argument */ - setobj2s(L, L->top++, p2); /* 2nd argument */ + StkId func = L->top; + setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ + setobj2s(L, func + 1, p1); /* 1st argument */ + setobj2s(L, func + 2, p2); /* 2nd argument */ + L->top += 3; if (!hasres) /* no result? 'p3' is third argument */ setobj2s(L, L->top++, p3); /* 3rd argument */ /* metamethod may yield only when called from Lua code */ - luaD_call(L, L->top - (4 - hasres), hasres, isLua(L->ci)); + if (isLua(L->ci)) + luaD_call(L, func, hasres); + else + luaD_callnoyield(L, func, hasres); if (hasres) { /* if has result, move it to its place */ p3 = restorestack(L, result); setobjs2s(L, p3, --L->top); diff --git a/libs/lua-5.3.1/src/ltm.h b/libs/lua-5.3.2/src/ltm.h similarity index 100% rename from libs/lua-5.3.1/src/ltm.h rename to libs/lua-5.3.2/src/ltm.h diff --git a/libs/lua-5.3.1/src/lua.dontcompile b/libs/lua-5.3.2/src/lua.dontcompile similarity index 96% rename from libs/lua-5.3.1/src/lua.dontcompile rename to libs/lua-5.3.2/src/lua.dontcompile index 7a47582..545d23d 100644 --- a/libs/lua-5.3.1/src/lua.dontcompile +++ b/libs/lua-5.3.2/src/lua.dontcompile @@ -1,5 +1,5 @@ /* -** $Id: lua.c,v 1.225 2015/03/30 15:42:59 roberto Exp $ +** $Id: lua.c,v 1.226 2015/08/14 19:11:20 roberto Exp $ ** Lua stand-alone interpreter ** See Copyright Notice in lua.h */ @@ -324,24 +324,20 @@ static int pushline (lua_State *L, int firstline) { /* -** Try to compile line on the stack as 'return '; on return, stack +** Try to compile line on the stack as 'return ;'; on return, stack ** has either compiled chunk or original line (if compilation failed). */ static int addreturn (lua_State *L) { - int status; - size_t len; const char *line; - lua_pushliteral(L, "return "); - lua_pushvalue(L, -2); /* duplicate line */ - lua_concat(L, 2); /* new line is "return ..." */ - line = lua_tolstring(L, -1, &len); - if ((status = luaL_loadbuffer(L, line, len, "=stdin")) == LUA_OK) { - lua_remove(L, -3); /* remove original line */ - line += sizeof("return")/sizeof(char); /* remove 'return' for history */ + const char *line = lua_tostring(L, -1); /* original line */ + const char *retline = lua_pushfstring(L, "return %s;", line); + int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin"); + if (status == LUA_OK) { + lua_remove(L, -2); /* remove modified line */ if (line[0] != '\0') /* non empty? */ lua_saveline(L, line); /* keep history */ } else - lua_pop(L, 2); /* remove result from 'luaL_loadbuffer' and new line */ + lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */ return status; } diff --git a/libs/lua-5.3.1/src/lua.h b/libs/lua-5.3.2/src/lua.h similarity index 99% rename from libs/lua-5.3.1/src/lua.h rename to libs/lua-5.3.2/src/lua.h index 1c2b95a..06381d7 100644 --- a/libs/lua-5.3.1/src/lua.h +++ b/libs/lua-5.3.2/src/lua.h @@ -1,5 +1,5 @@ /* -** $Id: lua.h,v 1.328 2015/06/03 13:03:38 roberto Exp $ +** $Id: lua.h,v 1.329 2015/11/13 17:18:42 roberto Exp $ ** Lua - A Scripting Language ** Lua.org, PUC-Rio, Brazil (http://www.lua.org) ** See Copyright Notice at the end of this file @@ -19,7 +19,7 @@ #define LUA_VERSION_MAJOR "5" #define LUA_VERSION_MINOR "3" #define LUA_VERSION_NUM 503 -#define LUA_VERSION_RELEASE "1" +#define LUA_VERSION_RELEASE "2" #define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR #define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE diff --git a/libs/lua-5.3.1/src/lua.hpp b/libs/lua-5.3.2/src/lua.hpp similarity index 100% rename from libs/lua-5.3.1/src/lua.hpp rename to libs/lua-5.3.2/src/lua.hpp diff --git a/libs/lua-5.3.1/src/luac.dontcompile b/libs/lua-5.3.2/src/luac.dontcompile similarity index 100% rename from libs/lua-5.3.1/src/luac.dontcompile rename to libs/lua-5.3.2/src/luac.dontcompile diff --git a/libs/lua-5.3.1/src/luaconf.h b/libs/lua-5.3.2/src/luaconf.h similarity index 96% rename from libs/lua-5.3.1/src/luaconf.h rename to libs/lua-5.3.2/src/luaconf.h index 74a9252..8d0e536 100644 --- a/libs/lua-5.3.1/src/luaconf.h +++ b/libs/lua-5.3.2/src/luaconf.h @@ -1,5 +1,5 @@ /* -** $Id: luaconf.h,v 1.251 2015/05/20 17:39:23 roberto Exp $ +** $Id: luaconf.h,v 1.254 2015/10/21 18:17:40 roberto Exp $ ** Configuration file for Lua ** See Copyright Notice in lua.h */ @@ -145,7 +145,7 @@ #if !defined(LUA_FLOAT_TYPE) #define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE -#endif /* } */ +#endif /* }================================================================== */ @@ -412,9 +412,33 @@ @@ LUA_NUMBER_FMT is the format for writing floats. @@ lua_number2str converts a float to a string. @@ l_mathop allows the addition of an 'l' or 'f' to all math operations. +@@ l_floor takes the floor of a float. @@ lua_str2number converts a decimal numeric string to a number. */ + +/* The following definitions are good for most cases here */ + +#define l_floor(x) (l_mathop(floor)(x)) + +#define lua_number2str(s,sz,n) l_sprintf((s), sz, LUA_NUMBER_FMT, (n)) + +/* +@@ lua_numbertointeger converts a float number to an integer, or +** returns 0 if float is not within the range of a lua_Integer. +** (The range comparisons are tricky because of rounding. The tests +** here assume a two-complement representation, where MININTEGER always +** has an exact representation as a float; MAXINTEGER may not have one, +** and therefore its conversion to float may have an ill-defined value.) +*/ +#define lua_numbertointeger(n,p) \ + ((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \ + (n) < -(LUA_NUMBER)(LUA_MININTEGER) && \ + (*(p) = (LUA_INTEGER)(n), 1)) + + +/* now the variable definitions */ + #if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */ #define LUA_NUMBER float @@ -468,25 +492,6 @@ #endif /* } */ -#define l_floor(x) (l_mathop(floor)(x)) - -#define lua_number2str(s,n) sprintf((s), LUA_NUMBER_FMT, (n)) - - -/* -@@ lua_numbertointeger converts a float number to an integer, or -** returns 0 if float is not within the range of a lua_Integer. -** (The range comparisons are tricky because of rounding. The tests -** here assume a two-complement representation, where MININTEGER always -** has an exact representation as a float; MAXINTEGER may not have one, -** and therefore its conversion to float may have an ill-defined value.) -*/ -#define lua_numbertointeger(n,p) \ - ((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \ - (n) < -(LUA_NUMBER)(LUA_MININTEGER) && \ - (*(p) = (LUA_INTEGER)(n), 1)) - - /* @@ LUA_INTEGER is the integer type used by Lua. @@ -506,7 +511,7 @@ /* The following definitions are good for most cases here */ #define LUA_INTEGER_FMT "%" LUA_INTEGER_FRMLEN "d" -#define lua_integer2str(s,n) sprintf((s), LUA_INTEGER_FMT, (n)) +#define lua_integer2str(s,sz,n) l_sprintf((s), sz, LUA_INTEGER_FMT, (n)) #define LUAI_UACINT LUA_INTEGER @@ -537,6 +542,7 @@ #elif LUA_INT_TYPE == LUA_INT_LONGLONG /* }{ long long */ +/* use presence of macro LLONG_MAX as proxy for C99 compliance */ #if defined(LLONG_MAX) /* { */ /* use ISO C99 stuff */ @@ -577,6 +583,17 @@ ** =================================================================== */ +/* +@@ l_sprintf is equivalent to 'snprintf' or 'sprintf' in C89. +** (All uses in Lua have only one format item.) +*/ +#if !defined(LUA_USE_C89) +#define l_sprintf(s,sz,f,i) snprintf(s,sz,f,i) +#else +#define l_sprintf(s,sz,f,i) ((void)(sz), sprintf(s,f,i)) +#endif + + /* @@ lua_strx2number converts an hexadecimal numeric string to a number. ** In C99, 'strtod' does that conversion. Otherwise, you can @@ -584,7 +601,7 @@ ** implementation. */ #if !defined(LUA_USE_C89) -#define lua_strx2number(s,p) lua_str2number(s,p) +#define lua_strx2number(s,p) lua_str2number(s,p) #endif @@ -595,7 +612,7 @@ ** provide its own implementation. */ #if !defined(LUA_USE_C89) -#define lua_number2strx(L,b,f,n) sprintf(b,f,n) +#define lua_number2strx(L,b,sz,f,n) l_sprintf(b,sz,f,n) #endif diff --git a/libs/lua-5.3.1/src/lualib.h b/libs/lua-5.3.2/src/lualib.h similarity index 100% rename from libs/lua-5.3.1/src/lualib.h rename to libs/lua-5.3.2/src/lualib.h diff --git a/libs/lua-5.3.1/src/lundump.c b/libs/lua-5.3.2/src/lundump.c similarity index 92% rename from libs/lua-5.3.1/src/lundump.c rename to libs/lua-5.3.2/src/lundump.c index 510f325..4080af9 100644 --- a/libs/lua-5.3.1/src/lundump.c +++ b/libs/lua-5.3.2/src/lundump.c @@ -1,5 +1,5 @@ /* -** $Id: lundump.c,v 2.41 2014/11/02 19:19:04 roberto Exp $ +** $Id: lundump.c,v 2.44 2015/11/02 16:09:30 roberto Exp $ ** load precompiled Lua chunks ** See Copyright Notice in lua.h */ @@ -32,7 +32,6 @@ typedef struct { lua_State *L; ZIO *Z; - Mbuffer *b; const char *name; } LoadState; @@ -92,10 +91,15 @@ static TString *LoadString (LoadState *S) { LoadVar(S, size); if (size == 0) return NULL; - else { - char *s = luaZ_openspace(S->L, S->b, --size); - LoadVector(S, s, size); - return luaS_newlstr(S->L, s, size); + else if (--size <= LUAI_MAXSHORTLEN) { /* short string? */ + char buff[LUAI_MAXSHORTLEN]; + LoadVector(S, buff, size); + return luaS_newlstr(S->L, buff, size); + } + else { /* long string */ + TString *ts = luaS_createlngstrobj(S->L, size); + LoadVector(S, getstr(ts), size); /* load directly in final place */ + return ts; } } @@ -251,8 +255,7 @@ static void checkHeader (LoadState *S) { /* ** load precompiled chunk */ -LClosure *luaU_undump(lua_State *L, ZIO *Z, Mbuffer *buff, - const char *name) { +LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) { LoadState S; LClosure *cl; if (*name == '@' || *name == '=') @@ -263,11 +266,10 @@ LClosure *luaU_undump(lua_State *L, ZIO *Z, Mbuffer *buff, S.name = name; S.L = L; S.Z = Z; - S.b = buff; checkHeader(&S); cl = luaF_newLclosure(L, LoadByte(&S)); setclLvalue(L, L->top, cl); - incr_top(L); + luaD_inctop(L); cl->p = luaF_newproto(L); LoadFunction(&S, cl->p, NULL); lua_assert(cl->nupvalues == cl->p->sizeupvalues); diff --git a/libs/lua-5.3.1/src/lundump.h b/libs/lua-5.3.2/src/lundump.h similarity index 78% rename from libs/lua-5.3.1/src/lundump.h rename to libs/lua-5.3.2/src/lundump.h index ef43d51..aa5cc82 100644 --- a/libs/lua-5.3.1/src/lundump.h +++ b/libs/lua-5.3.2/src/lundump.h @@ -1,5 +1,5 @@ /* -** $Id: lundump.h,v 1.44 2014/06/19 18:27:20 roberto Exp $ +** $Id: lundump.h,v 1.45 2015/09/08 15:41:05 roberto Exp $ ** load precompiled Lua chunks ** See Copyright Notice in lua.h */ @@ -23,8 +23,7 @@ #define LUAC_FORMAT 0 /* this is the official format */ /* load one chunk; from lundump.c */ -LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, - const char* name); +LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name); /* dump one chunk; from ldump.c */ LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, diff --git a/libs/lua-5.3.1/src/lutf8lib.c b/libs/lua-5.3.2/src/lutf8lib.c similarity index 100% rename from libs/lua-5.3.1/src/lutf8lib.c rename to libs/lua-5.3.2/src/lutf8lib.c diff --git a/libs/lua-5.3.1/src/lvm.c b/libs/lua-5.3.2/src/lvm.c similarity index 88% rename from libs/lua-5.3.1/src/lvm.c rename to libs/lua-5.3.2/src/lvm.c index a8cefc5..aba7ace 100644 --- a/libs/lua-5.3.1/src/lvm.c +++ b/libs/lua-5.3.2/src/lvm.c @@ -1,5 +1,5 @@ /* -** $Id: lvm.c,v 2.245 2015/06/09 15:53:35 roberto Exp $ +** $Id: lvm.c,v 2.265 2015/11/23 11:30:45 roberto Exp $ ** Lua virtual machine ** See Copyright Notice in lua.h */ @@ -153,30 +153,28 @@ static int forlimit (const TValue *obj, lua_Integer *p, lua_Integer step, /* -** Main function for table access (invoking metamethods if needed). -** Compute 'val = t[key]' +** Complete a table access: if 't' is a table, 'tm' has its metamethod; +** otherwise, 'tm' is NULL. */ -void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) { +void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, + const TValue *tm) { int loop; /* counter to avoid infinite loops */ + lua_assert(tm != NULL || !ttistable(t)); for (loop = 0; loop < MAXTAGLOOP; loop++) { - const TValue *tm; - if (ttistable(t)) { /* 't' is a table? */ - Table *h = hvalue(t); - const TValue *res = luaH_get(h, key); /* do a primitive get */ - if (!ttisnil(res) || /* result is not nil? */ - (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */ - setobj2s(L, val, res); /* result is the raw get */ - return; - } - /* else will try metamethod */ + if (tm == NULL) { /* no metamethod (from a table)? */ + if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) + luaG_typeerror(L, t, "index"); /* no metamethod */ } - else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) - luaG_typeerror(L, t, "index"); /* no metamethod */ if (ttisfunction(tm)) { /* metamethod is a function */ - luaT_callTM(L, tm, t, key, val, 1); + luaT_callTM(L, tm, t, key, val, 1); /* call it */ return; } t = tm; /* else repeat access over 'tm' */ + if (luaV_fastget(L,t,key,tm,luaH_get)) { /* try fast track */ + setobj2s(L, val, tm); /* done */ + return; + } + /* else repeat */ } luaG_runerror(L, "gettable chain too long; possible loop"); } @@ -186,40 +184,41 @@ void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) { ** Main function for table assignment (invoking metamethods if needed). ** Compute 't[key] = val' */ -void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) { +void luaV_finishset (lua_State *L, const TValue *t, TValue *key, + StkId val, const TValue *oldval) { int loop; /* counter to avoid infinite loops */ for (loop = 0; loop < MAXTAGLOOP; loop++) { const TValue *tm; - if (ttistable(t)) { /* 't' is a table? */ - Table *h = hvalue(t); - TValue *oldval = cast(TValue *, luaH_get(h, key)); - /* if previous value is not nil, there must be a previous entry - in the table; a metamethod has no relevance */ - if (!ttisnil(oldval) || - /* previous value is nil; must check the metamethod */ - ((tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL && + if (oldval != NULL) { + lua_assert(ttistable(t) && ttisnil(oldval)); + /* must check the metamethod */ + if ((tm = fasttm(L, hvalue(t)->metatable, TM_NEWINDEX)) == NULL && /* no metamethod; is there a previous entry in the table? */ (oldval != luaO_nilobject || /* no previous entry; must create one. (The next test is always true; we only need the assignment.) */ - (oldval = luaH_newkey(L, h, key), 1)))) { + (oldval = luaH_newkey(L, hvalue(t), key), 1))) { /* no metamethod and (now) there is an entry with given key */ - setobj2t(L, oldval, val); /* assign new value to that entry */ - invalidateTMcache(h); - luaC_barrierback(L, h, val); + setobj2t(L, cast(TValue *, oldval), val); + invalidateTMcache(hvalue(t)); + luaC_barrierback(L, hvalue(t), val); return; } /* else will try the metamethod */ } - else /* not a table; check metamethod */ + else { /* not a table; check metamethod */ if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) luaG_typeerror(L, t, "index"); + } /* try the metamethod */ if (ttisfunction(tm)) { luaT_callTM(L, tm, t, key, val, 0); return; } t = tm; /* else repeat assignment over 'tm' */ + if (luaV_fastset(L, t, key, oldval, luaH_get, val)) + return; /* done */ + /* else loop */ } luaG_runerror(L, "settable chain too long; possible loop"); } @@ -443,6 +442,17 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) { #define isemptystr(o) (ttisshrstring(o) && tsvalue(o)->shrlen == 0) +/* copy strings in stack from top - n up to top - 1 to buffer */ +static void copy2buff (StkId top, int n, char *buff) { + size_t tl = 0; /* size already copied */ + do { + size_t l = vslen(top - n); /* length of string being copied */ + memcpy(buff + tl, svalue(top - n), l * sizeof(char)); + tl += l; + } while (--n > 0); +} + + /* ** Main operation for concatenation: concat 'total' values in the stack, ** from 'L->top - total' up to 'L->top - 1'. @@ -462,24 +472,24 @@ void luaV_concat (lua_State *L, int total) { else { /* at least two non-empty string values; get as many as possible */ size_t tl = vslen(top - 1); - char *buffer; - int i; - /* collect total length */ - for (i = 1; i < total && tostring(L, top-i-1); i++) { - size_t l = vslen(top - i - 1); + TString *ts; + /* collect total length and number of strings */ + for (n = 1; n < total && tostring(L, top - n - 1); n++) { + size_t l = vslen(top - n - 1); if (l >= (MAX_SIZE/sizeof(char)) - tl) luaG_runerror(L, "string length overflow"); tl += l; } - buffer = luaZ_openspace(L, &G(L)->buff, tl); - tl = 0; - n = i; - do { /* copy all strings to buffer */ - size_t l = vslen(top - i); - memcpy(buffer+tl, svalue(top-i), l * sizeof(char)); - tl += l; - } while (--i > 0); - setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl)); /* create result */ + if (tl <= LUAI_MAXSHORTLEN) { /* is result a short string? */ + char buff[LUAI_MAXSHORTLEN]; + copy2buff(top, n, buff); /* copy strings to buffer */ + ts = luaS_newlstr(L, buff, tl); + } + else { /* long string; copy strings directly to final result */ + ts = luaS_createlngstrobj(L, tl); + copy2buff(top, n, getstr(ts)); + } + setsvalue2s(L, top - n, ts); /* create result */ } total -= n-1; /* got 'n' strings to create 1 new */ L->top -= n-1; /* popped 'n' strings and pushed one */ @@ -700,27 +710,20 @@ void luaV_finishOp (lua_State *L) { ** some macros for common tasks in 'luaV_execute' */ -#if !defined(luai_runtimecheck) -#define luai_runtimecheck(L, c) /* void */ -#endif - #define RA(i) (base+GETARG_A(i)) -/* to be used after possible stack reallocation */ #define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i)) #define RC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i)) #define RKB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \ ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i)) #define RKC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \ ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i)) -#define KBx(i) \ - (k + (GETARG_Bx(i) != 0 ? GETARG_Bx(i) - 1 : GETARG_Ax(*ci->u.l.savedpc++))) /* execute a jump instruction */ #define dojump(ci,i,e) \ { int a = GETARG_A(i); \ - if (a > 0) luaF_close(L, ci->u.l.base + a - 1); \ + if (a != 0) luaF_close(L, ci->u.l.base + a - 1); \ ci->u.l.savedpc += GETARG_sBx(i) + e; } /* for test instructions, execute the jump instruction that follows it */ @@ -730,34 +733,49 @@ void luaV_finishOp (lua_State *L) { #define Protect(x) { {x;}; base = ci->u.l.base; } #define checkGC(L,c) \ - Protect( luaC_condGC(L,{L->top = (c); /* limit of live values */ \ - luaC_step(L); \ - L->top = ci->top;}) /* restore top */ \ - luai_threadyield(L); ) + { luaC_condGC(L, L->top = (c), /* limit of live values */ \ + Protect(L->top = ci->top)); /* restore top */ \ + luai_threadyield(L); } #define vmdispatch(o) switch(o) #define vmcase(l) case l: #define vmbreak break + +/* +** copy of 'luaV_gettable', but protecting call to potential metamethod +** (which can reallocate the stack) +*/ +#define gettableProtected(L,t,k,v) { const TValue *aux; \ + if (luaV_fastget(L,t,k,aux,luaH_get)) { setobj2s(L, v, aux); } \ + else Protect(luaV_finishget(L,t,k,v,aux)); } + + +/* same for 'luaV_settable' */ +#define settableProtected(L,t,k,v) { const TValue *slot; \ + if (!luaV_fastset(L,t,k,slot,luaH_get,v)) \ + Protect(luaV_finishset(L,t,k,v,slot)); } + + + void luaV_execute (lua_State *L) { CallInfo *ci = L->ci; LClosure *cl; TValue *k; StkId base; + ci->callstatus |= CIST_FRESH; /* fresh invocation of 'luaV_execute" */ newframe: /* reentry point when frame changes (call/return) */ lua_assert(ci == L->ci); - cl = clLvalue(ci->func); - k = cl->p->k; - base = ci->u.l.base; + cl = clLvalue(ci->func); /* local reference to function's closure */ + k = cl->p->k; /* local reference to function's constant table */ + base = ci->u.l.base; /* local copy of function's base */ /* main loop of interpreter */ for (;;) { Instruction i = *(ci->u.l.savedpc++); StkId ra; - if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) && - (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) { + if (L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) Protect(luaG_traceexec(L)); - } /* WARNING: several calls may realloc the stack and invalidate 'ra' */ ra = RA(i); lua_assert(base == ci->u.l.base); @@ -797,17 +815,22 @@ void luaV_execute (lua_State *L) { vmbreak; } vmcase(OP_GETTABUP) { - int b = GETARG_B(i); - Protect(luaV_gettable(L, cl->upvals[b]->v, RKC(i), ra)); + TValue *upval = cl->upvals[GETARG_B(i)]->v; + TValue *rc = RKC(i); + gettableProtected(L, upval, rc, ra); vmbreak; } vmcase(OP_GETTABLE) { - Protect(luaV_gettable(L, RB(i), RKC(i), ra)); + StkId rb = RB(i); + TValue *rc = RKC(i); + gettableProtected(L, rb, rc, ra); vmbreak; } vmcase(OP_SETTABUP) { - int a = GETARG_A(i); - Protect(luaV_settable(L, cl->upvals[a]->v, RKB(i), RKC(i))); + TValue *upval = cl->upvals[GETARG_A(i)]->v; + TValue *rb = RKB(i); + TValue *rc = RKC(i); + settableProtected(L, upval, rb, rc); vmbreak; } vmcase(OP_SETUPVAL) { @@ -817,7 +840,9 @@ void luaV_execute (lua_State *L) { vmbreak; } vmcase(OP_SETTABLE) { - Protect(luaV_settable(L, ra, RKB(i), RKC(i))); + TValue *rb = RKB(i); + TValue *rc = RKC(i); + settableProtected(L, ra, rb, rc); vmbreak; } vmcase(OP_NEWTABLE) { @@ -831,9 +856,15 @@ void luaV_execute (lua_State *L) { vmbreak; } vmcase(OP_SELF) { + const TValue *aux; StkId rb = RB(i); - setobjs2s(L, ra+1, rb); - Protect(luaV_gettable(L, rb, RKC(i), ra)); + TValue *rc = RKC(i); + TString *key = tsvalue(rc); /* key must be a string */ + setobjs2s(L, ra + 1, rb); + if (luaV_fastget(L, rb, key, aux, luaH_getstr)) { + setobj2s(L, ra, aux); + } + else Protect(luaV_finishget(L, rb, rc, ra, aux)); vmbreak; } vmcase(OP_ADD) { @@ -1020,7 +1051,7 @@ void luaV_execute (lua_State *L) { StkId rb; L->top = base + c + 1; /* mark the end of concat operands */ Protect(luaV_concat(L, c - b + 1)); - ra = RA(i); /* 'luav_concat' may invoke TMs and move the stack */ + ra = RA(i); /* 'luaV_concat' may invoke TMs and move the stack */ rb = base + b; setobjs2s(L, ra, rb); checkGC(L, (ra >= rb ? ra + 1 : rb)); @@ -1035,7 +1066,7 @@ void luaV_execute (lua_State *L) { TValue *rb = RKB(i); TValue *rc = RKC(i); Protect( - if (cast_int(luaV_equalobj(L, rb, rc)) != GETARG_A(i)) + if (luaV_equalobj(L, rb, rc) != GETARG_A(i)) ci->u.l.savedpc++; else donextjump(ci); @@ -1082,12 +1113,12 @@ void luaV_execute (lua_State *L) { int nresults = GETARG_C(i) - 1; if (b != 0) L->top = ra+b; /* else previous instruction set top */ if (luaD_precall(L, ra, nresults)) { /* C function? */ - if (nresults >= 0) L->top = ci->top; /* adjust results */ - base = ci->u.l.base; + if (nresults >= 0) + L->top = ci->top; /* adjust results */ + Protect((void)0); /* update 'base' */ } else { /* Lua function */ ci = L->ci; - ci->callstatus |= CIST_REENTRY; goto newframe; /* restart luaV_execute over new Lua function */ } vmbreak; @@ -1096,8 +1127,9 @@ void luaV_execute (lua_State *L) { int b = GETARG_B(i); if (b != 0) L->top = ra+b; /* else previous instruction set top */ lua_assert(GETARG_C(i) - 1 == LUA_MULTRET); - if (luaD_precall(L, ra, LUA_MULTRET)) /* C function? */ - base = ci->u.l.base; + if (luaD_precall(L, ra, LUA_MULTRET)) { /* C function? */ + Protect((void)0); /* update 'base' */ + } else { /* tail call: put called frame (n) in place of caller one (o) */ CallInfo *nci = L->ci; /* called frame */ @@ -1125,8 +1157,8 @@ void luaV_execute (lua_State *L) { vmcase(OP_RETURN) { int b = GETARG_B(i); if (cl->p->sizep > 0) luaF_close(L, base); - b = luaD_poscall(L, ra, (b != 0 ? b - 1 : L->top - ra)); - if (!(ci->callstatus & CIST_REENTRY)) /* 'ci' still the called one */ + b = luaD_poscall(L, ci, ra, (b != 0 ? b - 1 : cast_int(L->top - ra))); + if (ci->callstatus & CIST_FRESH) /* local 'ci' still from callee */ return; /* external invocation: return */ else { /* invocation via reentry: continue execution */ ci = L->ci; @@ -1139,7 +1171,7 @@ void luaV_execute (lua_State *L) { vmcase(OP_FORLOOP) { if (ttisinteger(ra)) { /* integer loop? */ lua_Integer step = ivalue(ra + 2); - lua_Integer idx = ivalue(ra) + step; /* increment index */ + lua_Integer idx = intop(+, ivalue(ra), step); /* increment index */ lua_Integer limit = ivalue(ra + 1); if ((0 < step) ? (idx <= limit) : (limit <= idx)) { ci->u.l.savedpc += GETARG_sBx(i); /* jump back */ @@ -1171,7 +1203,7 @@ void luaV_execute (lua_State *L) { /* all values are integer */ lua_Integer initv = (stopnow ? 0 : ivalue(init)); setivalue(plimit, ilimit); - setivalue(init, initv - ivalue(pstep)); + setivalue(init, intop(-, initv, ivalue(pstep))); } else { /* try making all values floats */ lua_Number ninit; lua_Number nlimit; lua_Number nstep; @@ -1194,7 +1226,7 @@ void luaV_execute (lua_State *L) { setobjs2s(L, cb+1, ra+1); setobjs2s(L, cb, ra); L->top = cb + 3; /* func. + 2 args (state and index) */ - Protect(luaD_call(L, cb, GETARG_C(i), 1)); + Protect(luaD_call(L, cb, GETARG_C(i))); L->top = ci->top; i = *(ci->u.l.savedpc++); /* go to next instruction */ ra = RA(i); @@ -1219,11 +1251,10 @@ void luaV_execute (lua_State *L) { lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_EXTRAARG); c = GETARG_Ax(*ci->u.l.savedpc++); } - luai_runtimecheck(L, ttistable(ra)); h = hvalue(ra); last = ((c-1)*LFIELDS_PER_FLUSH) + n; if (last > h->sizearray) /* needs more space? */ - luaH_resizearray(L, h, last); /* pre-allocate it at once */ + luaH_resizearray(L, h, last); /* preallocate it at once */ for (; n > 0; n--) { TValue *val = ra+n; luaH_setint(L, h, last--, val); @@ -1243,23 +1274,21 @@ void luaV_execute (lua_State *L) { vmbreak; } vmcase(OP_VARARG) { - int b = GETARG_B(i) - 1; + int b = GETARG_B(i) - 1; /* required results */ int j; int n = cast_int(base - ci->func) - cl->p->numparams - 1; + if (n < 0) /* less arguments than parameters? */ + n = 0; /* no vararg arguments */ if (b < 0) { /* B == 0? */ b = n; /* get all var. arguments */ Protect(luaD_checkstack(L, n)); ra = RA(i); /* previous call may change the stack */ L->top = ra + n; } - for (j = 0; j < b; j++) { - if (j < n) { - setobjs2s(L, ra + j, base - n + j); - } - else { - setnilvalue(ra + j); - } - } + for (j = 0; j < b && j < n; j++) + setobjs2s(L, ra + j, base - n + j); + for (; j < b; j++) /* complete required results with nil */ + setnilvalue(ra + j); vmbreak; } vmcase(OP_EXTRAARG) { diff --git a/libs/lua-5.3.2/src/lvm.h b/libs/lua-5.3.2/src/lvm.h new file mode 100644 index 0000000..fd0e748 --- /dev/null +++ b/libs/lua-5.3.2/src/lvm.h @@ -0,0 +1,114 @@ +/* +** $Id: lvm.h,v 2.39 2015/09/09 13:44:07 roberto Exp $ +** Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#ifndef lvm_h +#define lvm_h + + +#include "ldo.h" +#include "lobject.h" +#include "ltm.h" + + +#if !defined(LUA_NOCVTN2S) +#define cvt2str(o) ttisnumber(o) +#else +#define cvt2str(o) 0 /* no conversion from numbers to strings */ +#endif + + +#if !defined(LUA_NOCVTS2N) +#define cvt2num(o) ttisstring(o) +#else +#define cvt2num(o) 0 /* no conversion from strings to numbers */ +#endif + + +/* +** You can define LUA_FLOORN2I if you want to convert floats to integers +** by flooring them (instead of raising an error if they are not +** integral values) +*/ +#if !defined(LUA_FLOORN2I) +#define LUA_FLOORN2I 0 +#endif + + +#define tonumber(o,n) \ + (ttisfloat(o) ? (*(n) = fltvalue(o), 1) : luaV_tonumber_(o,n)) + +#define tointeger(o,i) \ + (ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o,i,LUA_FLOORN2I)) + +#define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2)) + +#define luaV_rawequalobj(t1,t2) luaV_equalobj(NULL,t1,t2) + + +/* +** fast track for 'gettable': 1 means 'aux' points to resulted value; +** 0 means 'aux' is metamethod (if 't' is a table) or NULL. 'f' is +** the raw get function to use. +*/ +#define luaV_fastget(L,t,k,aux,f) \ + (!ttistable(t) \ + ? (aux = NULL, 0) /* not a table; 'aux' is NULL and result is 0 */ \ + : (aux = f(hvalue(t), k), /* else, do raw access */ \ + !ttisnil(aux) ? 1 /* result not nil? 'aux' has it */ \ + : (aux = fasttm(L, hvalue(t)->metatable, TM_INDEX), /* get metamethod */\ + aux != NULL ? 0 /* has metamethod? must call it */ \ + : (aux = luaO_nilobject, 1)))) /* else, final result is nil */ + +/* +** standard implementation for 'gettable' +*/ +#define luaV_gettable(L,t,k,v) { const TValue *aux; \ + if (luaV_fastget(L,t,k,aux,luaH_get)) { setobj2s(L, v, aux); } \ + else luaV_finishget(L,t,k,v,aux); } + + +/* +** Fast track for set table. If 't' is a table and 't[k]' is not nil, +** call GC barrier, do a raw 't[k]=v', and return true; otherwise, +** return false with 'slot' equal to NULL (if 't' is not a table) or +** 'nil'. (This is needed by 'luaV_finishget'.) Note that, if the macro +** returns true, there is no need to 'invalidateTMcache', because the +** call is not creating a new entry. +*/ +#define luaV_fastset(L,t,k,slot,f,v) \ + (!ttistable(t) \ + ? (slot = NULL, 0) \ + : (slot = f(hvalue(t), k), \ + ttisnil(slot) ? 0 \ + : (luaC_barrierback(L, hvalue(t), v), \ + setobj2t(L, cast(TValue *,slot), v), \ + 1))) + + +#define luaV_settable(L,t,k,v) { const TValue *slot; \ + if (!luaV_fastset(L,t,k,slot,luaH_get,v)) \ + luaV_finishset(L,t,k,v,slot); } + + + +LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2); +LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r); +LUAI_FUNC int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r); +LUAI_FUNC int luaV_tonumber_ (const TValue *obj, lua_Number *n); +LUAI_FUNC int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode); +LUAI_FUNC void luaV_finishget (lua_State *L, const TValue *t, TValue *key, + StkId val, const TValue *tm); +LUAI_FUNC void luaV_finishset (lua_State *L, const TValue *t, TValue *key, + StkId val, const TValue *oldval); +LUAI_FUNC void luaV_finishOp (lua_State *L); +LUAI_FUNC void luaV_execute (lua_State *L); +LUAI_FUNC void luaV_concat (lua_State *L, int total); +LUAI_FUNC lua_Integer luaV_div (lua_State *L, lua_Integer x, lua_Integer y); +LUAI_FUNC lua_Integer luaV_mod (lua_State *L, lua_Integer x, lua_Integer y); +LUAI_FUNC lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y); +LUAI_FUNC void luaV_objlen (lua_State *L, StkId ra, const TValue *rb); + +#endif diff --git a/libs/lua-5.3.1/src/lzio.c b/libs/lua-5.3.2/src/lzio.c similarity index 79% rename from libs/lua-5.3.1/src/lzio.c rename to libs/lua-5.3.2/src/lzio.c index 4649392..c9e1f49 100644 --- a/libs/lua-5.3.1/src/lzio.c +++ b/libs/lua-5.3.2/src/lzio.c @@ -1,5 +1,5 @@ /* -** $Id: lzio.c,v 1.36 2014/11/02 19:19:04 roberto Exp $ +** $Id: lzio.c,v 1.37 2015/09/08 15:41:05 roberto Exp $ ** Buffered streams ** See Copyright Notice in lua.h */ @@ -66,13 +66,3 @@ size_t luaZ_read (ZIO *z, void *b, size_t n) { return 0; } -/* ------------------------------------------------------------------------ */ -char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n) { - if (n > buff->buffsize) { - if (n < LUA_MINBUFFER) n = LUA_MINBUFFER; - luaZ_resizebuffer(L, buff, n); - } - return buff->buffer; -} - - diff --git a/libs/lua-5.3.1/src/lzio.h b/libs/lua-5.3.2/src/lzio.h similarity index 91% rename from libs/lua-5.3.1/src/lzio.h rename to libs/lua-5.3.2/src/lzio.h index b2e56bc..e7b6f34 100644 --- a/libs/lua-5.3.1/src/lzio.h +++ b/libs/lua-5.3.2/src/lzio.h @@ -1,5 +1,5 @@ /* -** $Id: lzio.h,v 1.30 2014/12/19 17:26:14 roberto Exp $ +** $Id: lzio.h,v 1.31 2015/09/08 15:41:05 roberto Exp $ ** Buffered streams ** See Copyright Notice in lua.h */ @@ -44,7 +44,6 @@ typedef struct Mbuffer { #define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0) -LUAI_FUNC char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n); LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data); LUAI_FUNC size_t luaZ_read (ZIO* z, void *b, size_t n); /* read next n bytes */ From b4d025d6021a1cbf6f1a185de57af946a6f83187 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Wed, 9 Dec 2015 23:14:23 +0100 Subject: [PATCH 040/101] Updated the sf2dlib and sftdlib; Added the gfx.color.hex() function; Added the New3DS CPU mode control. As the color order of the sf2dlib changed, you have to change it in your code, or use the color.hex() function. To fix the problems, just change "0xRRGGBBAA" to "0xAABBGGRR". Also, the shader compiler changed to Picasso, so you'll need it in order to compile. https://github.com/fincs/picasso --- libs/sf2dlib/libsf2d/Makefile | 7 +-- libs/sf2dlib/libsf2d/data/shader.vsh | 61 +++++++++------------ libs/sf2dlib/libsf2d/include/sf2d.h | 51 ++++++++++++----- libs/sf2dlib/libsf2d/source/sf2d.c | 18 ++++-- libs/sf2dlib/libsf2d/source/sf2d_draw.c | 45 ++++----------- libs/sf2dlib/libsf2d/source/sf2d_private.c | 15 +---- libs/sf2dlib/libsf2d/source/sf2d_texture.c | 46 ++++++++++------ libs/sf2dlib/sample/Makefile | 2 +- libs/sf2dlib/sample/source/main.c | 20 +++++++ libs/sftdlib/libsftd/source/sftd.c | 3 +- libs/sftdlib/libsftd/source/texture_atlas.c | 3 +- sdcard/3ds/ctruLua/examples/example.lua | 4 +- sdcard/3ds/ctruLua/libs/keyboard.lua | 4 +- sdcard/3ds/ctruLua/libs/openfile.lua | 2 +- source/color.c | 22 +++++++- source/ptm.c | 29 +++++++++- source/texture.c | 4 +- 17 files changed, 205 insertions(+), 131 deletions(-) diff --git a/libs/sf2dlib/libsf2d/Makefile b/libs/sf2dlib/libsf2d/Makefile index 99c44b9..a035946 100644 --- a/libs/sf2dlib/libsf2d/Makefile +++ b/libs/sf2dlib/libsf2d/Makefile @@ -125,15 +125,14 @@ $(OUTPUT) : $(OFILES) # WARNING: This is not the right way to do this! TODO: Do it right! #--------------------------------------------------------------------------------- -%_vsh.h %.vsh.o : %.vsh +%.vsh.o : %.vsh #--------------------------------------------------------------------------------- @echo $(notdir $<) - @python ../../../aemstro/aemstro_as.py $< ../$(notdir $<).shbin - @bin2s ../$(notdir $<).shbin | $(PREFIX)as -o $@ + @picasso -o $(notdir $<).shbin $< + @bin2s $(notdir $<).shbin | $(PREFIX)as -o $@ @echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(notdir $<).shbin | tr . _)`.h @echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(notdir $<).shbin | tr . _)`.h @echo "extern const u32" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(notdir $<).shbin | tr . _)`.h - @rm ../$(notdir $<).shbin -include $(DEPENDS) diff --git a/libs/sf2dlib/libsf2d/data/shader.vsh b/libs/sf2dlib/libsf2d/data/shader.vsh index 2a604d5..3793136 100644 --- a/libs/sf2dlib/libsf2d/data/shader.vsh +++ b/libs/sf2dlib/libsf2d/data/shader.vsh @@ -1,39 +1,30 @@ -; setup constants - .const c20, 0.0, 0.0, 0.0, 1.0 +; Outputs +.out outpos position +.out outtc0 texcoord0 +.out outclr color -; setup outmap - .out o0, result.position, 0xF - .out o1, result.texcoord0, 0x3 - .out o2, result.color, 0xF +; Inputs +.alias inpos v0 +.alias inarg v1 -; setup uniform map (not required) - .uniform c0, c3, projection +; Uniforms +.fvec projection[4] - .vsh vmain, end_vmain +; Constants +.constf RGBA8_TO_FLOAT4(0.00392156862, 0, 0, 0) -;code - vmain: - ; result.pos = projMtx * in.pos - dp4 o0, c0, v0 (0x0) - dp4 o0, c1, v0 (0x1) - dp4 o0, c2, v0 (0x2) - dp4 o0, c3, v0 (0x3) - ; result.texcoord = in.texcoord - mov o1, v1 (0x5) - ; result.color = in.color - mov o2, v1 (0x5) - nop - end - end_vmain: - -;operand descriptors - .opdesc x___, xyzw, xyzw ; 0x0 - .opdesc _y__, xyzw, xyzw ; 0x1 - .opdesc __z_, xyzw, xyzw ; 0x2 - .opdesc ___w, xyzw, xyzw ; 0x3 - .opdesc xyz_, xyzw, xyzw ; 0x4 - .opdesc xyzw, xyzw, xyzw ; 0x5 - .opdesc x_zw, xyzw, xyzw ; 0x6 - .opdesc xyzw, yyyw, xyzw ; 0x7 - .opdesc xyz_, wwww, wwww ; 0x8 - .opdesc xyz_, yyyy, xyzw ; 0x9 +.proc main + ; outpos = projection * in.pos + dp4 outpos.x, projection[0].wzyx, inpos + dp4 outpos.y, projection[1].wzyx, inpos + dp4 outpos.z, projection[2].wzyx, inpos + dp4 outpos.w, projection[3].wzyx, inpos + + ; outtc0 = in.texcoord + mov outtc0, inarg + + ; outclr = RGBA8_TO_FLOAT4(in.color) + mul outclr, RGBA8_TO_FLOAT4.xxxx, inarg + + end +.end diff --git a/libs/sf2dlib/libsf2d/include/sf2d.h b/libs/sf2dlib/libsf2d/include/sf2d.h index 9df8d08..6385b83 100644 --- a/libs/sf2dlib/libsf2d/include/sf2d.h +++ b/libs/sf2dlib/libsf2d/include/sf2d.h @@ -23,7 +23,12 @@ extern "C" { * @param b the blue component of the color to create * @param a the alpha component of the color to create */ -#define RGBA8(r, g, b, a) ((((r)&0xFF)<<24) | (((g)&0xFF)<<16) | (((b)&0xFF)<<8) | (((a)&0xFF)<<0)) +#define RGBA8(r, g, b, a) ((((a)&0xFF)<<24) | (((b)&0xFF)<<16) | (((g)&0xFF)<<8) | (((r)&0xFF)<<0)) + +#define RGBA8_GET_R(c) (((c) >> 0) & 0xFF) +#define RGBA8_GET_G(c) (((c) >> 8) & 0xFF) +#define RGBA8_GET_B(c) (((c) >> 16) & 0xFF) +#define RGBA8_GET_A(c) (((c) >> 24) & 0xFF) /** * @brief Default size of the GPU commands FIFO buffer @@ -96,23 +101,13 @@ typedef struct { } sf2d_vector_3f; /** - * @brief Represents a four dimensional float vector - */ - -typedef struct { - float r; /**< Red component of the vector/color */ - float g; /**< Green component of the vector/color */ - float b; /**< Blue component of the vector/color */ - float a; /**< Alpha component of the vector/color */ -} sf2d_vector_4f; - -/** - * @brief Represents a vertex containing position and color attributes + * @brief Represents a vertex containing position (float) + * and color (unsigned int) */ typedef struct { sf2d_vector_3f position; /**< Position of the vertex */ - sf2d_vector_4f color; /**< Color of the vertex */ + u32 color; /**< Color of the vertex */ } sf2d_vertex_pos_col; /** @@ -132,6 +127,7 @@ typedef struct { sf2d_place place; /**< Where the texture data resides, RAM or VRAM */ int tiled; /**< Whether the tetxure is tiled or not */ sf2d_texfmt pixel_format; /**< Pixel format */ + u32 params; /**< Texture filters and wrapping */ int width; /**< Texture width */ int height; /**< Texture height */ int pow2_w; /**< Nearest power of 2 >= width */ @@ -210,6 +206,15 @@ void *sf2d_pool_malloc(u32 size); */ void *sf2d_pool_memalign(u32 size, u32 alignment); +/** + * @brief Allocates aligned memory for an array from a temporary pool. Works as sf2d_pool_malloc + * @param nmemb the number of elements to allocate + * @param size the size (and alignment) of each element to allocate + * @note Unlike libc's calloc, this function does not initialize to 0, + * and returns a pointer aligned to size. + */ +void *sf2d_pool_calloc(u32 nmemb, u32 size); + /** * @brief Returns the temporary pool's free space * @return the temporary pool's free space @@ -282,6 +287,8 @@ void sf2d_draw_fill_circle(int x, int y, int radius, u32 color); * @return a pointer to the newly created texture * @note Before drawing the texture, it needs to be tiled * by calling sf2d_texture_tile32. + * The default texture params are both min and mag filters + * GPU_NEAREST, and both S and T wrappings GPU_CLAMP_TO_BORDER. */ sf2d_texture *sf2d_create_texture(int width, int height, sf2d_texfmt pixel_format, sf2d_place place); @@ -336,6 +343,22 @@ void sf2d_bind_texture_color(const sf2d_texture *texture, GPU_TEXUNIT unit, u32 */ void sf2d_bind_texture_parameters(const sf2d_texture *texture, GPU_TEXUNIT unit, unsigned int params); +/** + * @brief Changes the texture params (filters and wrapping) + * @param texture the texture to change the params + * @param params the new texture params to use. You can use the + * GPU_TEXTURE_[MIN,MAG]_FILTER and GPU_TEXTURE_WRAP_[S,T] + * macros as helpers. + */ +void sf2d_texture_set_params(sf2d_texture *texture, u32 params); + +/** + * @brief Returns the texture params + * @param texture the texture to get the params + * @return the current texture params of texture + */ +int sf2d_texture_get_params(const sf2d_texture *texture); + /** * @brief Draws a texture * @param texture the texture to draw diff --git a/libs/sf2dlib/libsf2d/source/sf2d.c b/libs/sf2dlib/libsf2d/source/sf2d.c index 6f564fa..52e5309 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d.c +++ b/libs/sf2dlib/libsf2d/source/sf2d.c @@ -4,7 +4,7 @@ static int sf2d_initialized = 0; -static u32 clear_color = RGBA8(0x00, 0x00, 0x00, 0xFF); +static u32 clear_color = 0; static u32 *gpu_cmd = NULL; //GPU init variables static int gpu_cmd_size = 0; @@ -193,8 +193,9 @@ void sf2d_end_frame() gspWaitForPPF(); //Clear the screen - GX_SetMemoryFill(NULL, gpu_fb_addr, clear_color, &gpu_fb_addr[0x2EE00], - 0x201, gpu_depth_fb_addr, 0x00000000, &gpu_depth_fb_addr[0x2EE00], 0x201); + GX_SetMemoryFill(NULL, + gpu_fb_addr, clear_color, &gpu_fb_addr[240*400], GX_FILL_TRIGGER | GX_FILL_32BIT_DEPTH, + gpu_depth_fb_addr, 0, &gpu_depth_fb_addr[240*400], GX_FILL_TRIGGER | GX_FILL_32BIT_DEPTH); gspWaitForPSC0(); } @@ -245,6 +246,11 @@ void *sf2d_pool_memalign(u32 size, u32 alignment) return NULL; } +void *sf2d_pool_calloc(u32 nmemb, u32 size) +{ + return sf2d_pool_memalign(nmemb * size, size); +} + unsigned int sf2d_pool_space_free() { return pool_size - pool_index; @@ -257,7 +263,11 @@ void sf2d_pool_reset() void sf2d_set_clear_color(u32 color) { - clear_color = color; + // GX_SetMemoryFill wants the color inverted? + clear_color = RGBA8_GET_R(color) << 24 | + RGBA8_GET_G(color) << 16 | + RGBA8_GET_B(color) << 8 | + RGBA8_GET_A(color) << 0; } void sf2d_set_scissor_test(GPU_SCISSORMODE mode, u32 x, u32 y, u32 w, u32 h) diff --git a/libs/sf2dlib/libsf2d/source/sf2d_draw.c b/libs/sf2dlib/libsf2d/source/sf2d_draw.c index bf8f896..d84966b 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d_draw.c +++ b/libs/sf2dlib/libsf2d/source/sf2d_draw.c @@ -4,7 +4,7 @@ void sf2d_draw_line(int x0, int y0, int x1, int y1, u32 color) { - sf2d_vertex_pos_col *vertices = sf2d_pool_malloc(4 * sizeof(sf2d_vertex_pos_col)); + sf2d_vertex_pos_col *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_col), 8); if (!vertices) return; vertices[0].position = (sf2d_vector_3f){(float)x0+1.0f, (float)y0+1.0f, SF2D_DEFAULT_DEPTH}; @@ -12,12 +12,7 @@ void sf2d_draw_line(int x0, int y0, int x1, int y1, u32 color) vertices[2].position = (sf2d_vector_3f){(float)x1+1.0f, (float)y1+1.0f, SF2D_DEFAULT_DEPTH}; vertices[3].position = (sf2d_vector_3f){(float)x1-1.0f, (float)y1-1.0f, SF2D_DEFAULT_DEPTH}; - u8 r = (color>>24) & 0xFF; - u8 g = (color>>16) & 0xFF; - u8 b = (color>>8) & 0xFF; - u8 a = color & 0xFF; - - vertices[0].color = (sf2d_vector_4f){r/255.0f, g/255.0f, b/255.0f, a/255.0f}; + vertices[0].color = color; vertices[1].color = vertices[0].color; vertices[2].color = vertices[0].color; vertices[3].color = vertices[0].color; @@ -35,7 +30,7 @@ void sf2d_draw_line(int x0, int y0, int x1, int y1, u32 color) GPU_SetAttributeBuffers( 2, // number of attributes (u32*)osConvertVirtToPhys((u32)vertices), - GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_FLOAT), + GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE), 0xFFFC, //0b1100 0x10, 1, //number of buffers @@ -49,7 +44,7 @@ void sf2d_draw_line(int x0, int y0, int x1, int y1, u32 color) void sf2d_draw_rectangle(int x, int y, int w, int h, u32 color) { - sf2d_vertex_pos_col *vertices = sf2d_pool_malloc(4 * sizeof(sf2d_vertex_pos_col)); + sf2d_vertex_pos_col *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_col), 8); if (!vertices) return; vertices[0].position = (sf2d_vector_3f){(float)x, (float)y, SF2D_DEFAULT_DEPTH}; @@ -57,12 +52,7 @@ void sf2d_draw_rectangle(int x, int y, int w, int h, u32 color) vertices[2].position = (sf2d_vector_3f){(float)x, (float)y+h, SF2D_DEFAULT_DEPTH}; vertices[3].position = (sf2d_vector_3f){(float)x+w, (float)y+h, SF2D_DEFAULT_DEPTH}; - u8 r = (color>>24) & 0xFF; - u8 g = (color>>16) & 0xFF; - u8 b = (color>>8) & 0xFF; - u8 a = color & 0xFF; - - vertices[0].color = (sf2d_vector_4f){r/255.0f, g/255.0f, b/255.0f, a/255.0f}; + vertices[0].color = color; vertices[1].color = vertices[0].color; vertices[2].color = vertices[0].color; vertices[3].color = vertices[0].color; @@ -80,7 +70,7 @@ void sf2d_draw_rectangle(int x, int y, int w, int h, u32 color) GPU_SetAttributeBuffers( 2, // number of attributes (u32*)osConvertVirtToPhys((u32)vertices), - GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_FLOAT), + GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE), 0xFFFC, //0b1100 0x10, 1, //number of buffers @@ -94,7 +84,7 @@ void sf2d_draw_rectangle(int x, int y, int w, int h, u32 color) void sf2d_draw_rectangle_rotate(int x, int y, int w, int h, u32 color, float rad) { - sf2d_vertex_pos_col *vertices = sf2d_pool_malloc(4 * sizeof(sf2d_vertex_pos_col)); + sf2d_vertex_pos_col *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_col), 8); if (!vertices) return; int w2 = w/2.0f; @@ -105,12 +95,7 @@ void sf2d_draw_rectangle_rotate(int x, int y, int w, int h, u32 color, float rad vertices[2].position = (sf2d_vector_3f){(float)-w2, (float) h2, SF2D_DEFAULT_DEPTH}; vertices[3].position = (sf2d_vector_3f){(float) w2, (float) h2, SF2D_DEFAULT_DEPTH}; - u8 r = (color>>24) & 0xFF; - u8 g = (color>>16) & 0xFF; - u8 b = (color>>8) & 0xFF; - u8 a = color & 0xFF; - - vertices[0].color = (sf2d_vector_4f){r/255.0f, g/255.0f, b/255.0f, a/255.0f}; + vertices[0].color = color; vertices[1].color = vertices[0].color; vertices[2].color = vertices[0].color; vertices[3].color = vertices[0].color; @@ -138,7 +123,7 @@ void sf2d_draw_rectangle_rotate(int x, int y, int w, int h, u32 color, float rad GPU_SetAttributeBuffers( 2, // number of attributes (u32*)osConvertVirtToPhys((u32)vertices), - GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_FLOAT), + GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE), 0xFFFC, //0b1100 0x10, 1, //number of buffers @@ -153,17 +138,11 @@ void sf2d_draw_rectangle_rotate(int x, int y, int w, int h, u32 color, float rad void sf2d_draw_fill_circle(int x, int y, int radius, u32 color) { static const int num_segments = 100; - sf2d_vertex_pos_col *vertices = sf2d_pool_malloc((num_segments + 2) * sizeof(sf2d_vertex_pos_col)); + sf2d_vertex_pos_col *vertices = sf2d_pool_memalign((num_segments + 2) * sizeof(sf2d_vertex_pos_col), 8); if (!vertices) return; vertices[0].position = (sf2d_vector_3f){(float)x, (float)y, SF2D_DEFAULT_DEPTH}; - - u8 r = (color>>24) & 0xFF; - u8 g = (color>>16) & 0xFF; - u8 b = (color>>8) & 0xFF; - u8 a = color & 0xFF; - - vertices[0].color = (sf2d_vector_4f){r/255.0f, g/255.0f, b/255.0f, a/255.0f}; + vertices[0].color = color; float theta = 2 * M_PI / (float)num_segments; float c = cosf(theta); @@ -199,7 +178,7 @@ void sf2d_draw_fill_circle(int x, int y, int radius, u32 color) GPU_SetAttributeBuffers( 2, // number of attributes (u32*)osConvertVirtToPhys((u32)vertices), - GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_FLOAT), + GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE), 0xFFFC, //0b1100 0x10, 1, //number of buffers diff --git a/libs/sf2dlib/libsf2d/source/sf2d_private.c b/libs/sf2dlib/libsf2d/source/sf2d_private.c index 44819a0..073c437 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d_private.c +++ b/libs/sf2dlib/libsf2d/source/sf2d_private.c @@ -24,16 +24,7 @@ void vector_mult_matrix4x4(const float *msrc, const sf2d_vector_3f *vsrc, sf2d_v void matrix_gpu_set_uniform(const float *m, u32 startreg) { - float mu[4*4]; - - int i, j; - for (i = 0; i < 4; i++) { - for (j = 0; j < 4; j++) { - mu[i*4 + j] = m[i*4 + (3-j)]; - } - } - - GPU_SetFloatUniform(GPU_VERTEX_SHADER, startreg, (u32 *)mu, 4); + GPU_SetFloatUniform(GPU_VERTEX_SHADER, startreg, (u32 *)m, 4); } void matrix_copy(float *dst, const float *src) @@ -109,7 +100,7 @@ void matrix_swap_xy(float *m) void matrix_init_orthographic(float *m, float left, float right, float bottom, float top, float near, float far) { float mo[4*4], mp[4*4]; - + mo[0x0] = 2.0f/(right-left); mo[0x1] = 0.0f; mo[0x2] = 0.0f; @@ -129,7 +120,7 @@ void matrix_init_orthographic(float *m, float left, float right, float bottom, f mo[0xD] = 0.0f; mo[0xE] = 0.0f; mo[0xF] = 1.0f; - + matrix_identity4x4(mp); mp[0xA] = 0.5; mp[0xB] = -0.5; diff --git a/libs/sf2dlib/libsf2d/source/sf2d_texture.c b/libs/sf2dlib/libsf2d/source/sf2d_texture.c index 2dc9fce..9918953 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d_texture.c +++ b/libs/sf2dlib/libsf2d/source/sf2d_texture.c @@ -71,6 +71,10 @@ sf2d_texture *sf2d_create_texture(int width, int height, sf2d_texfmt pixel_forma texture->tiled = 0; texture->place = place; texture->pixel_format = pixel_format; + texture->params = GPU_TEXTURE_MAG_FILTER(GPU_NEAREST) + | GPU_TEXTURE_MIN_FILTER(GPU_NEAREST) + | GPU_TEXTURE_WRAP_S(GPU_CLAMP_TO_BORDER) + | GPU_TEXTURE_WRAP_T(GPU_CLAMP_TO_BORDER); texture->width = width; texture->height = height; texture->pow2_w = pow2_w; @@ -145,7 +149,7 @@ void sf2d_bind_texture(const sf2d_texture *texture, GPU_TEXUNIT unit) (u32 *)osConvertVirtToPhys((u32)texture->data), texture->pow2_w, texture->pow2_h, - GPU_TEXTURE_MAG_FILTER(GPU_NEAREST) | GPU_TEXTURE_MIN_FILTER(GPU_NEAREST), + texture->params, texture->pixel_format ); } @@ -161,7 +165,7 @@ void sf2d_bind_texture_color(const sf2d_texture *texture, GPU_TEXUNIT unit, u32 GPU_TEVOPERANDS(0, 0, 0), GPU_TEVOPERANDS(0, 0, 0), GPU_MODULATE, GPU_MODULATE, - __builtin_bswap32(color) //RGBA8 -> ABGR8 + color ); GPU_SetTexture( @@ -169,7 +173,7 @@ void sf2d_bind_texture_color(const sf2d_texture *texture, GPU_TEXUNIT unit, u32 (u32 *)osConvertVirtToPhys((u32)texture->data), texture->pow2_w, texture->pow2_h, - GPU_TEXTURE_MAG_FILTER(GPU_NEAREST) | GPU_TEXTURE_MIN_FILTER(GPU_NEAREST), + texture->params, texture->pixel_format ); } @@ -198,9 +202,19 @@ void sf2d_bind_texture_parameters(const sf2d_texture *texture, GPU_TEXUNIT unit, ); } +void sf2d_texture_set_params(sf2d_texture *texture, u32 params) +{ + texture->params = params; +} + +int sf2d_texture_get_params(const sf2d_texture *texture) +{ + return texture->params; +} + static inline void sf2d_draw_texture_generic(const sf2d_texture *texture, int x, int y) { - sf2d_vertex_pos_tex *vertices = sf2d_pool_malloc(4 * sizeof(sf2d_vertex_pos_tex)); + sf2d_vertex_pos_tex *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_tex), 8); if (!vertices) return; int w = texture->width; @@ -248,7 +262,7 @@ void sf2d_draw_texture_blend(const sf2d_texture *texture, int x, int y, u32 colo static inline void sf2d_draw_texture_rotate_hotspot_generic(const sf2d_texture *texture, int x, int y, float rad, float center_x, float center_y) { - sf2d_vertex_pos_tex *vertices = sf2d_pool_malloc(4 * sizeof(sf2d_vertex_pos_tex)); + sf2d_vertex_pos_tex *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_tex), 8); if (!vertices) return; const float w = texture->width; @@ -332,7 +346,7 @@ void sf2d_draw_texture_rotate_blend(const sf2d_texture *texture, int x, int y, f static inline void sf2d_draw_texture_part_generic(const sf2d_texture *texture, int x, int y, int tex_x, int tex_y, int tex_w, int tex_h) { - sf2d_vertex_pos_tex *vertices = sf2d_pool_malloc(4 * sizeof(sf2d_vertex_pos_tex)); + sf2d_vertex_pos_tex *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_tex), 8); if (!vertices) return; vertices[0].position = (sf2d_vector_3f){(float)x, (float)y, SF2D_DEFAULT_DEPTH}; @@ -379,7 +393,7 @@ void sf2d_draw_texture_part_blend(const sf2d_texture *texture, int x, int y, int static inline void sf2d_draw_texture_scale_generic(const sf2d_texture *texture, int x, int y, float x_scale, float y_scale) { - sf2d_vertex_pos_tex *vertices = sf2d_pool_malloc(4 * sizeof(sf2d_vertex_pos_tex)); + sf2d_vertex_pos_tex *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_tex), 8); if (!vertices) return; int ws = texture->width * x_scale; @@ -427,7 +441,7 @@ void sf2d_draw_texture_scale_blend(const sf2d_texture *texture, int x, int y, fl static inline void sf2d_draw_texture_part_scale_generic(const sf2d_texture *texture, float x, float y, float tex_x, float tex_y, float tex_w, float tex_h, float x_scale, float y_scale) { - sf2d_vertex_pos_tex *vertices = sf2d_pool_malloc(4 * sizeof(sf2d_vertex_pos_tex)); + sf2d_vertex_pos_tex *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_tex), 8); if (!vertices) return; float u0 = tex_x/(float)texture->pow2_w; @@ -477,7 +491,7 @@ void sf2d_draw_texture_part_scale_blend(const sf2d_texture *texture, float x, fl static inline void sf2d_draw_texture_part_rotate_scale_generic(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale) { - sf2d_vertex_pos_tex *vertices = sf2d_pool_malloc(4 * sizeof(sf2d_vertex_pos_tex)); + sf2d_vertex_pos_tex *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_tex), 8); if (!vertices) return; int w2 = (tex_w * x_scale)/2.0f; @@ -537,7 +551,7 @@ void sf2d_draw_texture_part_rotate_scale_blend(const sf2d_texture *texture, int static inline void sf2d_draw_texture_depth_generic(const sf2d_texture *texture, int x, int y, signed short z) { - sf2d_vertex_pos_tex *vertices = sf2d_pool_malloc(4 * sizeof(sf2d_vertex_pos_tex)); + sf2d_vertex_pos_tex *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_tex), 8); if (!vertices) return; int w = texture->width; @@ -587,7 +601,7 @@ void sf2d_draw_texture_depth_blend(const sf2d_texture *texture, int x, int y, si void sf2d_draw_quad_uv(const sf2d_texture *texture, float left, float top, float right, float bottom, float u0, float v0, float u1, float v1, unsigned int params) { - sf2d_vertex_pos_tex *vertices = sf2d_pool_malloc(4 * sizeof(sf2d_vertex_pos_tex)); + sf2d_vertex_pos_tex *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_tex), 8); if (!vertices) return; vertices[0].position = (sf2d_vector_3f){left, top, SF2D_DEFAULT_DEPTH}; @@ -641,9 +655,9 @@ void sf2d_set_pixel(sf2d_texture *texture, int x, int y, u32 new_color) if (texture->tiled) { u32 coarse_y = y & ~7; u32 offset = get_morton_offset(x, y, 4) + coarse_y * texture->pow2_w * 4; - *(u32 *)(texture->data + offset) = __builtin_bswap32(new_color); + *(u32 *)(texture->data + offset) = new_color; } else { - ((u32 *)texture->data)[x + y * texture->pow2_w] = __builtin_bswap32(new_color); + ((u32 *)texture->data)[x + y * texture->pow2_w] = new_color; } } @@ -653,9 +667,9 @@ u32 sf2d_get_pixel(sf2d_texture *texture, int x, int y) if (texture->tiled) { u32 coarse_y = y & ~7; u32 offset = get_morton_offset(x, y, 4) + coarse_y * texture->pow2_w * 4; - return __builtin_bswap32(*(u32 *)(texture->data + offset)); + return *(u32 *)(texture->data + offset); } else { - return __builtin_bswap32(((u32 *)texture->data)[x + y * texture->pow2_w]); + return ((u32 *)texture->data)[x + y * texture->pow2_w]; } } @@ -675,7 +689,7 @@ void sf2d_texture_tile32(sf2d_texture *texture) u32 dst_offset = get_morton_offset(i, j, 4) + coarse_y * texture->pow2_w * 4; u32 v = ((u32 *)texture->data)[i + (texture->pow2_h - 1 - j)*texture->pow2_w]; - *(u32 *)(tmp + dst_offset) = __builtin_bswap32(v); + *(u32 *)(tmp + dst_offset) = __builtin_bswap32(v); /* RGBA8 -> ABGR8 */ } } diff --git a/libs/sf2dlib/sample/Makefile b/libs/sf2dlib/sample/Makefile index 840f32b..27ae68b 100644 --- a/libs/sf2dlib/sample/Makefile +++ b/libs/sf2dlib/sample/Makefile @@ -153,7 +153,7 @@ run: $(BUILD) @citra $(TARGET).3dsx #--------------------------------------------------------------------------------- copy_cia: $(TARGET).cia - @cp $(TARGET).cia /mnt/GATEWAYNAND + @cp $(TARGET).cia /mnt/3DS sync #--------------------------------------------------------------------------------- diff --git a/libs/sf2dlib/sample/source/main.c b/libs/sf2dlib/sample/source/main.c index fb96559..820888b 100644 --- a/libs/sf2dlib/sample/source/main.c +++ b/libs/sf2dlib/sample/source/main.c @@ -20,6 +20,8 @@ extern const struct { unsigned char pixel_data[]; } dice_img; +#define CONFIG_3D_SLIDERSTATE (*(float *)0x1FF81080) + int main() { // Set the random seed based on the time @@ -27,11 +29,13 @@ int main() sf2d_init(); sf2d_set_clear_color(RGBA8(0x40, 0x40, 0x40, 0xFF)); + sf2d_set_3D(1); sf2d_texture *tex1 = sf2d_create_texture_mem_RGBA8(dice_img.pixel_data, dice_img.width, dice_img.height, TEXFMT_RGBA8, SF2D_PLACE_RAM); sf2d_texture *tex2 = sf2d_create_texture_mem_RGBA8(citra_img.pixel_data, citra_img.width, citra_img.height, TEXFMT_RGBA8, SF2D_PLACE_RAM); + float offset3d = 0.0f; float rad = 0.0f; u16 touch_x = 320/2; u16 touch_y = 240/2; @@ -55,7 +59,23 @@ int main() sf2d_set_clear_color(RGBA8(rand()%255, rand()%255, rand()%255, 255)); } + offset3d = CONFIG_3D_SLIDERSTATE * 30.0f; + sf2d_start_frame(GFX_TOP, GFX_LEFT); + sf2d_draw_fill_circle(offset3d + 60, 100, 35, RGBA8(0x00, 0xFF, 0x00, 0xFF)); + sf2d_draw_fill_circle(offset3d + 180, 120, 55, RGBA8(0xFF, 0xFF, 0x00, 0xFF)); + + sf2d_draw_rectangle_rotate(offset3d + 260, 20, 40, 40, RGBA8(0xFF, 0xFF, 0x00, 0xFF), -2.0f*rad); + sf2d_draw_rectangle(offset3d + 20, 60, 40, 40, RGBA8(0xFF, 0x00, 0x00, 0xFF)); + sf2d_draw_rectangle(offset3d + 5, 5, 30, 30, RGBA8(0x00, 0xFF, 0xFF, 0xFF)); + sf2d_draw_texture_rotate(tex1, offset3d + 400/2 + circle.dx, 240/2 - circle.dy, rad); + sf2d_end_frame(); + + sf2d_start_frame(GFX_TOP, GFX_RIGHT); + + sf2d_draw_fill_circle(60, 100, 35, RGBA8(0x00, 0xFF, 0x00, 0xFF)); + sf2d_draw_fill_circle(180, 120, 55, RGBA8(0xFF, 0xFF, 0x00, 0xFF)); + sf2d_draw_rectangle_rotate(260, 20, 40, 40, RGBA8(0xFF, 0xFF, 0x00, 0xFF), -2.0f*rad); sf2d_draw_rectangle(20, 60, 40, 40, RGBA8(0xFF, 0x00, 0x00, 0xFF)); sf2d_draw_rectangle(5, 5, 30, 30, RGBA8(0x00, 0xFF, 0xFF, 0xFF)); diff --git a/libs/sftdlib/libsftd/source/sftd.c b/libs/sftdlib/libsftd/source/sftd.c index f8f2b4f..d0adf24 100644 --- a/libs/sftdlib/libsftd/source/sftd.c +++ b/libs/sftdlib/libsftd/source/sftd.c @@ -13,7 +13,6 @@ static int sftd_initialized = 0; static FT_Library ftlibrary; -static FTC_Manager ftcmanager; typedef enum { SFTD_LOAD_FROM_FILE, @@ -617,7 +616,7 @@ int sftd_width_wtext(sftd_font *font, unsigned int size, const wchar_t *text) { FTC_FaceID face_id = (FTC_FaceID)font; FT_Face face; - FTC_Manager_LookupFace(ftcmanager, face_id, &face); + FTC_Manager_LookupFace(font->ftcmanager, face_id, &face); FT_Int charmap_index; charmap_index = FT_Get_Charmap_Index(face->charmap); diff --git a/libs/sftdlib/libsftd/source/texture_atlas.c b/libs/sftdlib/libsftd/source/texture_atlas.c index c877c59..58f433d 100644 --- a/libs/sftdlib/libsftd/source/texture_atlas.c +++ b/libs/sftdlib/libsftd/source/texture_atlas.c @@ -58,7 +58,8 @@ int texture_atlas_insert(texture_atlas *atlas, unsigned int character, const voi int i, j; for (i = 0; i < height; i++) { for (j = 0; j < width; j++) { - sf2d_set_pixel(atlas->tex, pos.x + j, pos.y + i, *(unsigned int *)(image + (j + i*width)*4)); + sf2d_set_pixel(atlas->tex, pos.x + j, pos.y + i, + __builtin_bswap32(*(unsigned int *)(image + (j + i*width)*4))); } } diff --git a/sdcard/3ds/ctruLua/examples/example.lua b/sdcard/3ds/ctruLua/examples/example.lua index 13298ce..9f5fc28 100644 --- a/sdcard/3ds/ctruLua/examples/example.lua +++ b/sdcard/3ds/ctruLua/examples/example.lua @@ -21,7 +21,7 @@ local function drawStuffIn3D(eye) -- 3D stuff local function d(depth) return math.ceil(eye * depth * dMul) end - gfx.color.setDefault(0x00FFFFFF) + gfx.color.setDefault(0xFFFFFF00) gfx.rectangle(240 + d(10), 150, 120, 10) gfx.point(10 + d(6), 20, 0xFF0000FF) @@ -70,7 +70,7 @@ while ctr.run() do local cx, cy = hid.circle() gfx.rectangle(40, 90, 60, 60, 0, 0xDDDDDDFF) - gfx.circle(70 + math.ceil(cx/156 * 30), 120 - math.ceil(cy/156 * 30), 10, 0x000000FF) + gfx.circle(70 + math.ceil(cx/156 * 30), 120 - math.ceil(cy/156 * 30), 10, 0xFF000000) gfx.endFrame() diff --git a/sdcard/3ds/ctruLua/libs/keyboard.lua b/sdcard/3ds/ctruLua/libs/keyboard.lua index e97b9c5..d77b611 100644 --- a/sdcard/3ds/ctruLua/libs/keyboard.lua +++ b/sdcard/3ds/ctruLua/libs/keyboard.lua @@ -69,7 +69,7 @@ return { if xTouch then if xTouch > xKey and xTouch < xKey + keyWidth then if yTouch > yKey and yTouch < yKey + keyHeight then - gfx.rectangle(xKey, yKey, keyWidth, keyHeight, 0, 0xFFFFFFDD) + gfx.rectangle(xKey, yKey, keyWidth, keyHeight, 0, 0xDDFFFFFF) local k = alias[key] or key if sticky[k] and layout[sticky[k]] then @@ -103,4 +103,4 @@ return { return ret ~= "" and ret or nil end -} \ No newline at end of file +} diff --git a/sdcard/3ds/ctruLua/libs/openfile.lua b/sdcard/3ds/ctruLua/libs/openfile.lua index 2f67bae..c08b842 100644 --- a/sdcard/3ds/ctruLua/libs/openfile.lua +++ b/sdcard/3ds/ctruLua/libs/openfile.lua @@ -33,7 +33,7 @@ return function(title, curdir, exts, type) local wasFont = gfx.font.getDefault() gfx.set3D(false) gfx.color.setDefault(0xFFFFFFFF) - gfx.color.setBackground(0x000000FF) + gfx.color.setBackground(0xFF000000) gfx.font.setDefault() while ctr.run() do diff --git a/source/color.c b/source/color.c index a044b99..41d809e 100644 --- a/source/color.c +++ b/source/color.c @@ -76,12 +76,32 @@ static int color_RGBA8(lua_State *L) { return 1; } +/*** +Return a color from a hexadecimal value. +@function hex +@tparam integer hex the hexadecimal color code: 0xRRGGBBAA +@treturn integer the color +*/ +static int color_hex(lua_State *L) { + u32 hex = luaL_checkinteger(L, 1); + + u8 r = (hex & 0xFF000000) >> 24; + u8 g = (hex & 0x00FF0000) >> 16; + u8 b = (hex & 0x0000FF00) >> 8; + u8 a = (hex & 0x000000FF); + + lua_pushinteger(L, RGBA8(r, g, b, a)); + + return 1; +} + static const struct luaL_Reg color_lib[] = { { "setDefault", color_setDefault }, { "getDefault", color_getDefault }, { "setBackground", color_setBackground }, { "getBackground", color_getBackground }, { "RGBA8", color_RGBA8 }, + { "hex", color_hex }, { NULL, NULL } }; @@ -92,4 +112,4 @@ int luaopen_color_lib(lua_State *L) { void load_color_lib(lua_State *L) { luaL_requiref(L, "ctr.gfx.color", luaopen_color_lib, false); -} \ No newline at end of file +} diff --git a/source/ptm.c b/source/ptm.c index 6182635..79099bd 100644 --- a/source/ptm.c +++ b/source/ptm.c @@ -17,6 +17,7 @@ Initialize the PTM module. */ static int ptm_init(lua_State *L) { ptmInit(); + ptmSysmInit(); return 0; } @@ -27,12 +28,13 @@ Disable the PTM module. */ static int ptm_shutdown(lua_State *L) { ptmExit(); + ptmSysmExit(); return 0; } /*** -Return the shell state. Don't care about this. +Return the shell state. @function getShellState @treturn number shell state */ @@ -101,6 +103,30 @@ static int ptm_getTotalStepCount(lua_State *L) { return 1; } +/*** +Setup the new 3DS CPU features (overclock, 4 cores ...) +@newonly +@function configureNew3DSCPU +@tparam boolean enable enable the New3DS CPU features +@treturn boolean `true` if everything went fine +*/ +static int ptm_configureNew3DSCPU(lua_State *L) { + u8 conf = false; + if (lua_isboolean(L, 1)) + conf = lua_toboolean(L, 1); + + Result ret = PTMSYSM_ConfigureNew3DSCPU(conf); + + if (ret) { + lua_pushboolean(L, false); + lua_pushinteger(L, ret); + return 2; + } + + lua_pushboolean(L, true); + return 1; +} + static const struct luaL_Reg ptm_lib[] = { {"init", ptm_init }, {"shutdown", ptm_shutdown }, @@ -109,6 +135,7 @@ static const struct luaL_Reg ptm_lib[] = { {"getBatteryChargeState", ptm_getBatteryChargeState}, {"getPedometerState", ptm_getPedometerState }, {"getTotalStepCount", ptm_getTotalStepCount }, + {"configureNew3DSCPU", ptm_configureNew3DSCPU }, {NULL, NULL} }; diff --git a/source/texture.c b/source/texture.c index a369737..651d242 100644 --- a/source/texture.c +++ b/source/texture.c @@ -35,13 +35,13 @@ int getType(const char *name) { Load a texture from a file. Supported formats: PNG, JPEG, BMP. @function load @tparam string path path to the image file -@tparam[opt=PLACE_RAM] number place where to put the loaded texture +@tparam[opt=PLACE_VRAM] number place where to put the loaded texture @tparam[opt=auto] number type type of the image @treturn texture the loaded texture object */ static int texture_load(lua_State *L) { const char *path = luaL_checkstring(L, 1); - u8 place = luaL_optinteger(L, 2, SF2D_PLACE_RAM); //place in ram by default + u8 place = luaL_optinteger(L, 2, SF2D_PLACE_VRAM); //place in vram by default u8 type = luaL_optinteger(L, 3, 3); //type 3 is "search at the end of the filename" texture_userdata *texture; From 2e782ed9eacec2316705de88be119d036d0abf4f Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Mon, 14 Dec 2015 23:11:45 +0100 Subject: [PATCH 041/101] Updated to the latest ctrulib, Fixed some minor bugs. Working with citra, untested on real hardware but should be OK. IR should now work. Let's add some 3D drawing and sound now ! --- libs/sf2dlib/libsf2d/Makefile | 4 +++ libs/sf2dlib/libsf2d/source/sf2d.c | 26 +++++++-------- libs/sf2dlib/libsf2d/source/sf2d_draw.c | 8 ++--- libs/sf2dlib/libsf2d/source/sf2d_texture.c | 24 +++++++------- libs/sftdlib/libsftd/source/texture_atlas.c | 2 +- source/cam.c | 4 --- source/cfgu.c | 4 +-- source/fs.c | 36 ++++++++++----------- source/gfx.c | 2 +- source/hid.c | 4 +-- source/ir.c | 31 ++++++++---------- source/news.c | 2 +- source/ptm.c | 19 ++++++----- source/qtm.c | 4 +-- source/socket.c | 6 ++-- 15 files changed, 85 insertions(+), 91 deletions(-) diff --git a/libs/sf2dlib/libsf2d/Makefile b/libs/sf2dlib/libsf2d/Makefile index a035946..14e1112 100644 --- a/libs/sf2dlib/libsf2d/Makefile +++ b/libs/sf2dlib/libsf2d/Makefile @@ -30,6 +30,10 @@ CFLAGS := -g -Wall -O2\ $(ARCH) CFLAGS += $(INCLUDE) -DARM11 -D_3DS + +#WILL HAVE TO BE REMOVED SOON +CFLAGS += -DLIBCTRU_NO_DEPRECATION + CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions ASFLAGS := -g $(ARCH) diff --git a/libs/sf2dlib/libsf2d/source/sf2d.c b/libs/sf2dlib/libsf2d/source/sf2d.c index 52e5309..be9e4aa 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d.c +++ b/libs/sf2dlib/libsf2d/source/sf2d.c @@ -35,7 +35,7 @@ static float ortho_matrix_bot[4*4]; //Apt hook cookie static aptHookCookie apt_hook_cookie; //Functions -static void apt_hook_func(int hook, void* param); +static void apt_hook_func(APT_HookType hook, void *param); static void reset_gpu_apt_resume(); int sf2d_init() @@ -87,7 +87,7 @@ int sf2d_init_advanced(int gpucmd_size, int temppool_size) cur_side = GFX_LEFT; GPUCMD_Finalize(); - GPUCMD_FlushAndRun(NULL); + GPUCMD_FlushAndRun(); gspWaitForP3D(); sf2d_pool_reset(); @@ -144,8 +144,8 @@ void sf2d_start_frame(gfxScreen_t screen, gfx3dSide_t side) } else { screen_w = 320; } - GPU_SetViewport((u32 *)osConvertVirtToPhys((u32)gpu_depth_fb_addr), - (u32 *)osConvertVirtToPhys((u32)gpu_fb_addr), + GPU_SetViewport((u32 *)osConvertVirtToPhys(gpu_depth_fb_addr), + (u32 *)osConvertVirtToPhys(gpu_fb_addr), 0, 0, 240, screen_w); GPU_DepthMap(-1.0f, 0.0f); @@ -154,8 +154,8 @@ void sf2d_start_frame(gfxScreen_t screen, gfx3dSide_t side) GPU_SetStencilOp(GPU_STENCIL_KEEP, GPU_STENCIL_KEEP, GPU_STENCIL_KEEP); GPU_SetBlendingColor(0,0,0,0); GPU_SetDepthTestAndWriteMask(true, GPU_GEQUAL, GPU_WRITE_ALL); - GPUCMD_AddMaskedWrite(GPUREG_0062, 0x1, 0); - GPUCMD_AddWrite(GPUREG_0118, 0); + GPUCMD_AddMaskedWrite(GPUREG_EARLYDEPTH_TEST1, 0x1, 0); + GPUCMD_AddWrite(GPUREG_EARLYDEPTH_TEST2, 0); GPU_SetAlphaBlending( GPU_BLEND_ADD, @@ -177,23 +177,23 @@ void sf2d_end_frame() { GPU_FinishDrawing(); GPUCMD_Finalize(); - GPUCMD_FlushAndRun(NULL); + GPUCMD_FlushAndRun(); gspWaitForP3D(); //Copy the GPU rendered FB to the screen FB if (cur_screen == GFX_TOP) { - GX_SetDisplayTransfer(NULL, gpu_fb_addr, GX_BUFFER_DIM(240, 400), + GX_DisplayTransfer(gpu_fb_addr, GX_BUFFER_DIM(240, 400), (u32 *)gfxGetFramebuffer(GFX_TOP, cur_side, NULL, NULL), GX_BUFFER_DIM(240, 400), 0x1000); } else { - GX_SetDisplayTransfer(NULL, gpu_fb_addr, GX_BUFFER_DIM(240, 320), + GX_DisplayTransfer(gpu_fb_addr, GX_BUFFER_DIM(240, 320), (u32 *)gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL), GX_BUFFER_DIM(240, 320), 0x1000); } gspWaitForPPF(); //Clear the screen - GX_SetMemoryFill(NULL, + GX_MemoryFill( gpu_fb_addr, clear_color, &gpu_fb_addr[240*400], GX_FILL_TRIGGER | GX_FILL_32BIT_DEPTH, gpu_depth_fb_addr, 0, &gpu_depth_fb_addr[240*400], GX_FILL_TRIGGER | GX_FILL_32BIT_DEPTH); gspWaitForPSC0(); @@ -203,7 +203,7 @@ void sf2d_swapbuffers() { gfxSwapBuffersGpu(); if (vblank_wait) { - gspWaitForEvent(GSPEVENT_VBlank0, false); + gspWaitForEvent(GSPGPU_EVENT_VBlank0, false); } //Calculate FPS frames++; @@ -289,7 +289,7 @@ gfx3dSide_t sf2d_get_current_side() return cur_side; } -static void apt_hook_func(int hook, void* param) +static void apt_hook_func(APT_HookType hook, void *param) { if (hook == APTHOOK_ONRESTORE) { reset_gpu_apt_resume(); @@ -308,6 +308,6 @@ static void reset_gpu_apt_resume() } GPUCMD_Finalize(); - GPUCMD_FlushAndRun(NULL); + GPUCMD_FlushAndRun(); gspWaitForP3D(); } diff --git a/libs/sf2dlib/libsf2d/source/sf2d_draw.c b/libs/sf2dlib/libsf2d/source/sf2d_draw.c index d84966b..28ef34b 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d_draw.c +++ b/libs/sf2dlib/libsf2d/source/sf2d_draw.c @@ -29,7 +29,7 @@ void sf2d_draw_line(int x0, int y0, int x1, int y1, u32 color) GPU_SetAttributeBuffers( 2, // number of attributes - (u32*)osConvertVirtToPhys((u32)vertices), + (u32*)osConvertVirtToPhys(vertices), GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE), 0xFFFC, //0b1100 0x10, @@ -69,7 +69,7 @@ void sf2d_draw_rectangle(int x, int y, int w, int h, u32 color) GPU_SetAttributeBuffers( 2, // number of attributes - (u32*)osConvertVirtToPhys((u32)vertices), + (u32*)osConvertVirtToPhys(vertices), GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE), 0xFFFC, //0b1100 0x10, @@ -122,7 +122,7 @@ void sf2d_draw_rectangle_rotate(int x, int y, int w, int h, u32 color, float rad GPU_SetAttributeBuffers( 2, // number of attributes - (u32*)osConvertVirtToPhys((u32)vertices), + (u32*)osConvertVirtToPhys(vertices), GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE), 0xFFFC, //0b1100 0x10, @@ -177,7 +177,7 @@ void sf2d_draw_fill_circle(int x, int y, int radius, u32 color) GPU_SetAttributeBuffers( 2, // number of attributes - (u32*)osConvertVirtToPhys((u32)vertices), + (u32*)osConvertVirtToPhys(vertices), GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE), 0xFFFC, //0b1100 0x10, diff --git a/libs/sf2dlib/libsf2d/source/sf2d_texture.c b/libs/sf2dlib/libsf2d/source/sf2d_texture.c index 9918953..df314a9 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d_texture.c +++ b/libs/sf2dlib/libsf2d/source/sf2d_texture.c @@ -83,7 +83,7 @@ sf2d_texture *sf2d_create_texture(int width, int height, sf2d_texfmt pixel_forma texture->data = data; if (place == SF2D_PLACE_VRAM) { - GX_SetMemoryFill(NULL, texture->data, 0x00000000, (u32*)&((u8*)texture->data)[texture->data_size], GX_FILL_TRIGGER | GX_FILL_32BIT_DEPTH, + GX_MemoryFill(texture->data, 0x00000000, (u32*)&((u8*)texture->data)[texture->data_size], GX_FILL_TRIGGER | GX_FILL_32BIT_DEPTH, NULL, 0x00000000, NULL, 0); gspWaitForPSC0(); } else { @@ -146,7 +146,7 @@ void sf2d_bind_texture(const sf2d_texture *texture, GPU_TEXUNIT unit) GPU_SetTexture( unit, - (u32 *)osConvertVirtToPhys((u32)texture->data), + (u32 *)osConvertVirtToPhys(texture->data), texture->pow2_w, texture->pow2_h, texture->params, @@ -170,7 +170,7 @@ void sf2d_bind_texture_color(const sf2d_texture *texture, GPU_TEXUNIT unit, u32 GPU_SetTexture( unit, - (u32 *)osConvertVirtToPhys((u32)texture->data), + (u32 *)osConvertVirtToPhys(texture->data), texture->pow2_w, texture->pow2_h, texture->params, @@ -194,7 +194,7 @@ void sf2d_bind_texture_parameters(const sf2d_texture *texture, GPU_TEXUNIT unit, GPU_SetTexture( unit, - (u32 *)osConvertVirtToPhys((u32)texture->data), + (u32 *)osConvertVirtToPhys(texture->data), texture->pow2_w, texture->pow2_h, params, @@ -235,7 +235,7 @@ static inline void sf2d_draw_texture_generic(const sf2d_texture *texture, int x, GPU_SetAttributeBuffers( 2, // number of attributes - (u32*)osConvertVirtToPhys((u32)vertices), + (u32*)osConvertVirtToPhys(vertices), GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 2, GPU_FLOAT), 0xFFFC, //0b1100 0x10, @@ -304,7 +304,7 @@ static inline void sf2d_draw_texture_rotate_hotspot_generic(const sf2d_texture * GPU_SetAttributeBuffers( 2, // number of attributes - (u32*)osConvertVirtToPhys((u32)vertices), + (u32*)osConvertVirtToPhys(vertices), GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 2, GPU_FLOAT), 0xFFFC, //0b1100 0x10, @@ -366,7 +366,7 @@ static inline void sf2d_draw_texture_part_generic(const sf2d_texture *texture, i GPU_SetAttributeBuffers( 2, // number of attributes - (u32*)osConvertVirtToPhys((u32)vertices), + (u32*)osConvertVirtToPhys(vertices), GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 2, GPU_FLOAT), 0xFFFC, //0b1100 0x10, @@ -414,7 +414,7 @@ static inline void sf2d_draw_texture_scale_generic(const sf2d_texture *texture, GPU_SetAttributeBuffers( 2, // number of attributes - (u32*)osConvertVirtToPhys((u32)vertices), + (u32*)osConvertVirtToPhys(vertices), GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 2, GPU_FLOAT), 0xFFFC, //0b1100 0x10, @@ -464,7 +464,7 @@ static inline void sf2d_draw_texture_part_scale_generic(const sf2d_texture *text GPU_SetAttributeBuffers( 2, // number of attributes - (u32*)osConvertVirtToPhys((u32)vertices), + (u32*)osConvertVirtToPhys(vertices), GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 2, GPU_FLOAT), 0xFFFC, //0b1100 0x10, @@ -524,7 +524,7 @@ static inline void sf2d_draw_texture_part_rotate_scale_generic(const sf2d_textur GPU_SetAttributeBuffers( 2, // number of attributes - (u32*)osConvertVirtToPhys((u32)vertices), + (u32*)osConvertVirtToPhys(vertices), GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 2, GPU_FLOAT), 0xFFFC, //0b1100 0x10, @@ -573,7 +573,7 @@ static inline void sf2d_draw_texture_depth_generic(const sf2d_texture *texture, GPU_SetAttributeBuffers( 2, // number of attributes - (u32*)osConvertVirtToPhys((u32)vertices), + (u32*)osConvertVirtToPhys(vertices), GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 2, GPU_FLOAT), 0xFFFC, //0b1100 0x10, @@ -618,7 +618,7 @@ void sf2d_draw_quad_uv(const sf2d_texture *texture, float left, float top, float GPU_SetAttributeBuffers( 2, // number of attributes - (u32*)osConvertVirtToPhys((u32)vertices), + (u32*)osConvertVirtToPhys(vertices), GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 2, GPU_FLOAT), 0xFFFC, //0b1100 0x10, diff --git a/libs/sftdlib/libsftd/source/texture_atlas.c b/libs/sftdlib/libsftd/source/texture_atlas.c index 58f433d..03320c5 100644 --- a/libs/sftdlib/libsftd/source/texture_atlas.c +++ b/libs/sftdlib/libsftd/source/texture_atlas.c @@ -63,7 +63,7 @@ int texture_atlas_insert(texture_atlas *atlas, unsigned int character, const voi } } - GSPGPU_FlushDataCache(NULL, atlas->tex->data, atlas->tex->data_size); + GSPGPU_FlushDataCache(atlas->tex->data, atlas->tex->data_size); return 1; } diff --git a/source/cam.c b/source/cam.c index afa9009..961d26f 100644 --- a/source/cam.c +++ b/source/cam.c @@ -585,10 +585,6 @@ struct { char *name; int value; } cam_constants[] = { */ {"WHITE_BALANCE_7000K", WHITE_BALANCE_7000K}, /*** - @field WHITE_BALANCE_MAX - */ - {"WHITE_BALANCE_MAX", WHITE_BALANCE_MAX }, - /*** @field WHITE_BALANCE_TUNGSTEN */ {"WHITE_BALANCE_TUNGSTEN", WHITE_BALANCE_TUNGSTEN }, diff --git a/source/cfgu.c b/source/cfgu.c index 57829d0..4c4fb74 100644 --- a/source/cfgu.c +++ b/source/cfgu.c @@ -19,7 +19,7 @@ Initialize the CFGU module. @function init */ static int cfgu_init(lua_State *L) { - initCfgu(); + cfguInit(); return 0; } @@ -29,7 +29,7 @@ Disable the CFGU module. @function shutdown */ static int cfgu_shutdown(lua_State *L) { - exitCfgu(); + cfguExit(); return 0; } diff --git a/source/fs.c b/source/fs.c index 684570b..6d367cb 100644 --- a/source/fs.c +++ b/source/fs.c @@ -15,9 +15,9 @@ The `fs` module. #include Handle *fsuHandle; -FS_archive sdmcArchive; +FS_Archive sdmcArchive; #ifdef ROMFS -FS_archive romfsArchive; +FS_Archive romfsArchive; #endif /*** @@ -73,9 +73,9 @@ static int fs_list(lua_State *L) { // Get default archive #ifdef ROMFS - FS_archive archive = romfsArchive; + FS_Archive archive = romfsArchive; #else - FS_archive archive = sdmcArchive; + FS_Archive archive = sdmcArchive; #endif // Archive path override (and skip path prefix) if (strncmp(path, "sdmc:", 5) == 0) { @@ -88,14 +88,14 @@ static int fs_list(lua_State *L) { #endif } - FS_path dirPath = FS_makePath(PATH_CHAR, path); + FS_Path dirPath = fsMakePath(PATH_ASCII, path); Handle dirHandle; - FSUSER_OpenDirectory(fsuHandle, &dirHandle, archive, dirPath); + FSUSER_OpenDirectory(&dirHandle, archive, dirPath); u32 entriesRead = 0; do { - FS_dirent buffer; + FS_DirectoryEntry buffer; FSDIR_Read(dirHandle, &entriesRead, 1, &buffer); @@ -113,13 +113,13 @@ static int fs_list(lua_State *L) { lua_setfield(L, -2, "shortName"); lua_pushstring(L, (const char *)buffer.shortExt); lua_setfield(L, -2, "shortExt"); - lua_pushboolean(L, buffer.isDirectory); + lua_pushboolean(L, buffer.attributes&FS_ATTRIBUTE_DIRECTORY); lua_setfield(L, -2, "isDirectory"); - lua_pushboolean(L, buffer.isHidden); + lua_pushboolean(L, buffer.attributes&FS_ATTRIBUTE_HIDDEN); lua_setfield(L, -2, "isHidden"); - lua_pushboolean(L, buffer.isArchive); + lua_pushboolean(L, buffer.attributes&FS_ATTRIBUTE_ARCHIVE); lua_setfield(L, -2, "isArchive"); - lua_pushboolean(L, buffer.isReadOnly); + lua_pushboolean(L, buffer.attributes&FS_ATTRIBUTE_READ_ONLY); lua_setfield(L, -2, "isReadOnly"); lua_pushinteger(L, buffer.fileSize); lua_setfield(L, -2, "fileSize"); @@ -214,22 +214,22 @@ void load_fs_lib(lua_State *L) { fsInit(); fsuHandle = fsGetSessionHandle(); - FSUSER_Initialize(fsuHandle); + FSUSER_Initialize(*fsuHandle); - sdmcArchive = (FS_archive){ARCH_SDMC, FS_makePath(PATH_EMPTY, "")}; - FSUSER_OpenArchive(fsuHandle, &sdmcArchive); + sdmcArchive = (FS_Archive){ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")}; + FSUSER_OpenArchive(&sdmcArchive); #ifdef ROMFS - romfsArchive = (FS_archive){ARCH_ROMFS, FS_makePath(PATH_EMPTY, "")}; - FSUSER_OpenArchive(fsuHandle, &romfsArchive); + romfsArchive = (FS_Archive){ARCHIVE_ROMFS, fsMakePath(PATH_EMPTY, "")}; + FSUSER_OpenArchive(&romfsArchive); #endif luaL_requiref(L, "ctr.fs", luaopen_fs_lib, false); } void unload_fs_lib(lua_State *L) { - FSUSER_CloseArchive(fsuHandle, &sdmcArchive); + FSUSER_CloseArchive(&sdmcArchive); #ifdef ROMFS - FSUSER_CloseArchive(fsuHandle, &romfsArchive); + FSUSER_CloseArchive(&romfsArchive); #endif fsExit(); diff --git a/source/gfx.c b/source/gfx.c index b984027..aa64956 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -9,7 +9,7 @@ The `gfx` module. #include #include <3ds/vram.h> -#include <3ds/services/gsp.h> +//#include <3ds/services/gsp.h> #include #include diff --git a/source/hid.c b/source/hid.c index 6d54b20..141f49d 100644 --- a/source/hid.c +++ b/source/hid.c @@ -51,7 +51,7 @@ Keys states */ // Key list based on hid.h from the ctrulib by smealum -struct { PAD_KEY key; char *name; } hid_keys_name[] = { +struct { u32 key; char *name; } hid_keys_name[] = { { KEY_A , "a" }, { KEY_B , "b" }, { KEY_SELECT , "select" }, @@ -117,7 +117,7 @@ static int hid_keys(lua_State *L) { lua_newtable(L); // up table for (int i = 0; hid_keys_name[i].key; i++) { - PAD_KEY key = hid_keys_name[i].key; + u32 key = hid_keys_name[i].key; char *name = hid_keys_name[i].name; if (kDown & key) { diff --git a/source/ir.c b/source/ir.c index 705091b..5c5864e 100644 --- a/source/ir.c +++ b/source/ir.c @@ -10,8 +10,7 @@ The `ir` module. #include #include -u32 bufferSize = 0; -u32 *buffer; +#include /*** Bitrate codes list (this is not a part of the module, just a reference) @@ -38,19 +37,11 @@ Bitrate codes list (this is not a part of the module, just a reference) Initialize the IR module. @function init @tparam[opt=6] number bitrate bitrate of the IR module (more informations below) -@tparam[opt=2048] number buffer size of the buffer, in bytes (max 2048) */ static int ir_init(lua_State *L) { u8 bitrate = luaL_optinteger(L, 1, 6); - bufferSize = luaL_optinteger(L, 2, 2048); //default: 2Kio - if (bufferSize > 2048) { - lua_pushboolean(L, false); - lua_pushstring(L, "the buffer can't be larger than 2048 bytes."); - return 2; - } - buffer = linearAlloc(bufferSize); - Result ret = IRU_Initialize(buffer, bufferSize); + Result ret = IRU_Initialize(); if (ret) { lua_pushboolean(L, false); lua_pushinteger(L, ret); @@ -88,37 +79,41 @@ static int ir_send(lua_State *L) { u8 *data = (u8*)luaL_checkstring(L, 1); u32 wait = lua_toboolean(L, 2); - Result ret = IRU_SendData(data, sizeof(data), wait); + Result ret = IRU_StartSendTransfer(data, strlen((const char*)data)); + if (wait) + IRU_WaitSendTransfer(); + if (ret) { lua_pushboolean(L, false); lua_pushinteger(L, ret); return 2; } - return 0; + lua_pushboolean(L, true); + return 1; } /*** Receive some data from the IR module. @function receive -@tparam[opt=buffer size] number size bytes to receive +@tparam number size bytes to receive @tparam[opt=false] boolean wait wait until the data is received @return string data */ static int ir_receive(lua_State *L) { - u32 size = luaL_optinteger(L, 1, bufferSize); + u32 size = luaL_checkinteger(L, 1); u32 wait = lua_toboolean(L, 2); u8 *data = 0; - u32 *transfercount = 0; + u32 transfercount = 0; - Result ret = IRU_RecvData(data, size, 0x00, transfercount, wait); + Result ret = iruRecvData(data, size, 0x00, &transfercount, wait); if (ret) { lua_pushboolean(L, false); lua_pushinteger(L, ret); return 2; } - lua_pushstring(L, (const char *)data); + lua_pushlstring(L, (const char *)data, (size_t)transfercount); return 1; } diff --git a/source/news.c b/source/news.c index 20f78d8..d20b61b 100644 --- a/source/news.c +++ b/source/news.c @@ -47,7 +47,7 @@ static int news_notification(lua_State *L) { titleLength = (u32) utf8_to_utf16((uint16_t*)cTitle, (uint8_t*)title, strlen(title)); messageLength = (u32) utf8_to_utf16((uint16_t*)cMessage, (uint8_t*)message, strlen(message)); - NEWSU_AddNotification(cTitle, titleLength, cMessage, messageLength, imageData, imageDataLength, jpeg); + NEWS_AddNotification(cTitle, titleLength, cMessage, messageLength, imageData, imageDataLength, jpeg); return 0; } diff --git a/source/ptm.c b/source/ptm.c index 79099bd..8a902e9 100644 --- a/source/ptm.c +++ b/source/ptm.c @@ -4,19 +4,18 @@ The `ptm` module. @usage local ptm = require("ctr.ptm") */ #include <3ds/types.h> -#include <3ds/services/ptm.h> +#include <3ds/services/ptmu.h> +#include <3ds/services/ptmsysm.h> #include #include -static Handle *ptmHandle; - /*** Initialize the PTM module. @function init */ static int ptm_init(lua_State *L) { - ptmInit(); + ptmuInit(); ptmSysmInit(); return 0; @@ -27,7 +26,7 @@ Disable the PTM module. @function shutdown */ static int ptm_shutdown(lua_State *L) { - ptmExit(); + ptmuExit(); ptmSysmExit(); return 0; @@ -40,7 +39,7 @@ Return the shell state. */ static int ptm_getShellState(lua_State *L) { u8 out = 0; - PTMU_GetShellState(ptmHandle, &out); + PTMU_GetShellState(&out); lua_pushinteger(L, out); @@ -54,7 +53,7 @@ Return the battery level. */ static int ptm_getBatteryLevel(lua_State *L) { u8 out = 0; - PTMU_GetBatteryLevel(ptmHandle, &out); + PTMU_GetBatteryLevel(&out); lua_pushinteger(L, out); @@ -68,7 +67,7 @@ Return whether or not the battery is charging. */ static int ptm_getBatteryChargeState(lua_State *L) { u8 out = 0; - PTMU_GetBatteryChargeState(ptmHandle, &out); + PTMU_GetBatteryChargeState(&out); lua_pushboolean(L, out); @@ -82,7 +81,7 @@ Return whether or not the pedometer is counting. */ static int ptm_getPedometerState(lua_State *L) { u8 out = 0; - PTMU_GetPedometerState(ptmHandle, &out); + PTMU_GetPedometerState(&out); lua_pushboolean(L, out); @@ -96,7 +95,7 @@ Return the total steps taken with the system. */ static int ptm_getTotalStepCount(lua_State *L) { u32 steps = 0; - PTMU_GetTotalStepCount(ptmHandle, &steps); + PTMU_GetTotalStepCount(&steps); lua_pushinteger(L, steps); diff --git a/source/qtm.c b/source/qtm.c index 2e31cf3..6ad02e3 100644 --- a/source/qtm.c +++ b/source/qtm.c @@ -14,7 +14,7 @@ The `qtm` module, for headtracking. New3ds only. #include typedef struct { - qtmHeadtrackingInfo *info; + QTM_HeadTrackingInfo *info; } qtm_userdata; static const struct luaL_Reg qtm_methods[]; @@ -66,7 +66,7 @@ static int qtm_getHeadtrackingInfo(lua_State *L) { qtm_userdata *data = lua_newuserdata(L, sizeof(*data)); luaL_getmetatable(L, "LQTM"); lua_setmetatable(L, -2); - Result ret = qtmGetHeadtrackingInfo(0, data->info); + Result ret = QTM_GetHeadTrackingInfo(0, data->info); if (ret) { lua_pushnil(L); return 1; diff --git a/source/socket.c b/source/socket.c index 4183284..c4eef76 100644 --- a/source/socket.c +++ b/source/socket.c @@ -25,7 +25,7 @@ The UDP part is only without connection. typedef struct { int socket; struct sockaddr_in addr; - struct hostent *host; // only user for client sockets + struct hostent *host; // only used for client sockets } socket_userdata; /*** @@ -35,7 +35,7 @@ Initialize the socket module */ static int socket_init(lua_State *L) { u32 size = luaL_optinteger(L, 1, 0x100000); - Result ret = SOC_Initialize((u32*)memalign(0x1000, size), size); + Result ret = socInit((u32*)memalign(0x1000, size), size); if (ret) { lua_pushboolean(L, false); @@ -52,7 +52,7 @@ Disable the socket module. Must be called before exiting ctrµLua. @function shutdown */ static int socket_shutdown(lua_State *L) { - SOC_Shutdown(); + socExit(); return 0; } From 716c42b849d017f69726eb83709efed0774066bd Mon Sep 17 00:00:00 2001 From: Reuh Date: Thu, 24 Dec 2015 13:15:00 +0100 Subject: [PATCH 042/101] Fixed keyboard and editor colors --- sdcard/3ds/ctruLua/editor/color.lua | 22 ++++++++++++---------- sdcard/3ds/ctruLua/libs/keyboard.lua | 7 ++++--- source/main.c | 2 +- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/sdcard/3ds/ctruLua/editor/color.lua b/sdcard/3ds/ctruLua/editor/color.lua index e29506a..5f6ef58 100644 --- a/sdcard/3ds/ctruLua/editor/color.lua +++ b/sdcard/3ds/ctruLua/editor/color.lua @@ -1,16 +1,18 @@ +local hex = require("ctr.gfx.color").hex + -- Colors based on the Monokai theme return { -- General - ["background"] = 0x272822FF, - ["cursor"] = 0xF8F8F0FF, - ["default"] = 0xF8F8F2FF, + ["background"] = hex(0x272822FF), + ["cursor"] = hex(0xF8F8F0FF), + ["default"] = hex(0xF8F8F2FF), -- Syntax - ["comment"] = 0x75715EFF, - ["string"] = 0xE6DB74FF, - ["constant.numeric"] = 0xAE81FFFF, - ["constant.language"] = 0xAE81FFFF, - ["keyword.control"] = 0xF92672FF, - ["keyword.operator"] = 0xF92672FF, - ["support.function"] = 0x66D9EFFF + ["comment"] = hex(0x75715EFF), + ["string"] = hex(0xE6DB74FF), + ["constant.numeric"] = hex(0xAE81FFFF), + ["constant.language"] = hex(0xAE81FFFF), + ["keyword.control"] = hex(0xF92672FF), + ["keyword.operator"] = hex(0xF92672FF), + ["support.function"] = hex(0x66D9EFFF) } \ No newline at end of file diff --git a/sdcard/3ds/ctruLua/libs/keyboard.lua b/sdcard/3ds/ctruLua/libs/keyboard.lua index d77b611..af34714 100644 --- a/sdcard/3ds/ctruLua/libs/keyboard.lua +++ b/sdcard/3ds/ctruLua/libs/keyboard.lua @@ -1,5 +1,6 @@ local hid = require("ctr.hid") local gfx = require("ctr.gfx") +local hex = gfx.color.hex -- Options local keyWidth, keyHeight = 25, 25 @@ -62,14 +63,14 @@ return { for column, key in pairs(rowKeys) do local xKey, yKey = x + (column-1)*(keyWidth-1), y + (row-1)*(keyHeight-1) - gfx.rectangle(xKey, yKey, keyWidth, keyHeight, 0, 0xFFFFFFFF) - gfx.rectangle(xKey + 1, yKey + 1, keyWidth - 2, keyHeight - 2, 0, 0x000000FF) + gfx.rectangle(xKey, yKey, keyWidth, keyHeight, 0, hex(0xFFFFFFFF)) + gfx.rectangle(xKey + 1, yKey + 1, keyWidth - 2, keyHeight - 2, 0, hex(0x000000FF)) gfx.text(xKey + 2, yKey + 2, key) if xTouch then if xTouch > xKey and xTouch < xKey + keyWidth then if yTouch > yKey and yTouch < yKey + keyHeight then - gfx.rectangle(xKey, yKey, keyWidth, keyHeight, 0, 0xDDFFFFFF) + gfx.rectangle(xKey, yKey, keyWidth, keyHeight, 0, hex(0xDDFFFFFF)) local k = alias[key] or key if sticky[k] and layout[sticky[k]] then diff --git a/source/main.c b/source/main.c index 21750ed..c9e7473 100644 --- a/source/main.c +++ b/source/main.c @@ -52,6 +52,6 @@ int main() { // Unload Lua lua_close(L); - + return 0; } From 5c4da69997fe25030fdf49115e13af21506a6d98 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Thu, 24 Dec 2015 18:44:22 +0100 Subject: [PATCH 043/101] Added the ctr.apt library, tweaked ctr.news a bit, fixed a typo in cfgu.c --- source/apt.c | 342 ++++++++++++++++++++++++++++++++++++++++++++++++++ source/cfgu.c | 2 +- source/ctr.c | 10 +- source/news.c | 15 ++- 4 files changed, 362 insertions(+), 7 deletions(-) create mode 100644 source/apt.c diff --git a/source/apt.c b/source/apt.c new file mode 100644 index 0000000..99a1fd1 --- /dev/null +++ b/source/apt.c @@ -0,0 +1,342 @@ +/*** +The `apt` module. +Used to manage the applets and application status. +@module ctr.apt +@usage local apt = require("ctr.apt") +*/ + +#include + +#include <3ds/types.h> +#include <3ds/services/apt.h> + +#include +#include + +/*** +Initialize the APT module. Useless. +@function init +*/ +static int apt_init(lua_State *L) { + Result ret = aptInit(); + if (ret!=0) { + lua_pushboolean(L, false); + lua_pushinteger(L, ret); + return 2; + } + lua_pushboolean(L, true); + return 1; +} + +/*** +Shutdown the APT module. Useless, don't use it. +@function shutdown +*/ +static int apt_shutdown(lua_State *L) { + aptExit(); + return 0; +} + +/*** +Open an APT session. Should only work if you don't use the homebrew menu. +@function openSession +*/ +static int apt_openSession(lua_State *L) { + aptOpenSession(); + return 0; +} + +/*** +Close the current APT session. +@function closeSession +*/ +static int apt_closeSession(lua_State *L) { + aptCloseSession(); + return 0; +} + +/*** +Set the app status. +@function setStatus +*/ +static int apt_setStatus(lua_State *L) { + APT_AppStatus status = luaL_checkinteger(L, 1); + + aptSetStatus(status); + + return 0; +} + +/*** +Get the app status. +@function getStatus +*/ +static int apt_getStatus(lua_State *L) { + APT_AppStatus status = aptGetStatus(); + + lua_pushinteger(L, status); + return 1; +} + +/*** +Return to the Home menu. +@function returnToMenu +*/ +static int apt_returnToMenu(lua_State *L) { + aptReturnToMenu(); + return 0; +} + +/*** +Get the power status. +@function getStatusPower +@treturn boolean true if the power button has been pressed +*/ +static int apt_getStatusPower(lua_State *L) { + u32 status = aptGetStatusPower(); + + lua_pushboolean(L, status); + return 1; +} + +/*** +Set the power status. +@function setStatusPower +@tparam boolean status new power status +*/ +static int apt_setStatusPower(lua_State *L) { + u32 status = lua_toboolean(L, 1); + + aptSetStatusPower(status); + + return 0; +} + +/*** +Signal that the application is ready for sleeping. +@function signalReadyForSleep +*/ +static int apt_signalReadyForSleep(lua_State *L) { + aptSignalReadyForSleep(); + return 0; +} + +/*** +Return the Home menu AppID. +@function getMenuAppID +@treturn number the AppID +*/ +static int apt_getMenuAppID(lua_State *L) { + lua_pushinteger(L, aptGetMenuAppID()); + return 1; +} + +static const struct luaL_Reg apt_lib[] = { + {"init", apt_init }, + {"shutdown", apt_shutdown }, + {"openSession", apt_openSession }, + {"closeSession", apt_closeSession }, + {"setStatus", apt_setStatus }, + {"getStatus", apt_getStatus }, + {"returnToMenu", apt_returnToMenu }, + {"getStatusPower", apt_getStatusPower }, + {"setStatusPower", apt_setStatusPower }, + {"signalReadyForSleep", apt_signalReadyForSleep}, + {"getMenuAppID", apt_getMenuAppID }, + {NULL, NULL} +}; + +struct { char *name; int value; } apt_constants[] = { + /*** + @field APPID_HOMEMENU + */ + {"APPID_HOMEMENU", APPID_HOMEMENU }, + /*** + @field APPID_CAMERA + */ + {"APPID_CAMERA", APPID_CAMERA }, + /*** + @field APPID_FRIENDS_LIST + */ + {"APPID_FRIENDS_LIST", APPID_FRIENDS_LIST }, + /*** + @field APPID_GAME_NOTES + */ + {"APPID_GAME_NOTES", APPID_GAME_NOTES }, + /*** + @field APPID_WEB + */ + {"APPID_WEB", APPID_WEB }, + /*** + @field APPID_INSTRUCTION_MANUAL + */ + {"APPID_INSTRUCTION_MANUAL", APPID_INSTRUCTION_MANUAL}, + /*** + @field APPID_NOTIFICATIONS + */ + {"APPID_NOTIFICATIONS", APPID_NOTIFICATIONS }, + /*** + @field APPID_MIIVERSE + */ + {"APPID_MIIVERSE", APPID_MIIVERSE }, + /*** + @field APPID_MIIVERSE_POSTING + */ + {"APPID_MIIVERSE_POSTING", APPID_MIIVERSE_POSTING }, + /*** + @field APPID_AMIIBO_SETTINGS + */ + {"APPID_AMIIBO_SETTINGS", APPID_AMIIBO_SETTINGS }, + /*** + @field APPID_APPLICATION + */ + {"APPID_APPLICATION", APPID_APPLICATION }, + /*** + @field APPID_ESHOP + */ + {"APPID_ESHOP", APPID_ESHOP }, + /*** + @field APPID_SOFTWARE_KEYBOARD + */ + {"APPID_SOFTWARE_KEYBOARD", APPID_SOFTWARE_KEYBOARD }, + /*** + @field APPID_APPLETED + */ + {"APPID_APPLETED", APPID_APPLETED }, + /*** + @field APPID_PNOTE_AP + */ + {"APPID_PNOTE_AP", APPID_PNOTE_AP }, + /*** + @field APPID_SNOTE_AP + */ + {"APPID_SNOTE_AP", APPID_SNOTE_AP }, + /*** + @field APPID_ERROR + */ + {"APPID_ERROR", APPID_ERROR }, + /*** + @field APPID_MINT + */ + {"APPID_MINT", APPID_MINT }, + /*** + @field APPID_EXTRAPAD + */ + {"APPID_EXTRAPAD", APPID_EXTRAPAD }, + /*** + @field APPID_MEMOLIB + */ + {"APPID_MEMOLIB", APPID_MEMOLIB }, + /*** + @field APP_NOTINITIALIZED + */ + {"APP_NOTINITIALIZED", APP_NOTINITIALIZED }, + /*** + @field APP_RUNNING + */ + {"APP_RUNNING", APP_RUNNING }, + /*** + @field APP_SUSPENDED + */ + {"APP_SUSPENDED", APP_SUSPENDED }, + /*** + @field APP_EXITING + */ + {"APP_EXITING", APP_EXITING }, + /*** + @field APP_SUSPENDING + */ + {"APP_SUSPENDING", APP_SUSPENDING }, + /*** + @field APP_SLEEPMODE + */ + {"APP_SLEEPMODE", APP_SLEEPMODE }, + /*** + @field APP_PREPARE_SLEEPMODE + */ + {"APP_PREPARE_SLEEPMODE", APP_PREPARE_SLEEPMODE}, + /*** + @field APP_APPLETSTARTED + */ + {"APP_APPLETSTARTED", APP_APPLETSTARTED }, + /*** + @field APP_APPLETCLOSED + */ + {"APP_APPLETCLOSED", APP_APPLETCLOSED }, + /*** + @field APTSIGNAL_HOMEBUTTON + */ + {"APTSIGNAL_HOMEBUTTON", APTSIGNAL_HOMEBUTTON }, + /*** + @field APTSIGNAL_PREPARESLEEP + */ + {"APTSIGNAL_PREPARESLEEP", APTSIGNAL_PREPARESLEEP}, + /*** + @field APTSIGNAL_ENTERSLEEP + */ + {"APTSIGNAL_ENTERSLEEP", APTSIGNAL_ENTERSLEEP }, + /*** + @field APTSIGNAL_WAKEUP + */ + {"APTSIGNAL_WAKEUP", APTSIGNAL_WAKEUP }, + /*** + @field APTSIGNAL_ENABLE + */ + {"APTSIGNAL_ENABLE", APTSIGNAL_ENABLE }, + /*** + @field APTSIGNAL_POWERBUTTON + */ + {"APTSIGNAL_POWERBUTTON", APTSIGNAL_POWERBUTTON }, + /*** + @field APTSIGNAL_UTILITY + */ + {"APTSIGNAL_UTILITY", APTSIGNAL_UTILITY }, + /*** + @field APTSIGNAL_SLEEPSYSTEM + */ + {"APTSIGNAL_SLEEPSYSTEM", APTSIGNAL_SLEEPSYSTEM }, + /*** + @field APTSIGNAL_ERROR + */ + {"APTSIGNAL_ERROR", APTSIGNAL_ERROR }, + /*** + @field APTHOOK_ONSUSPEND + */ + {"APTHOOK_ONSUSPEND", APTHOOK_ONSUSPEND}, + /*** + @field APTHOOK_ONRESTORE + */ + {"APTHOOK_ONRESTORE", APTHOOK_ONRESTORE}, + /*** + @field APTHOOK_ONSLEEP + */ + {"APTHOOK_ONSLEEP", APTHOOK_ONSLEEP }, + /*** + @field APTHOOK_ONWAKEUP + */ + {"APTHOOK_ONWAKEUP", APTHOOK_ONWAKEUP }, + /*** + @field APTHOOK_ONEXIT + */ + {"APTHOOK_ONEXIT", APTHOOK_ONEXIT }, + /*** + @field APTHOOK_COUNT + */ + {"APTHOOK_COUNT", APTHOOK_COUNT }, + {NULL, 0} +}; + +int luaopen_apt_lib(lua_State *L) { + luaL_newlib(L, apt_lib); + + for (int i = 0; apt_constants[i].name; i++) { + lua_pushinteger(L, apt_constants[i].value); + lua_setfield(L, -2, apt_constants[i].name); + } + + return 1; +} + +void load_apt_lib(lua_State *L) { + luaL_requiref(L, "ctr.apt", luaopen_apt_lib, false); +} diff --git a/source/cfgu.c b/source/cfgu.c index 4c4fb74..f74abae 100644 --- a/source/cfgu.c +++ b/source/cfgu.c @@ -290,7 +290,7 @@ struct { char *name; int value; } cfgu_constants[] = { It is equal to `4`. @field MODEL_N3DSXL */ - {"MOdEL_N3DSXL", 4}, + {"MODEL_N3DSXL", 4}, {NULL, 0} }; diff --git a/source/ctr.c b/source/ctr.c index da788c0..dea614a 100644 --- a/source/ctr.c +++ b/source/ctr.c @@ -91,6 +91,13 @@ The `ctr.cam` module. */ void load_cam_lib(lua_State *L); +/*** +The `ctr.apt` module. +@table apt +@see ctr.apt +*/ +void load_apt_lib(lua_State *L); + /*** Return whether or not the program should continue. @function run @@ -132,7 +139,8 @@ struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } { "qtm", load_qtm_lib, NULL }, { "cfgu", load_cfgu_lib, NULL }, { "socket", load_socket_lib, NULL }, - { "cam", load_cam_lib, NULL }, + { "cam", load_cam_lib, NULL }, + { "apt", load_apt_lib, NULL }, { NULL, NULL } }; diff --git a/source/news.c b/source/news.c index d20b61b..200e33c 100644 --- a/source/news.c +++ b/source/news.c @@ -26,13 +26,13 @@ static int news_init(lua_State *L) { Send a notification to the user. WIP, do not use !!! @function notification @tparam string title title of the notification -@tparam string message message of the notification -@tparam string imageData data from a JPEG image (content of a file) or raw data -@tparam[OPT=false] boolean jpeg set to `true` if the data is from a JPEG file +@tparam[opt=nil] string message message of the notification, or nil for no message +@tparam[opt=nil] string imageData data from a JPEG image (content of a file) or raw data, or nil for no image +@tparam[opt=false] boolean jpeg set to `true` if the data is from a JPEG file */ static int news_notification(lua_State *L) { const char *title = luaL_checkstring(L, 1); - const char *message = luaL_checkstring(L, 2); + const char *message = luaL_optstring(L, 2, NULL); u32 imageDataLength = 0; const void *imageData = luaL_optlstring(L, 3, NULL, (size_t*)&imageDataLength); @@ -45,7 +45,12 @@ static int news_notification(lua_State *L) { u32 titleLength, messageLength; titleLength = (u32) utf8_to_utf16((uint16_t*)cTitle, (uint8_t*)title, strlen(title)); - messageLength = (u32) utf8_to_utf16((uint16_t*)cMessage, (uint8_t*)message, strlen(message)); + if (message != NULL) { + messageLength = (u32) utf8_to_utf16((uint16_t*)cMessage, (uint8_t*)message, strlen(message)); + } else { + messageLength = 0; + cMessage = NULL; + } NEWS_AddNotification(cTitle, titleLength, cMessage, messageLength, imageData, imageDataLength, jpeg); From fe894820a2c8ed60846a1ce290e4c71907c1472b Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Thu, 24 Dec 2015 23:58:16 +0100 Subject: [PATCH 044/101] The ctr.news library should now be bug-free ! Untested, but checked 3 times with citra, and everything matches the doc. Warning: there is no check of any kind, so don't put weird stuff as arguments. --- source/news.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/news.c b/source/news.c index 200e33c..f97e6d9 100644 --- a/source/news.c +++ b/source/news.c @@ -10,6 +10,7 @@ The `news` module. #include #include +#include #include /*** @@ -23,7 +24,7 @@ static int news_init(lua_State *L) { } /*** -Send a notification to the user. WIP, do not use !!! +Send a notification to the user. Should work now. @function notification @tparam string title title of the notification @tparam[opt=nil] string message message of the notification, or nil for no message @@ -40,8 +41,8 @@ static int news_notification(lua_State *L) { if (lua_isboolean(L, 4)) jpeg = lua_toboolean(L, 4); - const u16* cTitle = 0; - const u16* cMessage = 0; + const u16* cTitle = malloc(strlen(title)*sizeof(u16)); + const u16* cMessage = malloc(strlen(message)*sizeof(u16)); u32 titleLength, messageLength; titleLength = (u32) utf8_to_utf16((uint16_t*)cTitle, (uint8_t*)title, strlen(title)); From bda9de4d1c8f66997939a7bcc19397e6aae451b7 Mon Sep 17 00:00:00 2001 From: Reuh Date: Sun, 27 Dec 2015 19:54:58 +0100 Subject: [PATCH 045/101] ADDED AUDIO! WAV working perfectly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added libogg, libvorbis and libvorbisfile to 3ds_portlibs. You will need to compile thems using make build-portlibs. Opening OGG files works but they play badly. Added a very simple error handler in main.lua Added audio example. Renamed isGfxInitialised to isGfxInitialized. Did you know? The audio module is the longest ctrµLua module. --- Makefile | 4 +- libs/3ds_portlibs/.gitignore | 2 + libs/3ds_portlibs/Makefile | 54 +- sdcard/3ds/ctruLua/main.lua | 18 +- sdcard/3ds/ctruLua/tests/audio/main.lua | 52 ++ sdcard/3ds/ctruLua/tests/audio/test.wav | Bin 0 -> 321974 bytes source/audio.c | 851 ++++++++++++++++++++++++ source/ctr.c | 11 +- source/gfx.c | 4 +- source/main.c | 6 +- 10 files changed, 985 insertions(+), 17 deletions(-) create mode 100644 sdcard/3ds/ctruLua/tests/audio/main.lua create mode 100644 sdcard/3ds/ctruLua/tests/audio/test.wav create mode 100644 source/audio.c diff --git a/Makefile b/Makefile index 0b2d935..6ead142 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 ASFLAGS := -g $(ARCH) LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) -LIBS := -lsfil -ljpeg -lsftd -lfreetype -lpng -lz -lsf2d -lctru -lm +LIBS := -lsfil -ljpeg -lsftd -lfreetype -lpng -lz -lsf2d -lctru -lvorbisfile -lvorbis -logg -lm #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing @@ -142,7 +142,7 @@ $(BUILD): @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile build-portlibs: - @make -C libs/3ds_portlibs zlib freetype libjpeg-turbo libpng + @make -C libs/3ds_portlibs zlib freetype libjpeg-turbo libpng libogg libvorbis build-sf2dlib: @make -C libs/sf2dlib/libsf2d build diff --git a/libs/3ds_portlibs/.gitignore b/libs/3ds_portlibs/.gitignore index 115f9d0..8e0d03f 100644 --- a/libs/3ds_portlibs/.gitignore +++ b/libs/3ds_portlibs/.gitignore @@ -4,4 +4,6 @@ libjpeg-* libpng-* sqlite-* zlib-* +libogg-* +libvorbis-* build/ \ No newline at end of file diff --git a/libs/3ds_portlibs/Makefile b/libs/3ds_portlibs/Makefile index 355b8ad..c47b8c1 100644 --- a/libs/3ds_portlibs/Makefile +++ b/libs/3ds_portlibs/Makefile @@ -28,6 +28,16 @@ ZLIB_VERSION := $(ZLIB)-1.2.8 ZLIB_SRC := $(ZLIB_VERSION).tar.gz ZLIB_DOWNLOAD := "http://prdownloads.sourceforge.net/libpng/zlib-1.2.8.tar.gz" +LIBOGG := libogg +LIBOGG_VERSION := $(LIBOGG)-1.3.2 +LIBOGG_SRC := $(LIBOGG_VERSION).tar.gz +LIBOGG_DOWNLOAD := "http://downloads.xiph.org/releases/ogg/libogg-1.3.2.tar.gz" + +LIBVORBIS := libvorbis +LIBVORBIS_VERSION := $(LIBVORBIS)-1.3.5 +LIBVORBIS_SRC := $(LIBVORBIS_VERSION).tar.gz +LIBVORBIS_DOWNLOAD := "http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.5.tar.gz" + export PORTLIBS := $(CURDIR)/build export PATH := $(DEVKITARM)/bin:$(PATH) export PKG_CONFIG_PATH := $(PORTLIBS)/lib/pkgconfig @@ -66,8 +76,8 @@ $(FREETYPE): $(FREETYPE_SRC) ./configure --prefix=$(PORTLIBS) --host=arm-none-eabi --disable-shared --enable-static --without-harfbuzz @$(MAKE) -C $(FREETYPE_VERSION) @make create_build_dir - @cp -srf $(CURDIR)/freetype-2.6/include/. $(CURDIR)/build/include - @cp -sf $(CURDIR)/freetype-2.6/objs/.libs/libfreetype.a $(CURDIR)/build/lib/libfreetype.a + @cp -srf $(CURDIR)/$(FREETYPE_VERSION)/include/. $(CURDIR)/build/include + @cp -sf $(CURDIR)/$(FREETYPE_VERSION)/objs/.libs/libfreetype.a $(CURDIR)/build/lib/libfreetype.a $(LIBEXIF): $(LIBEXIF_SRC) @[ -d $(LIBEXIF_VERSION) ] || tar -xf $< @@ -81,8 +91,8 @@ $(LIBJPEGTURBO): $(LIBJPEGTURBO_SRC) ./configure --prefix=$(PORTLIBS) --host=arm-none-eabi --disable-shared --enable-static @$(MAKE) CFLAGS+="\"-Drandom()=rand()\"" -C $(LIBJPEGTURBO_VERSION) @make create_build_dir - @cp -sf $(CURDIR)/libjpeg-turbo-*/*.h $(CURDIR)/build/include - @cp -sf $(CURDIR)/libjpeg-turbo-*/.libs/libjpeg.a $(CURDIR)/build/lib/libjpeg.a + @cp -sf $(CURDIR)/$(LIBJPEGTURBO_VERSION)/*.h $(CURDIR)/build/include + @cp -sf $(CURDIR)/$(LIBJPEGTURBO_VERSION)/.libs/libjpeg.a $(CURDIR)/build/lib/libjpeg.a $(LIBPNG): $(LIBPNG_SRC) @[ -d $(LIBPNG_VERSION) ] || tar -xf $< @@ -90,8 +100,8 @@ $(LIBPNG): $(LIBPNG_SRC) ./configure --prefix=$(PORTLIBS) --host=arm-none-eabi --disable-shared --enable-static @$(MAKE) -C $(LIBPNG_VERSION) @make create_build_dir - @cp -sf $(CURDIR)/libpng-*/*.h $(CURDIR)/build/include - @cp -sf $(CURDIR)/libpng-*/.libs/*.a $(CURDIR)/build/lib/libpng.a + @cp -sf $(CURDIR)/$(LIBPNG_VERSION)/*.h $(CURDIR)/build/include + @cp -sf $(CURDIR)/$(LIBPNG_VERSION)/.libs/*.a $(CURDIR)/build/lib/libpng.a # sqlite won't work with -ffast-math $(SQLITE): $(SQLITE_SRC) @@ -107,8 +117,26 @@ $(ZLIB): $(ZLIB_SRC) CHOST=arm-none-eabi ./configure --static --prefix=$(PORTLIBS) @$(MAKE) -C $(ZLIB_VERSION) @make create_build_dir - @cp -sf $(CURDIR)/zlib-*/*.h $(CURDIR)/build/include - @cp -sf $(CURDIR)/zlib-*/libz.a $(CURDIR)/build/lib/libz.a + @cp -sf $(CURDIR)/$(ZLIB_VERSION)/*.h $(CURDIR)/build/include + @cp -sf $(CURDIR)/$(ZLIB_VERSION)/libz.a $(CURDIR)/build/lib/libz.a + +$(LIBOGG): $(LIBOGG_SRC) + @[ -d $(LIBOGG_VERSION) ] || tar -xf $< + @cd $(LIBOGG_VERSION) && \ + ./configure --prefix=$(PORTLIBS) --host=arm-none-eabi --disable-shared --enable-static + @$(MAKE) -C $(LIBOGG_VERSION) + @make create_build_dir + @cp -srf $(CURDIR)/$(LIBOGG_VERSION)/include/. $(CURDIR)/build/include + @cp -sf $(CURDIR)/$(LIBOGG_VERSION)/src/.libs/*.a $(CURDIR)/build/lib + +$(LIBVORBIS): $(LIBVORBIS_SRC) + @[ -d $(LIBVORBIS_VERSION) ] || tar -xf $< + @cd $(LIBVORBIS_VERSION) && \ + ./configure --prefix=$(PORTLIBS) --host=arm-none-eabi --disable-shared --enable-static + @$(MAKE) -C $(LIBVORBIS_VERSION) + @make create_build_dir + @cp -srf $(CURDIR)/$(LIBVORBIS_VERSION)/include/. $(CURDIR)/build/include + @cp -sf $(CURDIR)/$(LIBVORBIS_VERSION)/lib/.libs/*.a $(CURDIR)/build/lib # Downloads $(ZLIB_SRC): @@ -129,6 +157,12 @@ $(LIBPNG_SRC): $(SQLITE_SRC): wget -O $@ $(SQLITE_DOWNLOAD) +$(LIBOGG_SRC): + wget -O $@ $(LIBOGG_DOWNLOAD) + +$(LIBVORBIS_SRC): + wget -O $@ $(LIBVORBIS_DOWNLOAD) + install-zlib: @$(MAKE) -C $(ZLIB_VERSION) install @@ -138,6 +172,8 @@ install: @[ ! -d $(LIBJPEGTURBO_VERSION) ] || $(MAKE) -C $(LIBJPEGTURBO_VERSION) install @[ ! -d $(LIBPNG_VERSION) ] || $(MAKE) -C $(LIBPNG_VERSION) install @[ ! -d $(SQLITE_VERSION) ] || $(MAKE) -C $(SQLITE_VERSION) install-libLTLIBRARIES install-data + @[ ! -d $(LIBOGG_VERSION) ] || $(MAKE) -C $(LIBOGG_VERSION) install + @[ ! -d $(LIBVORBIS_VERSION) ] || $(MAKE) -C $(LIBVORBIS_VERSION) install clean: @$(RM) -r $(FREETYPE_VERSION) @@ -146,5 +182,7 @@ clean: @$(RM) -r $(LIBPNG_VERSION) @$(RM) -r $(SQLITE_VERSION) @$(RM) -r $(ZLIB_VERSION) + @$(RM) -r $(LIBOGG_VERSION) + @$(RM) -r $(LIBVORBIS_VERSION) @rm -rf $(CURDIR)/build @rm -f $(CURDIR)/*.tar.* diff --git a/sdcard/3ds/ctruLua/main.lua b/sdcard/3ds/ctruLua/main.lua index a0870ab..64d0770 100644 --- a/sdcard/3ds/ctruLua/main.lua +++ b/sdcard/3ds/ctruLua/main.lua @@ -8,6 +8,22 @@ repeat local file = require("openfile")("Choose a Lua file to execute", nil, ".lua", "exist") if file then fs.setDirectory(file:match("^(.-)[^/]*$")) - dofile(file) + local success, err = pcall(dofile, file) + if not success then + local gfx = require("ctr.gfx") + local hid = require("ctr.hid") + gfx.set3D(false) + gfx.color.setDefault(0xFFFFFFFF) + gfx.color.setBackground(0xFF000000) + gfx.font.setDefault() + while true do + hid.read() + if hid.keys().down.start then break end + gfx.startFrame(gfx.GFX_TOP) + gfx.wrappedText(0, 0, err, gfx.TOP_WIDTH) + gfx.endFrame() + gfx.render() + end + end end until not file \ No newline at end of file diff --git a/sdcard/3ds/ctruLua/tests/audio/main.lua b/sdcard/3ds/ctruLua/tests/audio/main.lua new file mode 100644 index 0000000..169c09c --- /dev/null +++ b/sdcard/3ds/ctruLua/tests/audio/main.lua @@ -0,0 +1,52 @@ +local ctr = require("ctr") +local hid = require("ctr.hid") +local gfx = require("ctr.gfx") +local audio = require("ctr.audio") + +local test = assert(audio.load("test.wav")) + +local channel = -1 +local speed = 1 +local leftBalance = 0.5 + +while true do + hid.read() + local keys = hid.keys() + if keys.down.start then break end + + if keys.down.a then + channel = test:play() + end + + if keys.down.up then + speed = speed + 0.01 + test:speed(speed) + audio.speed(nil, speed) + end + if keys.down.down then + speed = speed - 0.01 + test:speed(speed) + audio.speed(nil, speed) + end + + if keys.down.left then + leftBalance = leftBalance + 0.1 + test:mix(1-leftBalance, leftBalance) + audio.mix(nil, leftBalance, 1-leftBalance) + end + if keys.down.right then + leftBalance = leftBalance - 0.1 + test:mix(1-leftBalance, leftBalance) + audio.mix(nil, leftBalance, 1-leftBalance) + end + + gfx.startFrame(gfx.GFX_TOP) + gfx.text(5, 5, "Audio test! "..tostring(test:time()).."/"..tostring(test:duration()).."s") + gfx.text(5, 25, "Last audio played on channel "..tostring(channel)) + gfx.text(5, 65, "Speed: "..(speed*100).."% - Left balance: "..(leftBalance*100).."%") + gfx.endFrame() + + gfx.render() +end + +test:unload() \ No newline at end of file diff --git a/sdcard/3ds/ctruLua/tests/audio/test.wav b/sdcard/3ds/ctruLua/tests/audio/test.wav new file mode 100644 index 0000000000000000000000000000000000000000..3a71ee6ca27e3f4a9ad89c16faee0540acfb2fd6 GIT binary patch literal 321974 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

Zp_1gXW!a^8hOzO;iwmn!53Bca6aK=yHP8JRrW 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

*$egzl$Dc?3e7}^gkESH%{4;(eqLJ5&J>= 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

    R+Ym9d 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 JeldI`{U^ufFU9}> literal 0 HcmV?d00001 diff --git a/source/audio.c b/source/audio.c new file mode 100644 index 0000000..2bc9024 --- /dev/null +++ b/source/audio.c @@ -0,0 +1,851 @@ +/*** +The `audio` module. +An audio channel can play only one audio object at a time. +There are 24 audio channels available, numbered from 0 to 23. +@module ctr.audio +@usage local audio = require("ctr.audio") +*/ + +#include <3ds.h> + +#include +#include +#include + +#include +#include + +#include +#include + +// Audio object type +typedef enum { + TYPE_UNKNOWN = -1, + TYPE_OGG = 0, + TYPE_WAV = 1 +} filetype; + +// Audio object userdata +typedef struct { + filetype type; // file type + + // OGG Vorbis specific + OggVorbis_File vf; // ogg vorbis file + + // Needed for playback + float rate; // sample rate (per channel) (Hz) + u32 channels; // channel count + u32 encoding; // data encoding (NDSP_ENCODING_*) + u32 nsamples; // numbers of samples in the audio (per channel, not the total) + u32 size; // number of bytes in the audio (total, ie data size) + char* data; // raw audio data + + // Playing parameters (type-independant) + float mix[12]; // mix parameters + ndspInterpType interp; // interpolation type + double speed; // playing speed +} audio_userdata; + +// Indicate if NDSP was initialized or not. +// NDSP doesn't work on citra yet. +// Please only throw an error related to this when using a ndsp function, so other parts of the +// audio module are still available on citra, like audio.load() and audio:info(). +bool isAudioInitialized = false; + +// Array of the last audio_userdata sent to each channel; channels range from 0 to 23 +audio_userdata* channels[24]; + +/*** +Load an audio file. +OGG Vorbis and PCM WAV file format are currently supported. +(Most WAV files use the PCM encoding). +@function load +@tparam string path path to the file +@tparam[opt=detect] string type file type, `"ogg"` or `"wav"`. + If set to `"detect"`, will try to deduce the type from the filename. +@treturn[1] audio the loaded audio object +@treturn[2] nil if a error happened +@treturn[2] string error message +*/ +static int audio_load(lua_State *L) { + const char *path = luaL_checkstring(L, 1); + const char* argType = luaL_optstring(L, 2, "detect"); + + // Create userdata + audio_userdata *audio = lua_newuserdata(L, sizeof(*audio)); + luaL_getmetatable(L, "LAudio"); + lua_setmetatable(L, -2); + for (int i=0; i<12; i++) audio->mix[i] = 1; + audio->interp = NDSP_INTERP_LINEAR; + audio->speed = 1; + + // Get file type + filetype type = TYPE_UNKNOWN; + if (strcmp(argType, "detect") == 0) { + const char *dot = strrchr(path, '.'); + if (!dot || dot == path) dot = ""; + const char *ext = dot + 1; + if (strncmp(ext, "ogg", 3) == 0) type = TYPE_OGG; + else if (strncmp(ext, "wav", 3) == 0) type = TYPE_WAV; + } else if (strcmp(argType, "ogg") == 0) { + type = TYPE_OGG; + } else if (strcmp(argType, "wav") == 0) { + type = TYPE_WAV; + } + + // Open and read file + if (type == TYPE_OGG) { + audio->type = TYPE_OGG; + + // Load audio file + if (ov_fopen(path, &audio->vf) < 0) { + lua_pushnil(L); + lua_pushstring(L, "input does not appear to be a valid ogg vorbis file or doesn't exist"); + return 2; + } + + // Some Ogg Vorbis decoding parameters + bool bigendianp = false; // bigendianness + u8 word = 2; // word size; 1 or 2 (8bits/sample or 16bits/sample) + bool sgned = true; // signed data + + // Decoding Ogg Vorbis bitstream + vorbis_info* vi = ov_info(&audio->vf, -1); + if (vi == NULL) luaL_error(L, "could not retrieve ogg audio stream informations"); + + audio->rate = vi->rate; + audio->channels = vi->channels; + audio->encoding = NDSP_ENCODING_PCM16; + audio->nsamples = ov_pcm_total(&audio->vf, -1); + audio->size = audio->nsamples * word; + + if (linearSpaceFree() < audio->size) luaL_error(L, "not enough linear memory available"); + audio->data = linearAlloc(audio->size); + + // Decoding loop + int offset = 0; + int eof = 0; + int current_section; + while (!eof) { + long ret = ov_read(&audio->vf, &audio->data[offset], 4096, bigendianp, word, sgned, ¤t_section); + + if (ret == 0) { + eof = 1; + } else if (ret < 0) { + ov_clear(&audio->vf); + linearFree(audio->data); + luaL_error(L, "error in the ogg vorbis stream"); + return 0; + } else { + // TODO handle multiple links (http://xiph.org/vorbis/doc/vorbisfile/decoding.html) + offset += ret; + } + } + + return 1; + + } else if (type == TYPE_WAV) { + audio->type = TYPE_WAV; + + // Used this as a reference for the WAV format: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html + + // Load file + FILE *file = fopen(path, "rb"); + if (file) { + bool valid = true; // if something goes wrong, this will be false + + char buff[8]; + + // Master chunk + fread(buff, 4, 1, file); // ckId + if (strncmp(buff, "RIFF", 4) != 0) valid = false; + + fseek(file, 4, SEEK_CUR); // skip ckSize + + fread(buff, 4, 1, file); // WAVEID + if (strncmp(buff, "WAVE", 4) != 0) valid = false; + + // fmt Chunk + fread(buff, 4, 1, file); // ckId + if (strncmp(buff, "fmt ", 4) != 0) valid = false; + + fread(buff, 4, 1, file); // ckSize + if (*buff != 16) valid = false; // should be 16 for PCM format + + fread(buff, 2, 1, file); // wFormatTag + if (*buff != 0x0001) valid = false; // PCM format + + u16 channels; + fread(&channels, 2, 1, file); // nChannels + audio->channels = channels; + + u32 rate; + fread(&rate, 4, 1, file); // nSamplesPerSec + audio->rate = rate; + + fseek(file, 4, SEEK_CUR); // skip nAvgBytesPerSec + + u16 byte_per_block; // 1 block = 1*channelCount samples + fread(&byte_per_block, 2, 1, file); // nBlockAlign + + u16 byte_per_sample; + fread(&byte_per_sample, 2, 1, file); // wBitsPerSample + byte_per_sample /= 8; // bits -> bytes + + // There may be some additionals chunks between fmt and data + // TODO handle some usefull chunks that may be here + fread(&buff, 4, 1, file); // ckId + while (strncmp(buff, "data", 4) != 0) { + u32 size; + fread(&size, 4, 1, file); // ckSize + + fseek(file, size, SEEK_CUR); // skip chunk + + int i = fread(&buff, 4, 1, file); // next chunk ckId + + if (i < 4) { // reached EOF before finding a data chunk + valid = false; + break; + } + } + + // data Chunk (ckId already read) + u32 size; + fread(&size, 4, 1, file); // ckSize + audio->size = size; + + audio->nsamples = audio->size / byte_per_block; + + if (byte_per_sample == 1) audio->encoding = NDSP_ENCODING_PCM8; + else if (byte_per_sample == 2) audio->encoding = NDSP_ENCODING_PCM16; + else luaL_error(L, "unknown encoding, needs to be PCM8 or PCM16"); + + if (!valid) { + fclose(file); + luaL_error(L, "invalid PCM wav file"); + return 0; + } + + // Read data + if (linearSpaceFree() < audio->size) luaL_error(L, "not enough linear memory available"); + audio->data = linearAlloc(audio->size); + + fread(audio->data, audio->size, 1, file); + + fclose(file); + return 1; + + } else { + lua_pushnil(L); + lua_pushfstring(L, "error while opening wav file: %s", strerror(errno));; + return 2; + } + } + + luaL_error(L, "unknown audio type"); + return 0; +} + +/*** +Check if audio is currently playing on a channel. +@function playing +@tparam[opt] integer channel number; if `nil` will search the first channel playing an audio +@treturn boolean `true` if the channel is currently playing the audio, `false` otherwise. + If channel is not set, `false` means no audio is playing at all. +*/ +static int audio_playing(lua_State *L) { + if (!isAudioInitialized) { + lua_pushboolean(L, false); + return 1; + } + + int channel = luaL_optinteger(L, 1, -1); + if (channel < -1 || channel > 23) luaL_error(L, "channel number must be between 0 and 23"); + + // Search a channel playing audio + if (channel == -1) { + for (int i = 0; i <= 23; i++) { + if (ndspChnIsPlaying(i)) { + lua_pushboolean(L, true); + return 1; + } + } + + lua_pushboolean(L, false); + return 1; + + } else { + lua_pushboolean(L, ndspChnIsPlaying(channel)); + return 1; + } + + return 0; +} + +/*** +Set the mix parameters (volumes) of a channel. +Volumes go from 0 (0%) to 1 (100%). +Note that when a new audio object will play on this channel, theses parameters will be +reset with the new audio object defaults (set in `audio:mix()`). +@function mix +@tparam[opt] integer channel the channel number, if `nil` will change the mix parmaters of all channels +@tparam[opt=1] number frontLeft front left volume +@tparam[opt=frontLeft] number frontRight front right volume +@tparam[opt=frontLeft] number backLeft back left volume +@tparam[opt=frontRight] number backRight back right volume +*/ +static int audio_mix(lua_State *L) { + if (!isAudioInitialized) luaL_error(L, "audio wasn't initialized correctly"); + + int channel = luaL_optinteger(L, 1, -1); + if (channel < -1 || channel > 23) luaL_error(L, "channel number must be between 0 and 23"); + + float mix[12]; + mix[0] = luaL_optnumber(L, 2, 1); + mix[1] = luaL_optnumber(L, 3, mix[0]); + mix[2] = luaL_optnumber(L, 4, mix[0]); + mix[3] = luaL_optnumber(L, 5, mix[2]); + + if (channel == -1) { + for (int i=0; i<=23; i++) ndspChnSetMix(i, mix); + } else { + ndspChnSetMix(channel, mix); + } + + return 0; +} + +/*** +Set the interpolation type of a channel. +Note that when a new audio object will play on this channel, this parameter will be +reset with the new audio object default (set in `audio:interpolation()`). +@function interpolation +@tparam[opt] integer channel stop playing audio on this channel; if `nil` will change interpolation type on all channels +@tparam[opt=linear] string "none", "linear" or "polyphase" +*/ +static int audio_interpolation(lua_State *L) { + if (!isAudioInitialized) luaL_error(L, "audio wasn't initialized correctly"); + + int channel = luaL_optinteger(L, 1, -1); + if (channel < -1 || channel > 23) luaL_error(L, "channel number must be between 0 and 23"); + + const char* interpArg = luaL_optstring(L, 2, "linear"); + + ndspInterpType interp; + if (strcmp(interpArg, "none") == 0) + interp = NDSP_INTERP_NONE; + else if (strcmp(interpArg, "linear") == 0) + interp = NDSP_INTERP_LINEAR; + else if (strcmp(interpArg, "polyphase") == 0) + interp = NDSP_INTERP_POLYPHASE; + else { + luaL_error(L, "unknown interpolation type"); + return 0; + } + + if (channel == -1) { + for (int i=0; i<=23; i++) ndspChnSetInterp(i, interp); + } else { + ndspChnSetInterp(channel, interp); + } + + return 0; +} + +/*** +Set the speed of the audio playing in a channel. +Speed is expressed as a percentage of the normal playing speed. +1 is 100% speed and 2 is 200%, etc. +Note that when a new audio object will play on this channel, this parameter will be +reset with the new audio object default (set in `audio:speed()`). +@function speed +@tparam[opt] integer channel stop playing audio on this channel; if `nil` will change interpolation type on all channels +@tparam[opt=1] number speed percentage +*/ +static int audio_speed(lua_State *L) { + if (!isAudioInitialized) luaL_error(L, "audio wasn't initialized correctly"); + + int channel = luaL_optinteger(L, 1, -1); + if (channel < -1 || channel > 23) luaL_error(L, "channel number must be between 0 and 23"); + + double speed = luaL_optnumber(L, 2, 1); + + if (channel == -1) { + for (int i=0; i<=23; i++) { + if (channels[i]) ndspChnSetRate(i, channels[i]->rate * speed); + } + } else { + if (channels[channel]) ndspChnSetRate(channel, channels[channel]->rate * speed); + } + + return 0; +} + +/*** +Stop playing all audio on all channels or a specific channel. +@function stop +@tparam[opt] integer channel stop playing audio on this channel; if `nil` will stop audio on all channels +@treturn integer number of channels where audio was stopped +*/ +static int audio_stop(lua_State *L) { + if (!isAudioInitialized) { + lua_pushinteger(L, 0); + return 1; + } + + int channel = luaL_optinteger(L, 1, -1); + + int n = 0; + + if (channel == -1) { + for (int i = 0; i <= 23; i++) { + if (ndspChnIsPlaying(i)) { + ndspChnWaveBufClear(i); + n++; + } + } + } else if (channel < 0 || channel > 23) { + luaL_error(L, "channel number must be between 0 and 23"); + } else { + if (ndspChnIsPlaying(channel)) { + ndspChnWaveBufClear(channel); + n++; + } + } + + lua_pushinteger(L, n); + + return 1; +} + +/*** +audio object +@section Methods +*/ + +/*** +Returns the audio object duration. +@function :duration +@treturn number duration in seconds +*/ +static int audio_object_duration(lua_State *L) { + audio_userdata *audio = luaL_checkudata(L, 1, "LAudio"); + + lua_pushnumber(L, (double)(audio->nsamples) / audio->rate); + + return 1; +} + +/*** +Returns the current playing position. +@function :time +@tparam[opt] integer channel number; if `nil` will use the first channel found which played this audio +@treturn number time in seconds +*/ +static int audio_object_time(lua_State *L) { + audio_userdata *audio = luaL_checkudata(L, 1, "LAudio"); + + int channel = luaL_optinteger(L, 2, -1); + if (channel < -1 || channel > 23) luaL_error(L, "channel number must be between 0 and 23"); + + // Search a channel playing the audio object + if (channel == -1) { + for (int i = 0; i <= 23; i++) { + if (channels[i] == audio) { + channel = i; + break; + } + } + } + + if (channel == -1 || channels[channel] != audio || !isAudioInitialized) // audio not playing + lua_pushnumber(L, 0); + else + lua_pushnumber(L, (double)(ndspChnGetSamplePos(channel)) / audio->rate); + + return 1; +} + +/*** +Check if the audio is currently playing. +@function :playing +@tparam[opt] integer channel channel number; if `nil` will search the first channel playing this audio +@treturn boolean true if the channel is currently playing the audio, false otherwise +*/ +static int audio_object_playing(lua_State *L) { + if (!isAudioInitialized) { + lua_pushboolean(L, false); + return 1; + } + + audio_userdata *audio = luaL_checkudata(L, 1, "LAudio"); + + int channel = luaL_optinteger(L, 2, -1); + if (channel < -1 || channel > 23) luaL_error(L, "channel number must be between 0 and 23"); + + // Search a channel playing the audio object + if (channel == -1) { + for (int i = 0; i <= 23; i++) { + if (channels[i] == audio && ndspChnIsPlaying(i)) { + lua_pushboolean(L, true); + return 1; + } + } + + lua_pushboolean(L, false); + return 1; + + } else { + lua_pushboolean(L, channels[channel] == audio && ndspChnIsPlaying(channel)); + return 1; + } + + return 0; +} + +/*** +Set the mix parameters (volumes) of the audio. +Volumes go from 0 (0%) to 1 (100%). +@function :mix +@tparam[opt=1] number frontLeft front left volume +@tparam[opt=frontLeft] number frontRight front right volume +@tparam[opt=frontLeft] number backLeft back left volume +@tparam[opt=frontRight] number backRight back right volume +*/ +static int audio_object_mix(lua_State *L) { + audio_userdata *audio = luaL_checkudata(L, 1, "LAudio"); + + audio->mix[0] = luaL_optnumber(L, 2, 1); + audio->mix[1] = luaL_optnumber(L, 3, audio->mix[0]); + audio->mix[2] = luaL_optnumber(L, 4, audio->mix[0]); + audio->mix[3] = luaL_optnumber(L, 5, audio->mix[2]); + + return 0; +} + +/*** +Set the interpolation type of the audio. +@function :interpolation +@tparam[opt=linear] string "none", "linear" or "polyphase" +*/ +static int audio_object_interpolation(lua_State *L) { + audio_userdata *audio = luaL_checkudata(L, 1, "LAudio"); + + const char* interp = luaL_optstring(L, 2, "linear"); + + if (strcmp(interp, "none") == 0) + audio->interp = NDSP_INTERP_NONE; + else if (strcmp(interp, "linear") == 0) + audio->interp = NDSP_INTERP_LINEAR; + else if (strcmp(interp, "polyphase") == 0) + audio->interp = NDSP_INTERP_POLYPHASE; + else { + luaL_error(L, "unknown interpolation type"); + return 0; + } + + return 0; +} + +/*** +Set the speed of the audio. +Speed is expressed as a percentage of the normal playing speed. +1 is 100% speed and 2 is 200%, etc. +@function :speed +@tparam[opt=1] number speed percentage +*/ +static int audio_object_speed(lua_State *L) { + audio_userdata *audio = luaL_checkudata(L, 1, "LAudio"); + + audio->speed = luaL_optnumber(L, 2, 1); + + return 0; +} + +/*** +Plays the audio file. +@function :play +@tparam[opt=false] boolean loop if the audio should loop or not +@tparam[opt] integer channel the channel to play the audio on (0-23); if `nil` will use the first available channel. + If the channel was playing another audio, it will be stopped and replaced by this audio. + If not set and no channel is available, will return nil plus an error message. +@treturn[1] integer channel number the audio is playing on +@treturn[2] nil an error happened and the audio was not played +@treturn[2] error the error message +*/ +static int audio_object_play(lua_State *L) { + if (!isAudioInitialized) luaL_error(L, "audio wasn't initialized correctly"); + + audio_userdata *audio = luaL_checkudata(L, 1, "LAudio"); + bool loop = lua_toboolean(L, 2); + int channel = luaL_optinteger(L, 3, -1); + + // Find a free channel + if (channel == -1) { + for (int i = 0; i <= 23; i++) { + if (!ndspChnIsPlaying(i)) { + channel = i; + break; + } + } + } + if (channel == -1) { + lua_pushnil(L); + lua_pushstring(L, "no audio channel is currently available"); + return 2; + } + if (channel < 0 || channel > 23) luaL_error(L, "channel number must be between 0 and 23"); + + // Set channel parameters + ndspChnWaveBufClear(channel); + ndspChnReset(channel); + ndspChnInitParams(channel); + ndspChnSetMix(channel, audio->mix); + ndspChnSetInterp(channel, audio->interp); + ndspChnSetRate(channel, audio->rate * audio->speed); // maybe hackish way to set a different speed, but it works + ndspChnSetFormat(channel, NDSP_CHANNELS(audio->channels) | NDSP_ENCODING(audio->encoding)); + + // Send & play audio data + ndspWaveBuf* waveBuf = calloc(1, sizeof(ndspWaveBuf)); + + waveBuf->data_vaddr = audio->data; + waveBuf->nsamples = audio->nsamples; + waveBuf->looping = loop; + + DSP_FlushDataCache((u32*)audio->data, audio->size); + + ndspChnWaveBufAdd(channel, waveBuf); + channels[channel] = audio; + + lua_pushinteger(L, channel); + + return 1; +} + +/*** +Stop playing an audio object. +@function :stop +@tparam[opt] integer channel stop playing the audio on this channel; if `nil` will stop all channels playing this audio. + If the channel is playing another audio object, this function will do nothing. +@treturn integer number of channels where this audio was stopped +*/ +static int audio_object_stop(lua_State *L) { + if (!isAudioInitialized) { + lua_pushinteger(L, 0); + return 1; + } + + audio_userdata *audio = luaL_checkudata(L, 1, "LAudio"); + int channel = luaL_optinteger(L, 2, -1); + + int n = 0; + + if (channel == -1) { + for (int i = 0; i <= 23; i++) { + if (channels[i] == audio && ndspChnIsPlaying(i)) { + ndspChnWaveBufClear(i); + n++; + } + } + } else if (channel < 0 || channel > 23) { + luaL_error(L, "channel number must be between 0 and 23"); + } else { + if (channels[channel] == audio && ndspChnIsPlaying(channel)) { + ndspChnWaveBufClear(channel); + n++; + } + } + + lua_pushinteger(L, n); + + return 1; +} + +/*** +Returns the audio object type. +@function :type +@treturn string "ogg" or "wav" +*/ +static int audio_object_type(lua_State *L) { + audio_userdata *audio = luaL_checkudata(L, 1, "LAudio"); + + if (audio->type == TYPE_OGG) + lua_pushstring(L, "ogg"); + else if (audio->type == TYPE_WAV) + lua_pushstring(L, "wav"); + + return 1; +} + +/*** +Unload an audio object. +@function :unload +*/ +static int audio_object_unload(lua_State *L) { + audio_userdata *audio = luaL_checkudata(L, 1, "LAudio"); + + // Stop playing the audio + if (isAudioInitialized) { + for (int i = 0; i <= 23; i++) { + if (channels[i] == audio) { + ndspChnWaveBufClear(i); + } + } + } + + if (audio->type == TYPE_OGG) ov_clear(&audio->vf); + + // Free memory + linearFree(audio->data); + + return 0; +} + +/*** +audio object (ogg-only). +Ogg Vorbis files specific methods. +Using one of theses methods will throw an error if used on an non-ogg audio object. +@section Methods +*/ + +/*** +Returns basic information about the audio in a vorbis bitstream. +@function :info +@treturn infoTable information table +*/ +static int audio_object_info(lua_State *L) { + audio_userdata *audio = luaL_checkudata(L, 1, "LAudio"); + + if (audio->type != TYPE_OGG) luaL_error(L, "only avaible on OGG audio objects"); + + vorbis_info* vi = ov_info(&audio->vf, -1); + if (vi == NULL) luaL_error(L, "could not retrieve audio stream informations"); + + lua_createtable(L, 0, 6); + + lua_pushinteger(L, vi->version); + lua_setfield(L, -2, "version"); + + lua_pushinteger(L, vi->channels); + lua_setfield(L, -2, "channels"); + + lua_pushinteger(L, vi->rate); + lua_setfield(L, -2, "rate"); + + lua_pushinteger(L, vi->bitrate_upper); + lua_setfield(L, -2, "bitrateUpper"); + + lua_pushinteger(L, vi->bitrate_nominal); + lua_setfield(L, -2, "bitrateNominal"); + + lua_pushinteger(L, vi->bitrate_lower); + lua_setfield(L, -2, "bitrateLower"); + + return 1; +} + +/*** +Returns the Ogg Vorbis bitstream comment. +@function :comment +@treturn commentTable comment table +*/ +static int audio_object_comment(lua_State *L) { + audio_userdata *audio = luaL_checkudata(L, 1, "LAudio"); + + if (audio->type != TYPE_OGG) luaL_error(L, "only avaible on OGG audio objects"); + + vorbis_comment *vc = ov_comment(&audio->vf, -1); + + if (vc == NULL) luaL_error(L, "could not retrieve audio stream comment"); + + lua_createtable(L, 0, 5); + + lua_newtable(L); + for (int i=0; icomments; i++) { + lua_pushstring(L, vc->user_comments[i]); + lua_seti(L, -2, i+1); + } + lua_setfield(L, -2, "userComments"); + + lua_pushstring(L, vc->vendor); + lua_setfield(L, -2, "vendor"); + + return 1; +} + +/*** +Tables return. +The detailled table structures returned by some methods of audio objects. +@section +*/ + +/*** +Vorbis bitstream information, returned by audio:info(). +If bitrateLower == bitrateNominal == bitrateUpper, the stream is fixed bitrate. +@table infoTable +@tfield integer version Vorbis encoder version used to create this bitstream +@tfield integer channels number of channels in bitstream +@tfield integer rate sampling rate of the bitstream +@tfield integer bitrateUpper the upper limit in a VBR bitstream; may be unset if no limit exists +@tfield integer bitrateNominal the average bitrate for a VBR bitstream; may be unset +@tfield integer bitrateLower the lower limit in a VBR bitstream; may be unset if no limit exists +*/ + +/*** +Vorbis bitstream comment, returned by audio:comment(). +@table commentTable +@tfield table userComments list of all the user comment +@tfield string vendor information about the Vorbis implementation that encoded the file +*/ + +// Audio object methods +static const struct luaL_Reg audio_object_methods[] = { + // common + { "duration", audio_object_duration }, + { "time", audio_object_time }, + { "playing", audio_object_playing }, + { "mix", audio_object_mix }, + { "interpolation", audio_object_interpolation }, + { "speed", audio_object_speed }, + { "play", audio_object_play }, + { "stop", audio_object_stop }, + { "type", audio_object_type }, + { "unload", audio_object_unload }, + { "__gc", audio_object_unload }, + // ogg only + { "info", audio_object_info }, + { "comment", audio_object_comment }, + { NULL, NULL } +}; + +// Library functions +static const struct luaL_Reg audio_lib[] = { + { "load", audio_load }, + { "playing", audio_playing }, + { "mix", audio_mix }, + { "interpolation", audio_interpolation }, + { "speed", audio_speed }, + { "stop", audio_stop }, + { NULL, NULL } +}; + +int luaopen_audio_lib(lua_State *L) { + luaL_newmetatable(L, "LAudio"); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + luaL_setfuncs(L, audio_object_methods, 0); + + luaL_newlib(L, audio_lib); + + return 1; +} + +void load_audio_lib(lua_State *L) { + isAudioInitialized = !ndspInit(); // ndspInit returns 0 in case of success + + luaL_requiref(L, "ctr.audio", luaopen_audio_lib, false); +} + +void unload_audio_lib(lua_State *L) { + if (isAudioInitialized) ndspExit(); +} diff --git a/source/ctr.c b/source/ctr.c index da788c0..0eff2a8 100644 --- a/source/ctr.c +++ b/source/ctr.c @@ -91,6 +91,14 @@ The `ctr.cam` module. */ void load_cam_lib(lua_State *L); +/*** +The `ctr.audio` module. +@table audio +@see ctr.audio +*/ +void load_audio_lib(lua_State *L); +void unload_audio_lib(lua_State *L); + /*** Return whether or not the program should continue. @function run @@ -132,7 +140,8 @@ struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } { "qtm", load_qtm_lib, NULL }, { "cfgu", load_cfgu_lib, NULL }, { "socket", load_socket_lib, NULL }, - { "cam", load_cam_lib, NULL }, + { "cam", load_cam_lib, NULL }, + { "audio", load_audio_lib, unload_audio_lib }, { NULL, NULL } }; diff --git a/source/gfx.c b/source/gfx.c index aa64956..49c8195 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -16,7 +16,7 @@ The `gfx` module. #include "font.h" -bool isGfxInitialised = false; +bool isGfxInitialized = false; bool is3DEnabled = false; //TODO: add a function for this in the ctrulib/sf2dlib. /*** @@ -459,7 +459,7 @@ void load_gfx_lib(lua_State *L) { sf2d_init(); sftd_init(); - isGfxInitialised = true; + isGfxInitialized = true; luaL_requiref(L, "ctr.gfx", luaopen_gfx_lib, 0); } diff --git a/source/main.c b/source/main.c index c9e7473..270c655 100644 --- a/source/main.c +++ b/source/main.c @@ -7,11 +7,11 @@ void load_ctr_lib(lua_State *L); void unload_ctr_lib(lua_State *L); -bool isGfxInitialised; +bool isGfxInitialized; // Display an error void error(const char *error) { - if (!isGfxInitialised) gfxInitDefault(); + if (!isGfxInitialized) gfxInitDefault(); gfxSet3D(false); consoleInit(GFX_TOP, NULL); @@ -28,7 +28,7 @@ void error(const char *error) { gspWaitForVBlank(); } - if (!isGfxInitialised) gfxExit(); + if (!isGfxInitialized) gfxExit(); } // Main loop From b4f5365e960b5824a404f5d8cea9c93c15df61ef Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Mon, 28 Dec 2015 20:57:03 +0100 Subject: [PATCH 046/101] Rolled back to RAM by default for textures (not working on hardware) --- source/texture.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/texture.c b/source/texture.c index 651d242..9416e68 100644 --- a/source/texture.c +++ b/source/texture.c @@ -35,13 +35,13 @@ int getType(const char *name) { Load a texture from a file. Supported formats: PNG, JPEG, BMP. @function load @tparam string path path to the image file -@tparam[opt=PLACE_VRAM] number place where to put the loaded texture +@tparam[opt=PLACE_RAM] number place where to put the loaded texture @tparam[opt=auto] number type type of the image @treturn texture the loaded texture object */ static int texture_load(lua_State *L) { const char *path = luaL_checkstring(L, 1); - u8 place = luaL_optinteger(L, 2, SF2D_PLACE_VRAM); //place in vram by default + u8 place = luaL_optinteger(L, 2, SF2D_PLACE_RAM); //place in vram by default u8 type = luaL_optinteger(L, 3, 3); //type 3 is "search at the end of the filename" texture_userdata *texture; From c053997f96ec0a4834afb8efaf8a9d3624829ba6 Mon Sep 17 00:00:00 2001 From: Reuh Date: Tue, 29 Dec 2015 17:20:29 +0100 Subject: [PATCH 047/101] Reset defaults before starting a file (main.lua) --- sdcard/3ds/ctruLua/main.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sdcard/3ds/ctruLua/main.lua b/sdcard/3ds/ctruLua/main.lua index 64d0770..05160d6 100644 --- a/sdcard/3ds/ctruLua/main.lua +++ b/sdcard/3ds/ctruLua/main.lua @@ -1,3 +1,4 @@ +local gfx = require("ctr.gfx") local fs = require("ctr.fs") -- Set up path @@ -5,12 +6,15 @@ local ldir = fs.getDirectory().."libs/" package.path = package.path..";".. ldir.."?.lua;".. ldir.."?/init.lua" repeat + gfx.set3D(false) + gfx.color.setDefault(0xFFFFFFFF) + gfx.color.setBackground(0xFF000000) + gfx.font.setDefault() local file = require("openfile")("Choose a Lua file to execute", nil, ".lua", "exist") if file then fs.setDirectory(file:match("^(.-)[^/]*$")) local success, err = pcall(dofile, file) if not success then - local gfx = require("ctr.gfx") local hid = require("ctr.hid") gfx.set3D(false) gfx.color.setDefault(0xFFFFFFFF) From e39fcc6c7bf0ea30fb96233235ec79399c72c77e Mon Sep 17 00:00:00 2001 From: Reuh Date: Tue, 29 Dec 2015 17:25:34 +0100 Subject: [PATCH 048/101] Added Tremor Ogg decoder, removed libvorbis from Makefile. OGG audio working perfectly. Tremor is extremly similar to libogg but only uses integers (and doesn't provide an encoder). The playing problem with libvorbis was probably a float-precision related issue. No need for make build-all. --- Makefile | 8 +- libs/tremor/CHANGELOG | 19 + libs/tremor/COPYING | 28 + libs/tremor/Makefile.am | 50 + libs/tremor/README | 46 + libs/tremor/Version_script.in | 62 + libs/tremor/asm_arm.h | 245 ++ libs/tremor/autogen.sh | 120 + libs/tremor/backends.h | 131 ++ libs/tremor/block.c | 497 ++++ libs/tremor/block.h | 24 + libs/tremor/codebook.c | 391 ++++ libs/tremor/codebook.h | 101 + libs/tremor/codec_internal.h | 92 + libs/tremor/config_types.h | 25 + libs/tremor/configure.in | 146 ++ libs/tremor/debian/Makefile.am | 6 + libs/tremor/debian/changelog | 9 + libs/tremor/debian/control | 22 + libs/tremor/debian/copyright | 37 + libs/tremor/debian/libvorbisidec-dev.install | 8 + libs/tremor/debian/libvorbisidec1.install | 1 + libs/tremor/debian/rules | 151 ++ libs/tremor/doc/OggVorbis_File.html | 132 ++ libs/tremor/doc/build.html | 111 + libs/tremor/doc/callbacks.html | 113 + libs/tremor/doc/datastructures.html | 61 + libs/tremor/doc/decoding.html | 82 + libs/tremor/doc/diff.html | 67 + libs/tremor/doc/example.html | 205 ++ libs/tremor/doc/fileinfo.html | 95 + libs/tremor/doc/index.html | 53 + libs/tremor/doc/initialization.html | 101 + libs/tremor/doc/ov_bitrate.html | 72 + libs/tremor/doc/ov_bitrate_instant.html | 65 + libs/tremor/doc/ov_callbacks.html | 78 + libs/tremor/doc/ov_clear.html | 64 + libs/tremor/doc/ov_comment.html | 66 + libs/tremor/doc/ov_info.html | 64 + libs/tremor/doc/ov_open.html | 115 + libs/tremor/doc/ov_open_callbacks.html | 110 + libs/tremor/doc/ov_pcm_seek.html | 81 + libs/tremor/doc/ov_pcm_seek_page.html | 83 + libs/tremor/doc/ov_pcm_tell.html | 63 + libs/tremor/doc/ov_pcm_total.html | 67 + libs/tremor/doc/ov_raw_seek.html | 75 + libs/tremor/doc/ov_raw_tell.html | 63 + libs/tremor/doc/ov_raw_total.html | 68 + libs/tremor/doc/ov_read.html | 115 + libs/tremor/doc/ov_seekable.html | 63 + libs/tremor/doc/ov_serialnumber.html | 67 + libs/tremor/doc/ov_streams.html | 64 + libs/tremor/doc/ov_test.html | 89 + libs/tremor/doc/ov_test_callbacks.html | 90 + libs/tremor/doc/ov_test_open.html | 82 + libs/tremor/doc/ov_time_seek.html | 70 + libs/tremor/doc/ov_time_seek_page.html | 83 + libs/tremor/doc/ov_time_tell.html | 63 + libs/tremor/doc/ov_time_total.html | 67 + libs/tremor/doc/overview.html | 61 + libs/tremor/doc/reference.html | 75 + libs/tremor/doc/return.html | 77 + libs/tremor/doc/seeking.html | 74 + libs/tremor/doc/style.css | 7 + libs/tremor/doc/threads.html | 50 + libs/tremor/doc/vorbis_comment.html | 70 + libs/tremor/doc/vorbis_info.html | 80 + libs/tremor/floor0.c | 439 ++++ libs/tremor/floor1.c | 460 ++++ libs/tremor/info.c | 390 +++ libs/tremor/iseeking_example.dontcompile | 265 +++ libs/tremor/ivorbiscodec.h | 204 ++ libs/tremor/ivorbisfile.h | 131 ++ libs/tremor/ivorbisfile_example.dontcompile | 91 + libs/tremor/lsp_lookup.h | 136 ++ libs/tremor/mapping0.c | 328 +++ libs/tremor/mdct.c | 510 ++++ libs/tremor/mdct.h | 52 + libs/tremor/mdct_lookup.h | 540 +++++ libs/tremor/misc.h | 252 ++ libs/tremor/os.h | 64 + libs/tremor/registry.c | 50 + libs/tremor/registry.h | 40 + libs/tremor/res012.c | 374 +++ libs/tremor/sharedbook.c | 447 ++++ libs/tremor/synthesis.c | 131 ++ libs/tremor/vorbisfile.c | 1968 ++++++++++++++++ libs/tremor/vorbisidec.pc.in | 14 + libs/tremor/win32/VS2005/libogg.vsprops | 19 + .../win32/VS2005/libtremor/libtremor.vcproj | 865 +++++++ libs/tremor/win32/VS2008/libogg.vsprops | 19 + .../win32/VS2008/libtremor/libtremor.vcproj | 865 +++++++ libs/tremor/window.c | 83 + libs/tremor/window.h | 27 + libs/tremor/window_lookup.h | 2084 +++++++++++++++++ source/audio.c | 16 +- source/texture.c | 2 +- 97 files changed, 16571 insertions(+), 15 deletions(-) create mode 100644 libs/tremor/CHANGELOG create mode 100644 libs/tremor/COPYING create mode 100644 libs/tremor/Makefile.am create mode 100644 libs/tremor/README create mode 100644 libs/tremor/Version_script.in create mode 100644 libs/tremor/asm_arm.h create mode 100755 libs/tremor/autogen.sh create mode 100644 libs/tremor/backends.h create mode 100644 libs/tremor/block.c create mode 100644 libs/tremor/block.h create mode 100644 libs/tremor/codebook.c create mode 100644 libs/tremor/codebook.h create mode 100644 libs/tremor/codec_internal.h create mode 100644 libs/tremor/config_types.h create mode 100644 libs/tremor/configure.in create mode 100644 libs/tremor/debian/Makefile.am create mode 100644 libs/tremor/debian/changelog create mode 100644 libs/tremor/debian/control create mode 100644 libs/tremor/debian/copyright create mode 100644 libs/tremor/debian/libvorbisidec-dev.install create mode 100644 libs/tremor/debian/libvorbisidec1.install create mode 100755 libs/tremor/debian/rules create mode 100644 libs/tremor/doc/OggVorbis_File.html create mode 100644 libs/tremor/doc/build.html create mode 100644 libs/tremor/doc/callbacks.html create mode 100644 libs/tremor/doc/datastructures.html create mode 100644 libs/tremor/doc/decoding.html create mode 100644 libs/tremor/doc/diff.html create mode 100644 libs/tremor/doc/example.html create mode 100644 libs/tremor/doc/fileinfo.html create mode 100644 libs/tremor/doc/index.html create mode 100644 libs/tremor/doc/initialization.html create mode 100644 libs/tremor/doc/ov_bitrate.html create mode 100644 libs/tremor/doc/ov_bitrate_instant.html create mode 100644 libs/tremor/doc/ov_callbacks.html create mode 100644 libs/tremor/doc/ov_clear.html create mode 100644 libs/tremor/doc/ov_comment.html create mode 100644 libs/tremor/doc/ov_info.html create mode 100644 libs/tremor/doc/ov_open.html create mode 100644 libs/tremor/doc/ov_open_callbacks.html create mode 100644 libs/tremor/doc/ov_pcm_seek.html create mode 100644 libs/tremor/doc/ov_pcm_seek_page.html create mode 100644 libs/tremor/doc/ov_pcm_tell.html create mode 100644 libs/tremor/doc/ov_pcm_total.html create mode 100644 libs/tremor/doc/ov_raw_seek.html create mode 100644 libs/tremor/doc/ov_raw_tell.html create mode 100644 libs/tremor/doc/ov_raw_total.html create mode 100644 libs/tremor/doc/ov_read.html create mode 100644 libs/tremor/doc/ov_seekable.html create mode 100644 libs/tremor/doc/ov_serialnumber.html create mode 100644 libs/tremor/doc/ov_streams.html create mode 100644 libs/tremor/doc/ov_test.html create mode 100644 libs/tremor/doc/ov_test_callbacks.html create mode 100644 libs/tremor/doc/ov_test_open.html create mode 100644 libs/tremor/doc/ov_time_seek.html create mode 100644 libs/tremor/doc/ov_time_seek_page.html create mode 100644 libs/tremor/doc/ov_time_tell.html create mode 100644 libs/tremor/doc/ov_time_total.html create mode 100644 libs/tremor/doc/overview.html create mode 100644 libs/tremor/doc/reference.html create mode 100644 libs/tremor/doc/return.html create mode 100644 libs/tremor/doc/seeking.html create mode 100644 libs/tremor/doc/style.css create mode 100644 libs/tremor/doc/threads.html create mode 100644 libs/tremor/doc/vorbis_comment.html create mode 100644 libs/tremor/doc/vorbis_info.html create mode 100644 libs/tremor/floor0.c create mode 100644 libs/tremor/floor1.c create mode 100644 libs/tremor/info.c create mode 100644 libs/tremor/iseeking_example.dontcompile create mode 100644 libs/tremor/ivorbiscodec.h create mode 100644 libs/tremor/ivorbisfile.h create mode 100644 libs/tremor/ivorbisfile_example.dontcompile create mode 100644 libs/tremor/lsp_lookup.h create mode 100644 libs/tremor/mapping0.c create mode 100644 libs/tremor/mdct.c create mode 100644 libs/tremor/mdct.h create mode 100644 libs/tremor/mdct_lookup.h create mode 100644 libs/tremor/misc.h create mode 100644 libs/tremor/os.h create mode 100644 libs/tremor/registry.c create mode 100644 libs/tremor/registry.h create mode 100644 libs/tremor/res012.c create mode 100644 libs/tremor/sharedbook.c create mode 100644 libs/tremor/synthesis.c create mode 100644 libs/tremor/vorbisfile.c create mode 100644 libs/tremor/vorbisidec.pc.in create mode 100644 libs/tremor/win32/VS2005/libogg.vsprops create mode 100644 libs/tremor/win32/VS2005/libtremor/libtremor.vcproj create mode 100644 libs/tremor/win32/VS2008/libogg.vsprops create mode 100644 libs/tremor/win32/VS2008/libtremor/libtremor.vcproj create mode 100644 libs/tremor/window.c create mode 100644 libs/tremor/window.h create mode 100644 libs/tremor/window_lookup.h diff --git a/Makefile b/Makefile index 6ead142..671e455 100644 --- a/Makefile +++ b/Makefile @@ -29,9 +29,9 @@ include $(DEVKITARM)/3ds_rules #--------------------------------------------------------------------------------- TARGET := ctruLua BUILD := build -SOURCES := source libs/lua-5.3.2/src +SOURCES := source libs/lua-5.3.2/src libs/tremor DATA := data -INCLUDES := include libs/lua-5.3.2/src libs/lzlib +INCLUDES := include libs/lua-5.3.2/src libs/lzlib libs/tremor #ROMFS := romfs APP_TITLE := ctruLua @@ -55,7 +55,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 ASFLAGS := -g $(ARCH) LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) -LIBS := -lsfil -ljpeg -lsftd -lfreetype -lpng -lz -lsf2d -lctru -lvorbisfile -lvorbis -logg -lm +LIBS := -lsfil -ljpeg -lsftd -lfreetype -lpng -lz -lsf2d -lctru -logg -lm #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing @@ -142,7 +142,7 @@ $(BUILD): @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile build-portlibs: - @make -C libs/3ds_portlibs zlib freetype libjpeg-turbo libpng libogg libvorbis + @make -C libs/3ds_portlibs zlib freetype libjpeg-turbo libpng libogg build-sf2dlib: @make -C libs/sf2dlib/libsf2d build diff --git a/libs/tremor/CHANGELOG b/libs/tremor/CHANGELOG new file mode 100644 index 0000000..53f2335 --- /dev/null +++ b/libs/tremor/CHANGELOG @@ -0,0 +1,19 @@ +*** 20020517: 1.0.2 *** + + Playback bugfix to floor1; mode mistakenly used for sizing instead + of blockflag + +*** 20020515: 1.0.1 *** + + Added complete API documentation to source tarball. No code + changes. + +*** 20020412: 1.0.1 *** + + Fixed a clipping bug that affected ARM processors; negative + overflows were being properly clipped, but then clobbered to + positive by the positive overflow chec (asm_arm.h:CLIP_TO_15) + +*** 20020403: 1.0.0 *** + + Initial version \ No newline at end of file diff --git a/libs/tremor/COPYING b/libs/tremor/COPYING new file mode 100644 index 0000000..6111c6c --- /dev/null +++ b/libs/tremor/COPYING @@ -0,0 +1,28 @@ +Copyright (c) 2002, Xiph.org Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/tremor/Makefile.am b/libs/tremor/Makefile.am new file mode 100644 index 0000000..0a4bb2c --- /dev/null +++ b/libs/tremor/Makefile.am @@ -0,0 +1,50 @@ +AUTOMAKE_OPTIONS = foreign + +INCLUDES = -I./ @OGG_CFLAGS@ + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = vorbisidec.pc + +lib_LTLIBRARIES = libvorbisidec.la + +libvorbisidec_la_SOURCES = mdct.c block.c window.c \ + synthesis.c info.c \ + floor1.c floor0.c vorbisfile.c \ + res012.c mapping0.c registry.c codebook.c \ + sharedbook.c \ + codebook.h misc.h mdct_lookup.h\ + os.h mdct.h block.h ivorbisfile.h lsp_lookup.h\ + registry.h window.h window_lookup.h\ + codec_internal.h backends.h \ + asm_arm.h ivorbiscodec.h +libvorbisidec_la_LDFLAGS = -version-info @V_LIB_CURRENT@:@V_LIB_REVISION@:@V_LIB_AGE@ +libvorbisidec_la_LIBADD = @OGG_LIBS@ + +EXTRA_PROGRAMS = ivorbisfile_example iseeking_example +CLEANFILES = $(EXTRA_PROGRAMS) $(lib_LTLIBRARIES) + +ivorbisfile_example_SOURCES = ivorbisfile_example.c +ivorbisfile_example_LDFLAGS = -static +ivorbisfile_example_LDADD = libvorbisidec.la @OGG_LIBS@ + +iseeking_example_SOURCES = iseeking_example.c +iseeking_example_LDFLAGS = -static +iseeking_example_LDADD = libvorbisidec.la @OGG_LIBS@ + +includedir = $(prefix)/include/tremor + +include_HEADERS = ivorbiscodec.h ivorbisfile.h config_types.h + +EXTRA_DIST = vorbisidec.pc.in \ + $(srcdir)/doc/*.html $(srcdir)/win32/VS*/libtremor/*.vcproj + +example: + -ln -fs . vorbis + $(MAKE) ivorbisfile_example + $(MAKE) iseeking_example + +debug: + $(MAKE) all CFLAGS="@DEBUG@" + +profile: + $(MAKE) all CFLAGS="@PROFILE@" diff --git a/libs/tremor/README b/libs/tremor/README new file mode 100644 index 0000000..1321175 --- /dev/null +++ b/libs/tremor/README @@ -0,0 +1,46 @@ +This README covers the Ogg Vorbis 'Tremor' integer playback codec +source as of date 2002 09 02, version 1.0.0. + + ****** + +The C source in this package will build on any ANSI C compiler and +function completely and properly on any platform. The included build +system assumes GNU build system and make tools (m4, automake, +autoconf, libtool and gmake). GCC is not required, although GCC is +the most tested compiler. To build using GNU tools, type in the +source directory: + +./autogen.sh +make + +Currently, the source implements playback in pure C on all platforms +except ARM, where a [currently] small amount of assembly (see +asm_arm.h) is used to implement 64 bit math operations and fast LSP +computation. If building on ARM without the benefit of GNU build +system tools, be sure that '_ARM_ASSEM_' is #defined by the build +system if this assembly is desired, else the resulting library will +use whatever 64 bit math builtins the compiler implements. + +No math library is required by this source. No floating point +operations are used at any point in either setup or decode. This +decoder library will properly decode any past, current or future +Vorbis I file or stream. + + ******** + +The build system produces a static and [when supported by the OS] +dynamic library named 'libvorbisidec'. This library exposes an API +nearly identical to the BSD reference library's 'libvorbisfile', +including all the features familiar to users of vorbisfile. This API +is similar enough that the proper header file to include is named +'ivorbisfile.h' [included in the source build directory]. Lower level +libvorbis-style headers and structures are in 'ivorbiscodec.h' +[included in the source build directory]. A simple example program, +ivorbisfile_example.c, can be built with 'make example'. + + ******** + +Detailed Tremor API Documentation begins at doc/index.html + +Monty +xiph.org diff --git a/libs/tremor/Version_script.in b/libs/tremor/Version_script.in new file mode 100644 index 0000000..7f22f2f --- /dev/null +++ b/libs/tremor/Version_script.in @@ -0,0 +1,62 @@ +# +# Export file for libvorbisidec +# +# Only the symbols listed in the global section will be callable from +# applications linking to libvorbisidec. +# + +@PACKAGE@.so.1 +{ + global: + ov_clear; + ov_open; + ov_open_callbacks; + ov_test; + ov_test_callbacks; + ov_test_open; + ov_bitrate; + ov_bitrate_instant; + ov_streams; + ov_seekable; + ov_serialnumber; + ov_raw_total; + ov_pcm_total; + ov_time_total; + ov_raw_seek; + ov_pcm_seek; + ov_pcm_seek_page; + ov_time_seek; + ov_time_seek_page; + ov_raw_tell; + ov_pcm_tell; + ov_time_tell; + ov_info; + ov_comment; + ov_read; + + vorbis_info_init; + vorbis_info_clear; + vorbis_info_blocksize; + vorbis_comment_init; + vorbis_comment_add; + vorbis_comment_add_tag; + vorbis_comment_query; + vorbis_comment_query_count; + vorbis_comment_clear; + vorbis_block_init; + vorbis_block_clear; + vorbis_dsp_clear; + vorbis_synthesis_idheader; + vorbis_synthesis_headerin; + vorbis_synthesis_init; + vorbis_synthesis_restart; + vorbis_synthesis; + vorbis_synthesis_trackonly; + vorbis_synthesis_blockin; + vorbis_synthesis_pcmout; + vorbis_synthesis_read; + vorbis_packet_blocksize; + + local: + *; +}; diff --git a/libs/tremor/asm_arm.h b/libs/tremor/asm_arm.h new file mode 100644 index 0000000..c3bda00 --- /dev/null +++ b/libs/tremor/asm_arm.h @@ -0,0 +1,245 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: arm7 and later wide math functions + + ********************************************************************/ + +#ifdef _ARM_ASSEM_ + +#if !defined(_V_WIDE_MATH) && !defined(_LOW_ACCURACY_) +#define _V_WIDE_MATH + +static inline ogg_int32_t MULT32(ogg_int32_t x, ogg_int32_t y) { + int lo,hi; + asm volatile("smull\t%0, %1, %2, %3" + : "=&r"(lo),"=&r"(hi) + : "%r"(x),"r"(y) + : "cc"); + return(hi); +} + +static inline ogg_int32_t MULT31(ogg_int32_t x, ogg_int32_t y) { + return MULT32(x,y)<<1; +} + +static inline ogg_int32_t MULT31_SHIFT15(ogg_int32_t x, ogg_int32_t y) { + int lo,hi; + asm volatile("smull %0, %1, %2, %3\n\t" + "movs %0, %0, lsr #15\n\t" + "adc %1, %0, %1, lsl #17\n\t" + : "=&r"(lo),"=&r"(hi) + : "%r"(x),"r"(y) + : "cc"); + return(hi); +} + +#define MB() asm volatile ("" : : : "memory") + +static inline void XPROD32(ogg_int32_t a, ogg_int32_t b, + ogg_int32_t t, ogg_int32_t v, + ogg_int32_t *x, ogg_int32_t *y) +{ + int x1, y1, l; + asm( "smull %0, %1, %4, %6\n\t" + "smlal %0, %1, %5, %7\n\t" + "rsb %3, %4, #0\n\t" + "smull %0, %2, %5, %6\n\t" + "smlal %0, %2, %3, %7" + : "=&r" (l), "=&r" (x1), "=&r" (y1), "=r" (a) + : "3" (a), "r" (b), "r" (t), "r" (v) + : "cc" ); + *x = x1; + MB(); + *y = y1; +} + +static inline void XPROD31(ogg_int32_t a, ogg_int32_t b, + ogg_int32_t t, ogg_int32_t v, + ogg_int32_t *x, ogg_int32_t *y) +{ + int x1, y1, l; + asm( "smull %0, %1, %4, %6\n\t" + "smlal %0, %1, %5, %7\n\t" + "rsb %3, %4, #0\n\t" + "smull %0, %2, %5, %6\n\t" + "smlal %0, %2, %3, %7" + : "=&r" (l), "=&r" (x1), "=&r" (y1), "=r" (a) + : "3" (a), "r" (b), "r" (t), "r" (v) + : "cc" ); + *x = x1 << 1; + MB(); + *y = y1 << 1; +} + +static inline void XNPROD31(ogg_int32_t a, ogg_int32_t b, + ogg_int32_t t, ogg_int32_t v, + ogg_int32_t *x, ogg_int32_t *y) +{ + int x1, y1, l; + asm( "rsb %2, %4, #0\n\t" + "smull %0, %1, %3, %5\n\t" + "smlal %0, %1, %2, %6\n\t" + "smull %0, %2, %4, %5\n\t" + "smlal %0, %2, %3, %6" + : "=&r" (l), "=&r" (x1), "=&r" (y1) + : "r" (a), "r" (b), "r" (t), "r" (v) + : "cc" ); + *x = x1 << 1; + MB(); + *y = y1 << 1; +} + +#endif + +#ifndef _V_CLIP_MATH +#define _V_CLIP_MATH + +static inline ogg_int32_t CLIP_TO_15(ogg_int32_t x) { + int tmp; + asm volatile("subs %1, %0, #32768\n\t" + "movpl %0, #0x7f00\n\t" + "orrpl %0, %0, #0xff\n" + "adds %1, %0, #32768\n\t" + "movmi %0, #0x8000" + : "+r"(x),"=r"(tmp) + : + : "cc"); + return(x); +} + +#endif + +#ifndef _V_LSP_MATH_ASM +#define _V_LSP_MATH_ASM + +static inline void lsp_loop_asm(ogg_uint32_t *qip,ogg_uint32_t *pip, + ogg_int32_t *qexpp, + ogg_int32_t *ilsp,ogg_int32_t wi, + ogg_int32_t m){ + + ogg_uint32_t qi=*qip,pi=*pip; + ogg_int32_t qexp=*qexpp; + + asm("mov r0,%3;" + "movs r1,%5,asr#1;" + "add r0,r0,r1,lsl#3;" + "beq 2f;\n" + "1:" + + "ldmdb r0!,{r1,r3};" + "subs r1,r1,%4;" //ilsp[j]-wi + "rsbmi r1,r1,#0;" //labs(ilsp[j]-wi) + "umull %0,r2,r1,%0;" //qi*=labs(ilsp[j]-wi) + + "subs r1,r3,%4;" //ilsp[j+1]-wi + "rsbmi r1,r1,#0;" //labs(ilsp[j+1]-wi) + "umull %1,r3,r1,%1;" //pi*=labs(ilsp[j+1]-wi) + + "cmn r2,r3;" // shift down 16? + "beq 0f;" + "add %2,%2,#16;" + "mov %0,%0,lsr #16;" + "orr %0,%0,r2,lsl #16;" + "mov %1,%1,lsr #16;" + "orr %1,%1,r3,lsl #16;" + "0:" + "cmp r0,%3;\n" + "bhi 1b;\n" + + "2:" + // odd filter assymetry + "ands r0,%5,#1;\n" + "beq 3f;\n" + "add r0,%3,%5,lsl#2;\n" + + "ldr r1,[r0,#-4];\n" + "mov r0,#0x4000;\n" + + "subs r1,r1,%4;\n" //ilsp[j]-wi + "rsbmi r1,r1,#0;\n" //labs(ilsp[j]-wi) + "umull %0,r2,r1,%0;\n" //qi*=labs(ilsp[j]-wi) + "umull %1,r3,r0,%1;\n" //pi*=labs(ilsp[j+1]-wi) + + "cmn r2,r3;\n" // shift down 16? + "beq 3f;\n" + "add %2,%2,#16;\n" + "mov %0,%0,lsr #16;\n" + "orr %0,%0,r2,lsl #16;\n" + "mov %1,%1,lsr #16;\n" + "orr %1,%1,r3,lsl #16;\n" + + //qi=(pi>>shift)*labs(ilsp[j]-wi); + //pi=(qi>>shift)*labs(ilsp[j+1]-wi); + //qexp+=shift; + + //} + + /* normalize to max 16 sig figs */ + "3:" + "mov r2,#0;" + "orr r1,%0,%1;" + "tst r1,#0xff000000;" + "addne r2,r2,#8;" + "movne r1,r1,lsr #8;" + "tst r1,#0x00f00000;" + "addne r2,r2,#4;" + "movne r1,r1,lsr #4;" + "tst r1,#0x000c0000;" + "addne r2,r2,#2;" + "movne r1,r1,lsr #2;" + "tst r1,#0x00020000;" + "addne r2,r2,#1;" + "movne r1,r1,lsr #1;" + "tst r1,#0x00010000;" + "addne r2,r2,#1;" + "mov %0,%0,lsr r2;" + "mov %1,%1,lsr r2;" + "add %2,%2,r2;" + + : "+r"(qi),"+r"(pi),"+r"(qexp) + : "r"(ilsp),"r"(wi),"r"(m) + : "r0","r1","r2","r3","cc"); + + *qip=qi; + *pip=pi; + *qexpp=qexp; +} + +static inline void lsp_norm_asm(ogg_uint32_t *qip,ogg_int32_t *qexpp){ + + ogg_uint32_t qi=*qip; + ogg_int32_t qexp=*qexpp; + + asm("tst %0,#0x0000ff00;" + "moveq %0,%0,lsl #8;" + "subeq %1,%1,#8;" + "tst %0,#0x0000f000;" + "moveq %0,%0,lsl #4;" + "subeq %1,%1,#4;" + "tst %0,#0x0000c000;" + "moveq %0,%0,lsl #2;" + "subeq %1,%1,#2;" + "tst %0,#0x00008000;" + "moveq %0,%0,lsl #1;" + "subeq %1,%1,#1;" + : "+r"(qi),"+r"(qexp) + : + : "cc"); + *qip=qi; + *qexpp=qexp; +} + +#endif +#endif + diff --git a/libs/tremor/autogen.sh b/libs/tremor/autogen.sh new file mode 100755 index 0000000..73c8fca --- /dev/null +++ b/libs/tremor/autogen.sh @@ -0,0 +1,120 @@ +#!/bin/sh +# Run this to set up the build system: configure, makefiles, etc. +# (based on the version in enlightenment's cvs) + +package="vorbisdec" + +olddir=`pwd` +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +cd "$srcdir" +DIE=0 + +echo "checking for autoconf... " +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have autoconf installed to compile $package." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +VERSIONGREP="sed -e s/.*[^0-9\.]\([0-9]\.[0-9]\).*/\1/" +VERSIONMKINT="sed -e s/[^0-9]//" + +# do we need automake? +if test -r Makefile.am; then + AM_OPTIONS=`fgrep AUTOMAKE_OPTIONS Makefile.am` + AM_NEEDED=`echo $AM_OPTIONS | $VERSIONGREP` + if test x"$AM_NEEDED" = "x$AM_OPTIONS"; then + AM_NEEDED="" + fi + if test -z $AM_NEEDED; then + echo -n "checking for automake... " + AUTOMAKE=automake + ACLOCAL=aclocal + if ($AUTOMAKE --version < /dev/null > /dev/null 2>&1); then + echo "yes" + else + echo "no" + AUTOMAKE= + fi + else + echo -n "checking for automake $AM_NEEDED or later... " + for am in automake-$AM_NEEDED automake$AM_NEEDED automake; do + ($am --version < /dev/null > /dev/null 2>&1) || continue + ver=`$am --version < /dev/null | head -n 1 | $VERSIONGREP | $VERSIONMKINT` + verneeded=`echo $AM_NEEDED | $VERSIONMKINT` + if test $ver -ge $verneeded; then + AUTOMAKE=$am + echo $AUTOMAKE + break + fi + done + test -z $AUTOMAKE && echo "no" + echo -n "checking for aclocal $AM_NEEDED or later... " + for ac in aclocal-$AM_NEEDED aclocal$AM_NEEDED aclocal; do + ($ac --version < /dev/null > /dev/null 2>&1) || continue + ver=`$ac --version < /dev/null | head -n 1 | $VERSIONGREP | $VERSIONMKINT` + verneeded=`echo $AM_NEEDED | $VERSIONMKINT` + if test $ver -ge $verneeded; then + ACLOCAL=$ac + echo $ACLOCAL + break + fi + done + test -z $ACLOCAL && echo "no" + fi + test -z $AUTOMAKE || test -z $ACLOCAL && { + echo + echo "You must have automake installed to compile $package." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + exit 1 + } +fi + +echo -n "checking for libtool... " +for LIBTOOLIZE in libtoolize glibtoolize nope; do + ($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 && break +done +if test x$LIBTOOLIZE = xnope; then + echo "nope." + LIBTOOLIZE=libtoolize +else + echo $LIBTOOLIZE +fi +($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have libtool installed to compile $package." + echo "Download the appropriate package for your system," + echo "or get the source from one of the GNU ftp sites" + echo "listed in http://www.gnu.org/order/ftp.html" + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +if test -z "$*"; then + echo "I am going to run ./configure with no arguments - if you wish " + echo "to pass any to it, please specify them on the $0 command line." +fi + +echo "Generating configuration files for $package, please wait...." + +echo " $ACLOCAL $ACLOCAL_FLAGS" +$ACLOCAL $ACLOCAL_FLAGS || exit 1 +echo " $LIBTOOLIZE --automake" +$LIBTOOLIZE --automake || exit 1 +echo " autoheader" +autoheader || exit 1 +echo " $AUTOMAKE --add-missing $AUTOMAKE_FLAGS" +$AUTOMAKE --add-missing $AUTOMAKE_FLAGS || exit 1 +echo " autoconf" +autoconf || exit 1 + +cd $olddir +$srcdir/configure --enable-maintainer-mode "$@" && echo diff --git a/libs/tremor/backends.h b/libs/tremor/backends.h new file mode 100644 index 0000000..5202421 --- /dev/null +++ b/libs/tremor/backends.h @@ -0,0 +1,131 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: backend and mapping structures + + ********************************************************************/ + +/* this is exposed up here because we need it for static modes. + Lookups for each backend aren't exposed because there's no reason + to do so */ + +#ifndef _vorbis_backend_h_ +#define _vorbis_backend_h_ + +#include "codec_internal.h" + +/* this would all be simpler/shorter with templates, but.... */ +/* Transform backend generic *************************************/ + +/* only mdct right now. Flesh it out more if we ever transcend mdct + in the transform domain */ + +/* Floor backend generic *****************************************/ +typedef struct{ + vorbis_info_floor *(*unpack)(vorbis_info *,oggpack_buffer *); + vorbis_look_floor *(*look) (vorbis_dsp_state *,vorbis_info_mode *, + vorbis_info_floor *); + void (*free_info) (vorbis_info_floor *); + void (*free_look) (vorbis_look_floor *); + void *(*inverse1) (struct vorbis_block *,vorbis_look_floor *); + int (*inverse2) (struct vorbis_block *,vorbis_look_floor *, + void *buffer,ogg_int32_t *); +} vorbis_func_floor; + +typedef struct{ + int order; + long rate; + long barkmap; + + int ampbits; + int ampdB; + + int numbooks; /* <= 16 */ + int books[16]; + +} vorbis_info_floor0; + +#define VIF_POSIT 63 +#define VIF_CLASS 16 +#define VIF_PARTS 31 +typedef struct{ + int partitions; /* 0 to 31 */ + int partitionclass[VIF_PARTS]; /* 0 to 15 */ + + int class_dim[VIF_CLASS]; /* 1 to 8 */ + int class_subs[VIF_CLASS]; /* 0,1,2,3 (bits: 1< +#include +#include +#include +#include "ivorbiscodec.h" +#include "codec_internal.h" + +#include "window.h" +#include "registry.h" +#include "misc.h" + +static int ilog(unsigned int v){ + int ret=0; + if(v)--v; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +/* pcm accumulator examples (not exhaustive): + + <-------------- lW ----------------> + <--------------- W ----------------> +: .....|..... _______________ | +: .''' | '''_--- | |\ | +:.....''' |_____--- '''......| | \_______| +:.................|__________________|_______|__|______| + |<------ Sl ------>| > Sr < |endW + |beginSl |endSl | |endSr + |beginW |endlW |beginSr + + + |< lW >| + <--------------- W ----------------> + | | .. ______________ | + | | ' `/ | ---_ | + |___.'___/`. | ---_____| + |_______|__|_______|_________________| + | >|Sl|< |<------ Sr ----->|endW + | | |endSl |beginSr |endSr + |beginW | |endlW + mult[0] |beginSl mult[n] + + <-------------- lW -----------------> + |<--W-->| +: .............. ___ | | +: .''' |`/ \ | | +:.....''' |/`....\|...| +:.........................|___|___|___| + |Sl |Sr |endW + | | |endSr + | |beginSr + | |endSl + |beginSl + |beginW +*/ + +/* block abstraction setup *********************************************/ + +#ifndef WORD_ALIGN +#define WORD_ALIGN 8 +#endif + +int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb){ + memset(vb,0,sizeof(*vb)); + vb->vd=v; + vb->localalloc=0; + vb->localstore=NULL; + + return(0); +} + +void *_vorbis_block_alloc(vorbis_block *vb,long bytes){ + bytes=(bytes+(WORD_ALIGN-1)) & ~(WORD_ALIGN-1); + if(bytes+vb->localtop>vb->localalloc){ + /* can't just _ogg_realloc... there are outstanding pointers */ + if(vb->localstore){ + struct alloc_chain *link=(struct alloc_chain *)_ogg_malloc(sizeof(*link)); + vb->totaluse+=vb->localtop; + link->next=vb->reap; + link->ptr=vb->localstore; + vb->reap=link; + } + /* highly conservative */ + vb->localalloc=bytes; + vb->localstore=_ogg_malloc(vb->localalloc); + vb->localtop=0; + } + { + void *ret=(void *)(((char *)vb->localstore)+vb->localtop); + vb->localtop+=bytes; + return ret; + } +} + +/* reap the chain, pull the ripcord */ +void _vorbis_block_ripcord(vorbis_block *vb){ + /* reap the chain */ + struct alloc_chain *reap=vb->reap; + while(reap){ + struct alloc_chain *next=reap->next; + _ogg_free(reap->ptr); + memset(reap,0,sizeof(*reap)); + _ogg_free(reap); + reap=next; + } + /* consolidate storage */ + if(vb->totaluse){ + vb->localstore=_ogg_realloc(vb->localstore,vb->totaluse+vb->localalloc); + vb->localalloc+=vb->totaluse; + vb->totaluse=0; + } + + /* pull the ripcord */ + vb->localtop=0; + vb->reap=NULL; +} + +int vorbis_block_clear(vorbis_block *vb){ + _vorbis_block_ripcord(vb); + if(vb->localstore)_ogg_free(vb->localstore); + + memset(vb,0,sizeof(*vb)); + return(0); +} + +static int _vds_init(vorbis_dsp_state *v,vorbis_info *vi){ + int i; + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + private_state *b=NULL; + + if(ci==NULL) return 1; + + memset(v,0,sizeof(*v)); + b=(private_state *)(v->backend_state=_ogg_calloc(1,sizeof(*b))); + + v->vi=vi; + b->modebits=ilog(ci->modes); + + /* Vorbis I uses only window type 0 */ + b->window[0]=_vorbis_window(0,ci->blocksizes[0]/2); + b->window[1]=_vorbis_window(0,ci->blocksizes[1]/2); + + /* finish the codebooks */ + if(!ci->fullbooks){ + ci->fullbooks=(codebook *)_ogg_calloc(ci->books,sizeof(*ci->fullbooks)); + for(i=0;ibooks;i++){ + if(ci->book_param[i]==NULL) + goto abort_books; + if(vorbis_book_init_decode(ci->fullbooks+i,ci->book_param[i])) + goto abort_books; + /* decode codebooks are now standalone after init */ + vorbis_staticbook_destroy(ci->book_param[i]); + ci->book_param[i]=NULL; + } + } + + v->pcm_storage=ci->blocksizes[1]; + v->pcm=(ogg_int32_t **)_ogg_malloc(vi->channels*sizeof(*v->pcm)); + v->pcmret=(ogg_int32_t **)_ogg_malloc(vi->channels*sizeof(*v->pcmret)); + for(i=0;ichannels;i++) + v->pcm[i]=(ogg_int32_t *)_ogg_calloc(v->pcm_storage,sizeof(*v->pcm[i])); + + /* all 1 (large block) or 0 (small block) */ + /* explicitly set for the sake of clarity */ + v->lW=0; /* previous window size */ + v->W=0; /* current window size */ + + /* initialize all the mapping/backend lookups */ + b->mode=(vorbis_look_mapping **)_ogg_calloc(ci->modes,sizeof(*b->mode)); + for(i=0;imodes;i++){ + int mapnum=ci->mode_param[i]->mapping; + int maptype=ci->map_type[mapnum]; + b->mode[i]=_mapping_P[maptype]->look(v,ci->mode_param[i], + ci->map_param[mapnum]); + } + return 0; +abort_books: + for(i=0;ibooks;i++){ + if(ci->book_param[i]!=NULL){ + vorbis_staticbook_destroy(ci->book_param[i]); + ci->book_param[i]=NULL; + } + } + vorbis_dsp_clear(v); + return -1; +} + +int vorbis_synthesis_restart(vorbis_dsp_state *v){ + vorbis_info *vi=v->vi; + codec_setup_info *ci; + + if(!v->backend_state)return -1; + if(!vi)return -1; + ci=vi->codec_setup; + if(!ci)return -1; + + v->centerW=ci->blocksizes[1]/2; + v->pcm_current=v->centerW; + + v->pcm_returned=-1; + v->granulepos=-1; + v->sequence=-1; + ((private_state *)(v->backend_state))->sample_count=-1; + + return(0); +} + +int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi){ + if(_vds_init(v,vi))return 1; + vorbis_synthesis_restart(v); + + return 0; +} + +void vorbis_dsp_clear(vorbis_dsp_state *v){ + int i; + if(v){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=(codec_setup_info *)(vi?vi->codec_setup:NULL); + private_state *b=(private_state *)v->backend_state; + + if(v->pcm){ + for(i=0;ichannels;i++) + if(v->pcm[i])_ogg_free(v->pcm[i]); + _ogg_free(v->pcm); + if(v->pcmret)_ogg_free(v->pcmret); + } + + /* free mode lookups; these are actually vorbis_look_mapping structs */ + if(ci){ + for(i=0;imodes;i++){ + int mapnum=ci->mode_param[i]->mapping; + int maptype=ci->map_type[mapnum]; + if(b && b->mode)_mapping_P[maptype]->free_look(b->mode[i]); + } + } + + if(b){ + if(b->mode)_ogg_free(b->mode); + _ogg_free(b); + } + + memset(v,0,sizeof(*v)); + } +} + +/* Unlike in analysis, the window is only partially applied for each + block. The time domain envelope is not yet handled at the point of + calling (as it relies on the previous block). */ + +int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + private_state *b=v->backend_state; + int i,j; + + if(v->pcm_current>v->pcm_returned && v->pcm_returned!=-1)return(OV_EINVAL); + + v->lW=v->W; + v->W=vb->W; + v->nW=-1; + + if((v->sequence==-1)|| + (v->sequence+1 != vb->sequence)){ + v->granulepos=-1; /* out of sequence; lose count */ + b->sample_count=-1; + } + + v->sequence=vb->sequence; + + if(vb->pcm){ /* no pcm to process if vorbis_synthesis_trackonly + was called on block */ + int n=ci->blocksizes[v->W]/2; + int n0=ci->blocksizes[0]/2; + int n1=ci->blocksizes[1]/2; + + int thisCenter; + int prevCenter; + + if(v->centerW){ + thisCenter=n1; + prevCenter=0; + }else{ + thisCenter=0; + prevCenter=n1; + } + + /* v->pcm is now used like a two-stage double buffer. We don't want + to have to constantly shift *or* adjust memory usage. Don't + accept a new block until the old is shifted out */ + + /* overlap/add PCM */ + + for(j=0;jchannels;j++){ + /* the overlap/add section */ + if(v->lW){ + if(v->W){ + /* large/large */ + ogg_int32_t *pcm=v->pcm[j]+prevCenter; + ogg_int32_t *p=vb->pcm[j]; + for(i=0;ipcm[j]+prevCenter+n1/2-n0/2; + ogg_int32_t *p=vb->pcm[j]; + for(i=0;iW){ + /* small/large */ + ogg_int32_t *pcm=v->pcm[j]+prevCenter; + ogg_int32_t *p=vb->pcm[j]+n1/2-n0/2; + for(i=0;ipcm[j]+prevCenter; + ogg_int32_t *p=vb->pcm[j]; + for(i=0;ipcm[j]+thisCenter; + ogg_int32_t *p=vb->pcm[j]+n; + for(i=0;icenterW) + v->centerW=0; + else + v->centerW=n1; + + /* deal with initial packet state; we do this using the explicit + pcm_returned==-1 flag otherwise we're sensitive to first block + being short or long */ + + if(v->pcm_returned==-1){ + v->pcm_returned=thisCenter; + v->pcm_current=thisCenter; + }else{ + v->pcm_returned=prevCenter; + v->pcm_current=prevCenter+ + ci->blocksizes[v->lW]/4+ + ci->blocksizes[v->W]/4; + } + + } + + /* track the frame number... This is for convenience, but also + making sure our last packet doesn't end with added padding. If + the last packet is partial, the number of samples we'll have to + return will be past the vb->granulepos. + + This is not foolproof! It will be confused if we begin + decoding at the last page after a seek or hole. In that case, + we don't have a starting point to judge where the last frame + is. For this reason, vorbisfile will always try to make sure + it reads the last two marked pages in proper sequence */ + + if(b->sample_count==-1){ + b->sample_count=0; + }else{ + b->sample_count+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4; + } + + if(v->granulepos==-1){ + if(vb->granulepos!=-1){ /* only set if we have a position to set to */ + + v->granulepos=vb->granulepos; + + /* is this a short page? */ + if(b->sample_count>v->granulepos){ + /* corner case; if this is both the first and last audio page, + then spec says the end is cut, not beginning */ + long extra=b->sample_count-vb->granulepos; + + /* we use ogg_int64_t for granule positions because a + uint64 isn't universally available. Unfortunately, + that means granposes can be 'negative' and result in + extra being negative */ + if(extra<0) + extra=0; + + if(vb->eofflag){ + /* trim the end */ + /* no preceeding granulepos; assume we started at zero (we'd + have to in a short single-page stream) */ + /* granulepos could be -1 due to a seek, but that would result + in a long coun`t, not short count */ + + /* Guard against corrupt/malicious frames that set EOP and + a backdated granpos; don't rewind more samples than we + actually have */ + if(extra > v->pcm_current - v->pcm_returned) + extra = v->pcm_current - v->pcm_returned; + + v->pcm_current-=extra; + }else{ + /* trim the beginning */ + v->pcm_returned+=extra; + if(v->pcm_returned>v->pcm_current) + v->pcm_returned=v->pcm_current; + } + + } + + } + }else{ + v->granulepos+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4; + if(vb->granulepos!=-1 && v->granulepos!=vb->granulepos){ + + if(v->granulepos>vb->granulepos){ + long extra=v->granulepos-vb->granulepos; + + if(extra) + if(vb->eofflag){ + /* partial last frame. Strip the extra samples off */ + + /* Guard against corrupt/malicious frames that set EOP and + a backdated granpos; don't rewind more samples than we + actually have */ + if(extra > v->pcm_current - v->pcm_returned) + extra = v->pcm_current - v->pcm_returned; + + /* we use ogg_int64_t for granule positions because a + uint64 isn't universally available. Unfortunately, + that means granposes can be 'negative' and result in + extra being negative */ + if(extra<0) + extra=0; + + v->pcm_current-=extra; + + } /* else {Shouldn't happen *unless* the bitstream is out of + spec. Either way, believe the bitstream } */ + } /* else {Shouldn't happen *unless* the bitstream is out of + spec. Either way, believe the bitstream } */ + v->granulepos=vb->granulepos; + } + } + + /* Update, cleanup */ + + if(vb->eofflag)v->eofflag=1; + return(0); +} + +/* pcm==NULL indicates we just want the pending samples, no more */ +int vorbis_synthesis_pcmout(vorbis_dsp_state *v,ogg_int32_t ***pcm){ + vorbis_info *vi=v->vi; + if(v->pcm_returned>-1 && v->pcm_returnedpcm_current){ + if(pcm){ + int i; + for(i=0;ichannels;i++) + v->pcmret[i]=v->pcm[i]+v->pcm_returned; + *pcm=v->pcmret; + } + return(v->pcm_current-v->pcm_returned); + } + return(0); +} + +int vorbis_synthesis_read(vorbis_dsp_state *v,int bytes){ + if(bytes && v->pcm_returned+bytes>v->pcm_current)return(OV_EINVAL); + v->pcm_returned+=bytes; + return(0); +} + diff --git a/libs/tremor/block.h b/libs/tremor/block.h new file mode 100644 index 0000000..5e19354 --- /dev/null +++ b/libs/tremor/block.h @@ -0,0 +1,24 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2008 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: shared block functions + + ********************************************************************/ + +#ifndef _V_BLOCK_ +#define _V_BLOCK_ + +extern void _vorbis_block_ripcord(vorbis_block *vb); +extern void *_vorbis_block_alloc(vorbis_block *vb,long bytes); + +#endif diff --git a/libs/tremor/codebook.c b/libs/tremor/codebook.c new file mode 100644 index 0000000..f8b7983 --- /dev/null +++ b/libs/tremor/codebook.c @@ -0,0 +1,391 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: basic codebook pack/unpack/code/decode operations + + ********************************************************************/ + +#include +#include +#include +#include +#include "ivorbiscodec.h" +#include "codebook.h" +#include "misc.h" + +/* unpacks a codebook from the packet buffer into the codebook struct, + readies the codebook auxiliary structures for decode *************/ +static_codebook *vorbis_staticbook_unpack(oggpack_buffer *opb){ + long i,j; + static_codebook *s=_ogg_calloc(1,sizeof(*s)); + + /* make sure alignment is correct */ + if(oggpack_read(opb,24)!=0x564342)goto _eofout; + + /* first the basic parameters */ + s->dim=oggpack_read(opb,16); + s->entries=oggpack_read(opb,24); + if(s->entries==-1)goto _eofout; + + if(_ilog(s->dim)+_ilog(s->entries)>24)goto _eofout; + + /* codeword ordering.... length ordered or unordered? */ + switch((int)oggpack_read(opb,1)){ + case 0:{ + long unused; + /* allocated but unused entries? */ + unused=oggpack_read(opb,1); + if((s->entries*(unused?1:5)+7)>>3>opb->storage-oggpack_bytes(opb)) + goto _eofout; + /* unordered */ + s->lengthlist=(long *)_ogg_malloc(sizeof(*s->lengthlist)*s->entries); + + /* allocated but unused entries? */ + if(unused){ + /* yes, unused entries */ + + for(i=0;ientries;i++){ + if(oggpack_read(opb,1)){ + long num=oggpack_read(opb,5); + if(num==-1)goto _eofout; + s->lengthlist[i]=num+1; + }else + s->lengthlist[i]=0; + } + }else{ + /* all entries used; no tagging */ + for(i=0;ientries;i++){ + long num=oggpack_read(opb,5); + if(num==-1)goto _eofout; + s->lengthlist[i]=num+1; + } + } + + break; + } + case 1: + /* ordered */ + { + long length=oggpack_read(opb,5)+1; + if(length==0)goto _eofout; + s->lengthlist=(long *)_ogg_malloc(sizeof(*s->lengthlist)*s->entries); + + for(i=0;ientries;){ + long num=oggpack_read(opb,_ilog(s->entries-i)); + if(num==-1)goto _eofout; + if(length>32 || num>s->entries-i || + (num>0 && (num-1)>>(length>>1)>>((length+1)>>1))>0){ + goto _errout; + } + for(j=0;jlengthlist[i]=length; + length++; + } + } + break; + default: + /* EOF */ + goto _eofout; + } + + /* Do we have a mapping to unpack? */ + switch((s->maptype=oggpack_read(opb,4))){ + case 0: + /* no mapping */ + break; + case 1: case 2: + /* implicitly populated value mapping */ + /* explicitly populated value mapping */ + + s->q_min=oggpack_read(opb,32); + s->q_delta=oggpack_read(opb,32); + s->q_quant=oggpack_read(opb,4)+1; + s->q_sequencep=oggpack_read(opb,1); + if(s->q_sequencep==-1)goto _eofout; + + { + int quantvals=0; + switch(s->maptype){ + case 1: + quantvals=(s->dim==0?0:_book_maptype1_quantvals(s)); + break; + case 2: + quantvals=s->entries*s->dim; + break; + } + + /* quantized values */ + if((quantvals*s->q_quant+7)>>3>opb->storage-oggpack_bytes(opb)) + goto _eofout; + s->quantlist=(long *)_ogg_malloc(sizeof(*s->quantlist)*quantvals); + for(i=0;iquantlist[i]=oggpack_read(opb,s->q_quant); + + if(quantvals&&s->quantlist[quantvals-1]==-1)goto _eofout; + } + break; + default: + goto _errout; + } + + /* all set */ + return(s); + + _errout: + _eofout: + vorbis_staticbook_destroy(s); + return(NULL); +} + +/* the 'eliminate the decode tree' optimization actually requires the + codewords to be MSb first, not LSb. This is an annoying inelegancy + (and one of the first places where carefully thought out design + turned out to be wrong; Vorbis II and future Ogg codecs should go + to an MSb bitpacker), but not actually the huge hit it appears to + be. The first-stage decode table catches most words so that + bitreverse is not in the main execution path. */ + +static ogg_uint32_t bitreverse(ogg_uint32_t x){ + x= ((x>>16)&0x0000ffff) | ((x<<16)&0xffff0000); + x= ((x>> 8)&0x00ff00ff) | ((x<< 8)&0xff00ff00); + x= ((x>> 4)&0x0f0f0f0f) | ((x<< 4)&0xf0f0f0f0); + x= ((x>> 2)&0x33333333) | ((x<< 2)&0xcccccccc); + return((x>> 1)&0x55555555) | ((x<< 1)&0xaaaaaaaa); +} + +STIN long decode_packed_entry_number(codebook *book, + oggpack_buffer *b){ + int read=book->dec_maxlength; + long lo,hi; + long lok = oggpack_look(b,book->dec_firsttablen); + + if (lok >= 0) { + long entry = book->dec_firsttable[lok]; + if(entry&0x80000000UL){ + lo=(entry>>15)&0x7fff; + hi=book->used_entries-(entry&0x7fff); + }else{ + oggpack_adv(b, book->dec_codelengths[entry-1]); + return(entry-1); + } + }else{ + lo=0; + hi=book->used_entries; + } + + lok = oggpack_look(b, read); + + while(lok<0 && read>1) + lok = oggpack_look(b, --read); + + if(lok<0){ + oggpack_adv(b,1); /* force eop */ + return -1; + } + + /* bisect search for the codeword in the ordered list */ + { + ogg_uint32_t testword=bitreverse((ogg_uint32_t)lok); + + while(hi-lo>1){ + long p=(hi-lo)>>1; + long test=book->codelist[lo+p]>testword; + lo+=p&(test-1); + hi-=p&(-test); + } + + if(book->dec_codelengths[lo]<=read){ + oggpack_adv(b, book->dec_codelengths[lo]); + return(lo); + } + } + + oggpack_adv(b, read+1); + return(-1); +} + +/* Decode side is specced and easier, because we don't need to find + matches using different criteria; we simply read and map. There are + two things we need to do 'depending': + + We may need to support interleave. We don't really, but it's + convenient to do it here rather than rebuild the vector later. + + Cascades may be additive or multiplicitive; this is not inherent in + the codebook, but set in the code using the codebook. Like + interleaving, it's easiest to do it here. + addmul==0 -> declarative (set the value) + addmul==1 -> additive + addmul==2 -> multiplicitive */ + +/* returns the [original, not compacted] entry number or -1 on eof *********/ +long vorbis_book_decode(codebook *book, oggpack_buffer *b){ + if(book->used_entries>0){ + long packed_entry=decode_packed_entry_number(book,b); + if(packed_entry>=0) + return(book->dec_index[packed_entry]); + } + + /* if there's no dec_index, the codebook unpacking isn't collapsed */ + return(-1); +} + +/* returns 0 on OK or -1 on eof *************************************/ +/* decode vector / dim granularity gaurding is done in the upper layer */ +long vorbis_book_decodevs_add(codebook *book,ogg_int32_t *a, + oggpack_buffer *b,int n,int point){ + if(book->used_entries>0){ + int step=n/book->dim; + long *entry = (long *)alloca(sizeof(*entry)*step); + ogg_int32_t **t = (ogg_int32_t **)alloca(sizeof(*t)*step); + int i,j,o; + int shift=point-book->binarypoint; + + if(shift>=0){ + for (i = 0; i < step; i++) { + entry[i]=decode_packed_entry_number(book,b); + if(entry[i]==-1)return(-1); + t[i] = book->valuelist+entry[i]*book->dim; + } + for(i=0,o=0;idim;i++,o+=step) + for (j=0;j>shift; + }else{ + for (i = 0; i < step; i++) { + entry[i]=decode_packed_entry_number(book,b); + if(entry[i]==-1)return(-1); + t[i] = book->valuelist+entry[i]*book->dim; + } + for(i=0,o=0;idim;i++,o+=step) + for (j=0;jused_entries>0){ + int i,j,entry; + ogg_int32_t *t; + int shift=point-book->binarypoint; + + if(shift>=0){ + for(i=0;ivaluelist+entry*book->dim; + for (j=0;jdim;) + a[i++]+=t[j++]>>shift; + } + }else{ + for(i=0;ivaluelist+entry*book->dim; + for (j=0;jdim;) + a[i++]+=t[j++]<<-shift; + } + } + } + return(0); +} + +/* unlike the others, we guard against n not being an integer number + of internally rather than in the upper layer (called only by + floor0) */ +long vorbis_book_decodev_set(codebook *book,ogg_int32_t *a, + oggpack_buffer *b,int n,int point){ + if(book->used_entries>0){ + int i,j,entry; + ogg_int32_t *t; + int shift=point-book->binarypoint; + + if(shift>=0){ + + for(i=0;ivaluelist+entry*book->dim; + for (j=0;idim;){ + a[i++]=t[j++]>>shift; + } + } + }else{ + + for(i=0;ivaluelist+entry*book->dim; + for (j=0;idim;){ + a[i++]=t[j++]<<-shift; + } + } + } + }else{ + + int i,j; + for(i=0;iused_entries>0){ + long i,j,entry; + int chptr=0; + int shift=point-book->binarypoint; + + if(shift>=0){ + + for(i=offset;ivaluelist+entry*book->dim; + for (j=0;jdim;j++){ + a[chptr++][i]+=t[j]>>shift; + if(chptr==ch){ + chptr=0; + i++; + } + } + } + } + }else{ + + for(i=offset;ivaluelist+entry*book->dim; + for (j=0;jdim;j++){ + a[chptr++][i]+=t[j]<<-shift; + if(chptr==ch){ + chptr=0; + i++; + } + } + } + } + } + } + return(0); +} diff --git a/libs/tremor/codebook.h b/libs/tremor/codebook.h new file mode 100644 index 0000000..bb13942 --- /dev/null +++ b/libs/tremor/codebook.h @@ -0,0 +1,101 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: basic shared codebook operations + + ********************************************************************/ + +#ifndef _V_CODEBOOK_H_ +#define _V_CODEBOOK_H_ + +#include + +/* This structure encapsulates huffman and VQ style encoding books; it + doesn't do anything specific to either. + + valuelist/quantlist are nonNULL (and q_* significant) only if + there's entry->value mapping to be done. + + If encode-side mapping must be done (and thus the entry needs to be + hunted), the auxiliary encode pointer will point to a decision + tree. This is true of both VQ and huffman, but is mostly useful + with VQ. + +*/ + +typedef struct static_codebook{ + long dim; /* codebook dimensions (elements per vector) */ + long entries; /* codebook entries */ + long *lengthlist; /* codeword lengths in bits */ + + /* mapping ***************************************************************/ + int maptype; /* 0=none + 1=implicitly populated values from map column + 2=listed arbitrary values */ + + /* The below does a linear, single monotonic sequence mapping. */ + long q_min; /* packed 32 bit float; quant value 0 maps to minval */ + long q_delta; /* packed 32 bit float; val 1 - val 0 == delta */ + int q_quant; /* bits: 0 < quant <= 16 */ + int q_sequencep; /* bitflag */ + + long *quantlist; /* map == 1: (int)(entries^(1/dim)) element column map + map == 2: list of dim*entries quantized entry vals + */ +} static_codebook; + +typedef struct codebook{ + long dim; /* codebook dimensions (elements per vector) */ + long entries; /* codebook entries */ + long used_entries; /* populated codebook entries */ + + /* the below are ordered by bitreversed codeword and only used + entries are populated */ + int binarypoint; + ogg_int32_t *valuelist; /* list of dim*entries actual entry values */ + ogg_uint32_t *codelist; /* list of bitstream codewords for each entry */ + + int *dec_index; + char *dec_codelengths; + ogg_uint32_t *dec_firsttable; + int dec_firsttablen; + int dec_maxlength; + + long q_min; /* packed 32 bit float; quant value 0 maps to minval */ + long q_delta; /* packed 32 bit float; val 1 - val 0 == delta */ + +} codebook; + +extern void vorbis_staticbook_destroy(static_codebook *b); +extern int vorbis_book_init_decode(codebook *dest,const static_codebook *source); + +extern void vorbis_book_clear(codebook *b); +extern long _book_maptype1_quantvals(const static_codebook *b); + +extern static_codebook *vorbis_staticbook_unpack(oggpack_buffer *b); + +extern long vorbis_book_decode(codebook *book, oggpack_buffer *b); +extern long vorbis_book_decodevs_add(codebook *book, ogg_int32_t *a, + oggpack_buffer *b,int n,int point); +extern long vorbis_book_decodev_set(codebook *book, ogg_int32_t *a, + oggpack_buffer *b,int n,int point); +extern long vorbis_book_decodev_add(codebook *book, ogg_int32_t *a, + oggpack_buffer *b,int n,int point); +extern long vorbis_book_decodevv_add(codebook *book, ogg_int32_t **a, + long off,int ch, + oggpack_buffer *b,int n,int point); + +extern int _ilog(unsigned int v); + + +#endif diff --git a/libs/tremor/codec_internal.h b/libs/tremor/codec_internal.h new file mode 100644 index 0000000..3ca7f54 --- /dev/null +++ b/libs/tremor/codec_internal.h @@ -0,0 +1,92 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: libvorbis codec headers + + ********************************************************************/ + +#ifndef _V_CODECI_H_ +#define _V_CODECI_H_ + +#include "codebook.h" + +typedef void vorbis_look_mapping; +typedef void vorbis_look_floor; +typedef void vorbis_look_residue; +typedef void vorbis_look_transform; + +/* mode ************************************************************/ +typedef struct { + int blockflag; + int windowtype; + int transformtype; + int mapping; +} vorbis_info_mode; + +typedef void vorbis_info_floor; +typedef void vorbis_info_residue; +typedef void vorbis_info_mapping; + +typedef struct private_state { + /* local lookup storage */ + const void *window[2]; + + /* backend lookups are tied to the mode, not the backend or naked mapping */ + int modebits; + vorbis_look_mapping **mode; + + ogg_int64_t sample_count; + +} private_state; + +/* codec_setup_info contains all the setup information specific to the + specific compression/decompression mode in progress (eg, + psychoacoustic settings, channel setup, options, codebook + etc). +*********************************************************************/ + +typedef struct codec_setup_info { + + /* Vorbis supports only short and long blocks, but allows the + encoder to choose the sizes */ + + long blocksizes[2]; + + /* modes are the primary means of supporting on-the-fly different + blocksizes, different channel mappings (LR or M/A), + different residue backends, etc. Each mode consists of a + blocksize flag and a mapping (along with the mapping setup */ + + int modes; + int maps; + int times; + int floors; + int residues; + int books; + + vorbis_info_mode *mode_param[64]; + int map_type[64]; + vorbis_info_mapping *map_param[64]; + int time_type[64]; + int floor_type[64]; + vorbis_info_floor *floor_param[64]; + int residue_type[64]; + vorbis_info_residue *residue_param[64]; + static_codebook *book_param[256]; + codebook *fullbooks; + + int passlimit[32]; /* iteration limit per couple/quant pass */ + int coupling_passes; +} codec_setup_info; + +#endif diff --git a/libs/tremor/config_types.h b/libs/tremor/config_types.h new file mode 100644 index 0000000..1fdcb27 --- /dev/null +++ b/libs/tremor/config_types.h @@ -0,0 +1,25 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: #ifdef jail to whip a few platforms into the UNIX ideal. + + ********************************************************************/ +#ifndef _OS_CVTYPES_H +#define _OS_CVTYPES_H + +typedef long long ogg_int64_t; +typedef int ogg_int32_t; +typedef unsigned int ogg_uint32_t; +typedef short ogg_int16_t; + +#endif diff --git a/libs/tremor/configure.in b/libs/tremor/configure.in new file mode 100644 index 0000000..e7f5690 --- /dev/null +++ b/libs/tremor/configure.in @@ -0,0 +1,146 @@ +dnl Process this file with autoconf to produce a configure script + +dnl ------------------------------------------------ +dnl Initialization and Versioning +dnl ------------------------------------------------ + +AC_INIT(mdct.c) + +AC_CANONICAL_HOST +AC_CANONICAL_TARGET + +AM_CONFIG_HEADER([config.h]) + +AM_INIT_AUTOMAKE(libvorbisidec,1.2.1) + +dnl AM_MAINTAINER_MODE only provides the option to configure to enable it +AM_MAINTAINER_MODE + +dnl Library versioning + +V_LIB_CURRENT=1 +V_LIB_REVISION=3 +V_LIB_AGE=0 +AC_SUBST(V_LIB_CURRENT) +AC_SUBST(V_LIB_REVISION) +AC_SUBST(V_LIB_AGE) + +dnl -------------------------------------------------- +dnl Check for programs +dnl -------------------------------------------------- + +dnl save $CFLAGS since AC_PROG_CC likes to insert "-g -O2" +dnl if $CFLAGS is blank +cflags_save="$CFLAGS" +AC_PROG_CC +AC_PROG_CPP +CFLAGS="$cflags_save" + +AM_PROG_LIBTOOL + +dnl -------------------------------------------------- +dnl Set build flags based on environment +dnl -------------------------------------------------- + +dnl Set some target options + +cflags_save="$CFLAGS" +ldflags_save="$LDFLAGS" +if test -z "$GCC"; then + case $host in + arm-*-*) + DEBUG="-g -D_ARM_ASSEM_" + CFLAGS="-O -D_ARM_ASSEM_" + PROFILE="-p -g -O -D_ARM_ASSEM_" ;; + *) + DEBUG="-g" + CFLAGS="-O" + PROFILE="-g -p" ;; + esac +else + + case $host in + arm-*-*) + DEBUG="-g -Wall -D__NO_MATH_INLINES -fsigned-char -D_ARM_ASSEM_" + CFLAGS="-O2 -D_ARM_ASSEM_ -fsigned-char" + PROFILE="-W -pg -g -O2 -D_ARM_ASSEM_ -fsigned-char -fno-inline-functions";; + + *) + DEBUG="-g -Wall -D__NO_MATH_INLINES -fsigned-char" + CFLAGS="-O2 -Wall -fsigned-char" + PROFILE="-Wall -pg -g -O2 -fsigned-char -fno-inline-functions";; + esac +fi +CFLAGS="$CFLAGS $cflags_save -D_REENTRANT" +LDFLAGS="$LDFLAGS $ldflags_save" + + +# Test whenever ld supports -version-script +AC_PROG_LD +AC_PROG_LD_GNU +if test "x$lt_cv_prog_gnu_ld" = "xyes"; then + SHLIB_VERSION_ARG="-Wl,--version-script=Version_script" + LDFLAGS="$LDFLAGS $SHLIB_VERSION_ARG" +fi + +dnl -------------------------------------------------- +dnl Options +dnl -------------------------------------------------- + +AC_ARG_ENABLE( + low-accuracy, + [ --enable-low-accuracy enable 32 bit only multiply operations], + CFLAGS="$CFLAGS -D_LOW_ACCURACY_" +) + +dnl -------------------------------------------------- +dnl Check for headers +dnl -------------------------------------------------- + +AC_CHECK_HEADER(memory.h,CFLAGS="$CFLAGS -DUSE_MEMORY_H",:) + +dnl -------------------------------------------------- +dnl Check for typedefs, structures, etc +dnl -------------------------------------------------- + +dnl none + +dnl -------------------------------------------------- +dnl Check for libraries +dnl -------------------------------------------------- + +PKG_PROG_PKG_CONFIG + +HAVE_OGG=no +if test "x$PKG_CONFIG" != "x" +then + PKG_CHECK_MODULES(OGG, ogg >= 1.0, HAVE_OGG=yes, HAVE_OGG=no) +fi +if test "x$HAVE_OGG" = "xno" +then + dnl fall back to the old school test + XIPH_PATH_OGG(, AC_MSG_ERROR(must have Ogg installed!)) + libs_save=$LIBS + LIBS="$OGG_LIBS" + AC_CHECK_FUNC(oggpack_writealign, , AC_MSG_ERROR(Ogg >= 1.0 required !)) + LIBS=$libs_save +fi + +dnl -------------------------------------------------- +dnl Check for library functions +dnl -------------------------------------------------- + +AC_FUNC_ALLOCA +AC_FUNC_MEMCMP + +dnl -------------------------------------------------- +dnl Do substitutions +dnl -------------------------------------------------- + +LIBS="$LIBS" + +AC_SUBST(LIBS) +AC_SUBST(DEBUG) +AC_SUBST(PROFILE) + +AC_OUTPUT(Makefile Version_script vorbisidec.pc) diff --git a/libs/tremor/debian/Makefile.am b/libs/tremor/debian/Makefile.am new file mode 100644 index 0000000..45a0f01 --- /dev/null +++ b/libs/tremor/debian/Makefile.am @@ -0,0 +1,6 @@ +## Process this file with automake to produce Makefile.in + +AUTOMAKE_OPTIONS = foreign + +EXTRA_DIST = changelog control copyright libvorbisidec1.install\ + libvorbisidec-dev.install rules diff --git a/libs/tremor/debian/changelog b/libs/tremor/debian/changelog new file mode 100644 index 0000000..0cb4935 --- /dev/null +++ b/libs/tremor/debian/changelog @@ -0,0 +1,9 @@ +libvorbisidec (1.2.0-1) unstable; urgency=low + + * Initial Release. + + -- Christopher L Cheney Wed, 09 Oct 2002 22:00:00 -0500 + +Local variables: +mode: debian-changelog +End: diff --git a/libs/tremor/debian/control b/libs/tremor/debian/control new file mode 100644 index 0000000..f286e91 --- /dev/null +++ b/libs/tremor/debian/control @@ -0,0 +1,22 @@ +Source: libvorbisidec +Section: libs +Priority: optional +Maintainer: Christopher L Cheney +Build-Depends: autotools-dev, debhelper (>> 4.0.18), devscripts, gawk +Standards-Version: 3.5.7.0 + +Package: libvorbisidec1 +Architecture: any +Section: libs +Depends: ${shlibs:Depends} +Description: Ogg Bitstream Library + Libogg is a library for manipulating ogg bitstreams. It handles + both making ogg bitstreams and getting packets from ogg bitstreams. + +Package: libvorbisidec-dev +Architecture: any +Section: devel +Depends: libvorbisidec1 (= ${Source-Version}), libc6-dev +Description: Ogg Bitstream Library Development + The libogg-dev package contains the header files and documentation + needed to develop applications with libogg. diff --git a/libs/tremor/debian/copyright b/libs/tremor/debian/copyright new file mode 100644 index 0000000..ef98ddd --- /dev/null +++ b/libs/tremor/debian/copyright @@ -0,0 +1,37 @@ +This package was debianized by Christopher L Cheney on +Wed, 09 Oct 2002 22:00:00 -0500. + +It was downloaded from cvs. + +Upstream Author(s): Monty + +Copyright: +Copyright (c) 2002, Xiph.org Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.Org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/libs/tremor/debian/libvorbisidec-dev.install b/libs/tremor/debian/libvorbisidec-dev.install new file mode 100644 index 0000000..5c3ccf9 --- /dev/null +++ b/libs/tremor/debian/libvorbisidec-dev.install @@ -0,0 +1,8 @@ +debian/tmp/usr/include/tremor/config_types.h +debian/tmp/usr/include/tremor/ivorbiscodec.h +debian/tmp/usr/include/tremor/ivorbisfile.h +debian/tmp/usr/include/tremor/ogg.h +debian/tmp/usr/include/tremor/os_types.h +debian/tmp/usr/lib/libvorbisidec.a +debian/tmp/usr/lib/libvorbisidec.la +debian/tmp/usr/lib/libvorbisidec.so diff --git a/libs/tremor/debian/libvorbisidec1.install b/libs/tremor/debian/libvorbisidec1.install new file mode 100644 index 0000000..b824d1e --- /dev/null +++ b/libs/tremor/debian/libvorbisidec1.install @@ -0,0 +1 @@ +debian/tmp/usr/lib/libvorbisidec.so.* diff --git a/libs/tremor/debian/rules b/libs/tremor/debian/rules new file mode 100755 index 0000000..c684884 --- /dev/null +++ b/libs/tremor/debian/rules @@ -0,0 +1,151 @@ +#!/usr/bin/make -f +# Sample debian/rules that uses debhelper. +# GNU copyright 1997 to 1999 by Joey Hess. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +# This is the debhelper compatibility version to use. +export DH_COMPAT=4 + +# This has to be exported to make some magic below work. +export DH_OPTIONS + +# These are used for cross-compiling and for saving the configure script +# from having to guess our platform (since we know it already) +DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) + +objdir = $(CURDIR)/obj-$(DEB_BUILD_GNU_TYPE) + +ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS))) + CFLAGS += -g +endif +ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) + INSTALL_PROGRAM += -s +endif + +configure: configure-stamp +configure-stamp: + dh_testdir + + # make build directory + mkdir $(objdir) + + # run configure with build tree $(objdir) + # change ../configure to ../autogen.sh for CVS build + cd $(objdir) && \ + ../configure --build=$(DEB_BUILD_GNU_TYPE) --host=$(DEB_HOST_GNU_TYPE) \ + --prefix=/usr + + touch configure-stamp + +build: build-stamp +build-stamp: configure-stamp + dh_testdir + + cd $(objdir) && \ + $(MAKE) + + touch build-stamp + +autotools: + OLDDATESUB=`./config.sub -t | tr -d -` ;\ + OLDDATEGUESS=`./config.guess -t | tr -d -` ;\ + NEWDATESUB=`/usr/share/misc/config.sub -t | tr -d -` ;\ + NEWDATEGUESS=`/usr/share/misc/config.guess -t | tr -d -` ;\ + if [ $$OLDDATESUB -lt $$NEWDATESUB -o \ + $$OLDDATEGUESS -lt $$NEWDATEGUESS ]; then \ + dch -a -p "GNU config automated update: config.sub\ + ($$OLDDATESUB to $$NEWDATESUB), config.guess\ + ($$OLDDATEGUESS to $$NEWDATEGUESS)" ;\ + cp -f /usr/share/misc/config.sub config.sub ;\ + cp -f /usr/share/misc/config.guess config.guess ;\ + echo WARNING: GNU config scripts updated from master copies 1>&2 ;\ + fi + +debian-clean: + dh_testdir + dh_testroot + + dh_clean + +clean: autotools + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + + # Remove build tree + rm -rf $(objdir) + + # if Makefile exists run distclean + if test -f Makefile; then \ + $(MAKE) distclean; \ + fi + + #if test -d CVS; then \ + $(MAKE) cvs-clean ;\ + fi + + dh_clean + +install: DH_OPTIONS= +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + cd $(objdir) && \ + $(MAKE) install DESTDIR=$(CURDIR)/debian/tmp + + dh_install --list-missing + +# This single target is used to build all the packages, all at once, or +# one at a time. So keep in mind: any options passed to commands here will +# affect _all_ packages. Anything you want to only affect one package +# should be put in another target, such as the install target. +binary-common: + dh_testdir + dh_testroot +# dh_installxfonts + dh_installchangelogs + dh_installdocs + dh_installexamples +# dh_installmenu +# dh_installdebconf +# dh_installlogrotate +# dh_installemacsen +# dh_installpam +# dh_installmime +# dh_installinit +# dh_installcron +# dh_installinfo +# dh_undocumented + dh_installman + dh_strip + dh_link + dh_compress + dh_fixperms + dh_makeshlibs -V + dh_installdeb +# dh_perl + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +# Build architecture independant packages using the common target. +binary-indep: build install +# $(MAKE) -f debian/rules DH_OPTIONS=-i binary-common + +# Build architecture dependant packages using the common target. +binary-arch: build install + $(MAKE) -f debian/rules DH_OPTIONS=-a binary-common + +# Any other binary targets build just one binary package at a time. +binary-%: build install + $(MAKE) -f debian/rules binary-common DH_OPTIONS=-p$* + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure diff --git a/libs/tremor/doc/OggVorbis_File.html b/libs/tremor/doc/OggVorbis_File.html new file mode 100644 index 0000000..9201d18 --- /dev/null +++ b/libs/tremor/doc/OggVorbis_File.html @@ -0,0 +1,132 @@ + + + +Tremor - datatype - OggVorbis_File + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    OggVorbis_File

    + +

    declared in "ivorbisfile.h"

    + +

    +The OggVorbis_File structure defines an Ogg Vorbis file. +

    + +This structure is used in all libvorbisidec routines. Before it can be used, +it must be initialized by ov_open() or ov_open_callbacks(). + +

    +After use, the OggVorbis_File structure must be deallocated with a +call to ov_clear(). + +

    +Once a file or data source is opened successfully by libvorbisidec +(using ov_open() or ov_open_callbacks()), it is owned by +libvorbisidec. The file should not be used by any other applications or +functions outside of the libvorbisidec API. The file must not be closed +directly by the application at any time after a successful open; +libvorbisidec expects to close the file within ov_clear(). +

    +If the call to ov_open() or ov_open_callbacks() fails, +libvorbisidec does not assume ownership of the file and the +application is expected to close it if necessary. + +

    + + + + +
    +
    typedef struct {
    +  void             *datasource; /* Pointer to a FILE *, etc. */
    +  int              seekable;
    +  ogg_int64_t      offset;
    +  ogg_int64_t      end;
    +  ogg_sync_state   oy; 
    +
    +  /* If the FILE handle isn't seekable (eg, a pipe), only the current
    +     stream appears */
    +  int              links;
    +  ogg_int64_t      *offsets;
    +  ogg_int64_t      *dataoffsets;
    +  long             *serialnos;
    +  ogg_int64_t      *pcmlengths;
    +  vorbis_info      *vi;
    +  vorbis_comment   *vc;
    +
    +  /* Decoding working state local storage */
    +  ogg_int64_t      pcm_offset;
    +  int              ready_state;
    +  long             current_serialno;
    +  int              current_link;
    +
    +  ogg_int64_t      bittrack;
    +  ogg_int64_t      samptrack;
    +
    +  ogg_stream_state os; /* take physical pages, weld into a logical
    +                          stream of packets */
    +  vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
    +  vorbis_block     vb; /* local working space for packet->PCM decode */
    +
    +  ov_callbacks callbacks;
    +
    +} OggVorbis_File;
    +
    + +

    Relevant Struct Members

    +
    +
    datasource
    + +
    Pointer to file or other ogg source. When using stdio based +file/stream access, this field contains a FILE pointer. When using +custom IO via callbacks, libvorbisidec treats this void pointer as a +black box only to be passed to the callback routines provided by the +application.
    + +
    seekable
    +
    Read-only int indicating whether file is seekable. E.g., a physical file is seekable, a pipe isn't.
    +
    links
    +
    Read-only int indicating the number of logical bitstreams within the physical bitstream.
    +
    ov_callbacks
    +
    Collection of file manipulation routines to be used on this data source. When using stdio/FILE access via ov_open(), the callbacks will be filled in with stdio calls or wrappers to stdio calls.
    +
    + +

    Notes

    + +

    Tremor requires a native 64 bit integer type to compile and +function; The GNU build system will locate and typedef +ogg_int64_t to the appropriate native type. If not using the +GNU build tools, you will need to define ogg_int64_t as a +64-bit type inside your system's project file/Makefile, etc. On win32, +for example, this should be defined as __int64. +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/build.html b/libs/tremor/doc/build.html new file mode 100644 index 0000000..6f0f4ee --- /dev/null +++ b/libs/tremor/doc/build.html @@ -0,0 +1,111 @@ + + + +Tremor - Build + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    Tremor: Building libvorbisidec

    + +

    + +The C source in the Tremor package will build on any ANSI C compiler +and function completely and properly on any platform. The included +build system assumes GNU build system and make tools (m4, automake, +autoconf, libtool and gmake). GCC is not required, although GCC is +the most tested compiler. To build using GNU tools, type in the +source directory: + +

    +

    
    +./autogen.sh
    +gmake
    +
    +

    +or if GNU make is the standard make on the build system: +

    
    +./autogen.sh
    +make
    +
    + +

    +Currently, the source implements playback in pure C on all platforms +except ARM, where a [currently] small amount of assembly (see the file +asm_arm.h) is used to implement 64 bit math operations and +fast LSP computation. If building on ARM without the benefit of GNU +build system tools, be sure that _ARM_ASSEM_ is #defined by +the build system if this assembly is desired, else the resulting +library will use whatever 64 bit math builtins the compiler +implements. + +

    +No math library is required by this source. No floating point +operations are used at any point in either setup or decode. This +decoder library will properly decode any past, current or future +Vorbis I file or stream. + +

    +The GNU build system produces static and, when supported by the OS, +dynamic libraries named 'libvorbisidec'. This library exposes an API +nearly identical to the BSD reference library's 'libvorbisfile', +including all the features familiar to users of vorbisfile. This API +is similar enough that the proper header file to include is named +'ivorbisfile.h', included in the source build directory. +Lower level libvorbis-style headers and structures are +in 'ivorbiscodec.h', also included in the source build directory. A +simple example program, ivorbisfile_example.c, can be built with 'make +ivorbisfile_example'. +

    +(We've summarized differences between the free, +reference vorbisfile library and Tremor's libvorbisidec in a separate +document.) + +

    Notes

    + +

    Tremor requires a native 64 bit integer type to compile and +function; The GNU build system will locate and typedef +ogg_int64_t to the appropriate native type. If not using the +GNU build tools, you will need to define ogg_int64_t as a +64-bit type inside your system's project file/Makefile, etc. On win32, +for example, this should be defined as __int64. +

    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + + + + + + + + + + + + + + + + + diff --git a/libs/tremor/doc/callbacks.html b/libs/tremor/doc/callbacks.html new file mode 100644 index 0000000..9a6d392 --- /dev/null +++ b/libs/tremor/doc/callbacks.html @@ -0,0 +1,113 @@ + + + +Tremor - Callbacks and non-stdio I/O + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    Callbacks and non-stdio I/O

    + +Although stdio is convenient and nearly universally implemented as per +ANSI C, it is not suited to all or even most potential uses of Vorbis. +For additional flexibility, embedded applications may provide their +own I/O functions for use with Tremor when stdio is unavailable or not +suitable. One common example is decoding a Vorbis stream from a +memory buffer.

    + +Use custom I/O functions by populating an ov_callbacks structure and calling ov_open_callbacks() or ov_test_callbacks() rather than the +typical ov_open() or ov_test(). Past the open call, use of +libvorbisidec is identical to using it with stdio. + +

    Read function

    + +The read-like function provided in the read_func field is +used to fetch the requested amount of data. It expects the fetch +operation to function similar to file-access, that is, a multiple read +operations will retrieve contiguous sequential pieces of data, +advancing a position cursor after each read.

    + +The following behaviors are also expected:

    +

      +
    • a return of '0' indicates end-of-data (if the by-thread errno is unset) +
    • short reads mean nothing special (short reads are not treated as error conditions) +
    • a return of zero with the by-thread errno set to nonzero indicates a read error +
    +

    + +

    Seek function

    + +The seek-like function provided in the seek_func field is +used to request non-sequential data access by libvorbisidec, moving +the access cursor to the requested position.

    + +libvorbisidec expects the following behavior: +

      +
    • The seek function must always return -1 (failure) if the given +data abstraction is not seekable. It may choose to always return -1 +if the application desires libvorbisidec to treat the Vorbis data +strictly as a stream (which makes for a less expensive open +operation).

      + +

    • If the seek function initially indicates seekability, it must +always succeed upon being given a valid seek request.

      + +

    • The seek function must implement all of SEEK_SET, SEEK_CUR and +SEEK_END. The implementation of SEEK_END should set the access cursor +one past the last byte of accessible data, as would stdio +fseek()

      +

    + +

    Close function

    + +The close function should deallocate any access state used by the +passed in instance of the data access abstraction and invalidate the +instance handle. The close function is assumed to succeed.

    + +One common use of callbacks and the close function is to change the +behavior of libvorbisidec with respect to file closure for applications +that must fclose data files themselves. By passing +the normal stdio calls as callback functions, but passing a +close_func that does nothing, an application may call ov_clear() and then fclose() the +file originally passed to libvorbisidec. + +

    Tell function

    + +The tell function is intended to mimic the +behavior of ftell() and must return the byte position of the +next data byte that would be read. If the data access cursor is at +the end of the 'file' (pointing to one past the last byte of data, as +it would be after calling fseek(file,SEEK_END,0)), the tell +function must return the data position (and thus the total file size), +not an error.

    + +The tell function need not be provided if the data IO abstraction is +not seekable.
    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/datastructures.html b/libs/tremor/doc/datastructures.html new file mode 100644 index 0000000..2b3da07 --- /dev/null +++ b/libs/tremor/doc/datastructures.html @@ -0,0 +1,61 @@ + + + +Tremor - Base Data Structures + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    Base Data Structures

    +

    There are several data structures used to hold file and bitstream information during libvorbisidec decoding. These structures are declared in "ivorbisfile.h" and "ivorbiscodec.h". +

    +

    When using libvorbisidec, it's not necessary to know about most of the contents of these data structures, but it may be helpful to understand what they contain. +

    + + + + + + + + + + + + + + + + + + + + + + +
    datatypepurpose
    OggVorbis_FileThis structure represents the basic file information. It contains + a pointer to the physical file or bitstream and various information about that bitstream.
    vorbis_commentThis structure contains the file comments. It contains + a pointer to unlimited user comments, information about the number of comments, and a vendor description.
    vorbis_infoThis structure contains encoder-related information about the bitstream. It includes encoder info, channel info, and bitrate limits.
    ov_callbacksThis structure contains pointers to the application-specified file manipulation routines set for use by ov_open_callbacks(). See also the provided document on using application-provided callbacks instead of stdio.
    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/decoding.html b/libs/tremor/doc/decoding.html new file mode 100644 index 0000000..1f61b47 --- /dev/null +++ b/libs/tremor/doc/decoding.html @@ -0,0 +1,82 @@ + + + +Tremor - Decoding + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    Decoding

    + +

    +All libivorbisdec decoding routines are declared in "ivorbisfile.h". +

    + +After initialization, decoding audio +is as simple as calling ov_read(). This +function works similarly to reading from a normal file using +read().

    + +However, a few differences are worth noting: + +

    multiple stream links

    + +A Vorbis stream may consist of multiple sections (called links) that +encode differing numbers of channels or sample rates. It is vitally +important to pay attention to the link numbers returned by ov_read and handle audio changes that may +occur at link boundaries. Such multi-section files do exist in the +wild and are not merely a specification curiosity. + +

    returned data amount

    + +ov_read does not attempt to completely fill +a large, passed in data buffer; it merely guarantees that the passed +back data does not overflow the passed in buffer size. Large buffers +may be filled by iteratively looping over calls to ov_read (incrementing the buffer pointer) +until the original buffer is filled. + +

    file cursor position

    + +Vorbis files do not necessarily start at a sample number or time offset +of zero. Do not be surprised if a file begins at a positive offset of +several minutes or hours, such as would happen if a large stream (such +as a concert recording) is chopped into multiple seperate files. + +

    + + + + + + + + + +
    functionpurpose
    ov_readThis function makes up the main chunk of a decode loop. It takes an +OggVorbis_File structure, which must have been initialized by a previous +call to ov_open().
    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/diff.html b/libs/tremor/doc/diff.html new file mode 100644 index 0000000..ae0b908 --- /dev/null +++ b/libs/tremor/doc/diff.html @@ -0,0 +1,67 @@ + + + +Tremor - Vorbisfile Differences + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    Tremor / Vorbisfile API Differences

    + +

    + +The Tremor libvorbisidec library exposes an API intended to be as +similar as possible to the familiar 'vorbisfile' library included with +the open source Vorbis reference libraries distributed for free by +Xiph.org. Differences are summarized below.

    + +

    OggVorbis_File structure

    + +The bittrack and samptrack fields in the OggVorbis_File structure are changed to +64 bit integers in Tremor, from doubles in vorbisfile. + +

    Time-related seek and tell function calls

    + +The ov_time_total() and ov_time_tell() functions return milliseconds as +64 bit integers in Tremor. In vorbisfile, these functions returned +seconds as doubles.

    + +In Tremor, the ov_time_seek() and ov_time_seek_page() calls take +seeking positions in milliseconds as 64 bit integers, rather than in +seconds as doubles as in Vorbisfile.

    + +

    Reading decoded data

    + +Tremor ov_read() always returns data as +signed 16 bit interleaved PCM in host byte order. As such, it does not +take arguments to request specific signedness, byte order or bit depth +as in Vorbisfile.

    + +Tremor does not implement ov_read_float().

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/example.html b/libs/tremor/doc/example.html new file mode 100644 index 0000000..2b9a1dd --- /dev/null +++ b/libs/tremor/doc/example.html @@ -0,0 +1,205 @@ + + + +Tremor - Example Code + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    Example Code

    + +

    +The following is a run-through of the decoding example program supplied +with libvorbisidec, ivorbisfile_example.c. +This program takes a vorbis bitstream from stdin and writes raw pcm to stdout. + +

    +First, relevant headers, including vorbis-specific "ivorbiscodec.h" and "ivorbisfile.h" have to be included. + +

    + + + + +
    +
    
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <math.h>
    +#include "ivorbiscodec.h"
    +#include "ivorbisfile.h"
    +
    +
    +

    +We also have to make a concession to Windows users here. If we are using windows for decoding, we must declare these libraries so that we can set stdin/stdout to binary. +

    + + + + +
    +
    
    +#ifdef _WIN32
    +#include <io.h>
    +#include <fcntl.h>
    +#endif
    +
    +
    +

    +Next, a buffer for the pcm audio output is declared. + +

    + + + + +
    +
    
    +char pcmout[4096];
    +
    +
    + +

    Inside main(), we declare our primary OggVorbis_File structure. We also declare a few other helpful variables to track out progress within the file. +Also, we make our final concession to Windows users by setting the stdin and stdout to binary mode. +

    + + + + +
    +
    
    +int main(int argc, char **argv){
    +  OggVorbis_File vf;
    +  int eof=0;
    +  int current_section;
    +
    +#ifdef _WIN32
    +  _setmode( _fileno( stdin ), _O_BINARY );
    +  _setmode( _fileno( stdout ), _O_BINARY );
    +#endif
    +
    +
    + +

    ov_open() must be +called to initialize the OggVorbis_File structure with default values. +ov_open() also checks to ensure that we're reading Vorbis format and not something else. + +

    + + + + +
    +
    
    +  if(ov_open(stdin, &vf, NULL, 0) < 0) {
    +      fprintf(stderr,"Input does not appear to be an Ogg bitstream.\n");
    +      exit(1);
    +  }
    +
    +
    +
    + +

    +We're going to pull the channel and bitrate info from the file using ov_info() and show them to the user. +We also want to pull out and show the user a comment attached to the file using ov_comment(). + +

    + + + + +
    +
    
    +  {
    +    char **ptr=ov_comment(&vf,-1)->user_comments;
    +    vorbis_info *vi=ov_info(&vf,-1);
    +    while(*ptr){
    +      fprintf(stderr,"%s\n",*ptr);
    +      ++ptr;
    +    }
    +    fprintf(stderr,"\nBitstream is %d channel, %ldHz\n",vi->channels,vi->rate);
    +    fprintf(stderr,"\nDecoded length: %ld samples\n",
    +            (long)ov_pcm_total(&vf,-1));
    +    fprintf(stderr,"Encoded by: %s\n\n",ov_comment(&vf,-1)->vendor);
    +  }
    +  
    +
    +
    + +

    +Here's the read loop: + +

    + + + + +
    +
    
    +
    +  while(!eof){
    +    long ret=ov_read(&vf,pcmout,sizeof(pcmout),¤t_section);
    +    if (ret == 0) {
    +      /* EOF */
    +      eof=1;
    +    } else if (ret < 0) {
    +      /* error in the stream.  Not a problem, just reporting it in
    +	 case we (the app) cares.  In this case, we don't. */
    +    } else {
    +      /* we don't bother dealing with sample rate changes, etc, but
    +	 you'll have to*/
    +      fwrite(pcmout,1,ret,stdout);
    +    }
    +  }
    +
    +  
    +
    +
    + +

    +The code is reading blocks of data using ov_read(). +Based on the value returned, we know if we're at the end of the file or have invalid data. If we have valid data, we write it to the pcm output. + +

    +Now that we've finished playing, we can pack up and go home. It's important to call ov_clear() when we're finished. + +

    + + + + +
    +
    
    +
    +  ov_clear(&vf);
    +    
    +  fprintf(stderr,"Done.\n");
    +  return(0);
    +}
    +
    +
    + +

    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/fileinfo.html b/libs/tremor/doc/fileinfo.html new file mode 100644 index 0000000..53dfd38 --- /dev/null +++ b/libs/tremor/doc/fileinfo.html @@ -0,0 +1,95 @@ + + + +Tremor - File Information + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    File Information

    +

    Libvorbisidec contains many functions to get information about bitstream attributes and decoding status. +

    +All libvorbisidec file information routines are declared in "ivorbisfile.h". +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    functionpurpose
    ov_bitrateReturns the average bitrate of the current logical bitstream.
    ov_bitrate_instantReturns the exact bitrate since the last call of this function, or -1 if at the beginning of the bitream or no new information is available.
    ov_streamsGives the number of logical bitstreams within the current physical bitstream.
    ov_seekableIndicates whether the bitstream is seekable.
    ov_serialnumberReturns the unique serial number of the specified logical bitstream.
    ov_raw_totalReturns the total (compressed) bytes in a physical or logical seekable bitstream.
    ov_pcm_totalReturns the total number of samples in a physical or logical seekable bitstream.
    ov_time_totalReturns the total time length in seconds of a physical or logical seekable bitstream.
    ov_raw_tellReturns the byte location of the next sample to be read, giving the approximate location in the stream that the decoding engine has reached.
    ov_pcm_tellReturns the sample location of the next sample to be read, giving the approximate location in the stream that the decoding engine has reached.
    ov_time_tellReturns the time location of the next sample to be read, giving the approximate location in the stream that the decoding engine has reached.
    ov_infoReturns the vorbis_info struct for a specific bitstream section.
    ov_commentReturns attached comments for the current bitstream.
    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/index.html b/libs/tremor/doc/index.html new file mode 100644 index 0000000..671f13f --- /dev/null +++ b/libs/tremor/doc/index.html @@ -0,0 +1,53 @@ + + + +Tremor - Documentation + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    Tremor Documentation

    + +

    + +The Tremor Vorbis I stream and file decoder provides an embeddable, +integer-only library [libvorbisidec] intended for decoding all current +and future Vorbis I compliant streams. The Tremor libvorbisidec +library exposes an API intended to be as similar as possible to the +familiar 'vorbisfile' library included with the open source Vorbis +reference libraries distributed for free by Xiph.org.

    + +Tremor can be used along with any ANSI compliant stdio implementation +for file/stream access, or use custom stream i/o routines provided by +the embedded environment. Both uses are described in detail in this +documentation. + +

    +Building libvorbisidec
    +API overview
    +API reference
    +Example code
    +Tremor / vorbisfile API differences
    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/initialization.html b/libs/tremor/doc/initialization.html new file mode 100644 index 0000000..f9f6807 --- /dev/null +++ b/libs/tremor/doc/initialization.html @@ -0,0 +1,101 @@ + + + +Tremor - Setup/Teardown + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    Setup/Teardown

    In order to decode audio using +libvorbisidec, a bitstream containing Vorbis audio must be properly +initialized before decoding and cleared when decoding is finished. +The simplest possible case is to use fopen() to open a Vorbis +file and then pass the FILE * to an ov_open() call. A successful return code from ov_open() indicates the file is ready for use. +Once the file is no longer needed, ov_clear() is used to close the file and +deallocate decoding resources. Do not call fclose() on the +file; libvorbisidec does this in the ov_clear() call. + +

    +All libvorbisidec initialization and deallocation routines are declared in "ivorbisfile.h". +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    functionpurpose
    ov_openInitializes the Ogg Vorbis bitstream with a pointer to a bitstream and default values. This must be called before other functions in the library may be + used.
    ov_open_callbacksInitializes the Ogg Vorbis bitstream with a pointer to a bitstream, default values, and custom file/bitstream manipulation routines. Used instead of ov_open() when working with other than stdio based I/O.
    ov_testPartially opens a file just far enough to determine if the file +is an Ogg Vorbis file or not. A successful return indicates that the +file appears to be an Ogg Vorbis file, but the OggVorbis_File struct is not yet fully +initialized for actual decoding. After a successful return, the file +may be closed using ov_clear() or fully +opened for decoding using ov_test_open().

    This call is intended to +be used as a less expensive file open test than a full ov_open().

    +Note that libvorbisidec owns the passed in file resource is it returns success; do not fclose() files owned by libvorbisidec.

    ov_test_callbacksAs above but allowing application-define I/O callbacks.

    +Note that libvorbisidec owns the passed in file resource is it returns success; do not fclose() files owned by libvorbisidec.

    ov_test_open +Finish opening a file after a successful call to ov_test() or ov_test_callbacks().
    ov_clear Closes the + bitstream and cleans up loose ends. Must be called when + finished with the bitstream. After return, the OggVorbis_File struct is + invalid and may not be used before being initialized again + before begin reinitialized. + +
    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_bitrate.html b/libs/tremor/doc/ov_bitrate.html new file mode 100644 index 0000000..65ebfc3 --- /dev/null +++ b/libs/tremor/doc/ov_bitrate.html @@ -0,0 +1,72 @@ + + + +Tremor - function - ov_bitrate + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_bitrate

    + +

    declared in "ivorbisfile.h";

    + +

    This function returns the average bitrate for the specified logical bitstream. This may be different from the ov_info->nominal_bitrate value, as it is based on the actual average for this bitstream if the file is seekable. +

    Nonseekable files will return the nominal bitrate setting or the average of the upper and lower bounds, if any of these values are set. +

    + +

    + + + + +
    +
    
    +long ov_bitrate(OggVorbis_File *vf,int i);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    i
    +
    Link to the desired logical bitstream. For nonseekable files, this argument is ignored. To retrieve the bitrate for the entire bitstream, this parameter should be set to -1.
    +
    + + +

    Return Values

    +
    +
  1. OV_EINVAL indicates that an invalid argument value was submitted or that the stream represented by vf is not open.
  2. +
  3. OV_FALSE means the call returned a 'false' status, which in this case most likely indicates that the file is nonseekable and the upper, lower, and nominal bitrates were unset. +
  4. n indicates the bitrate for the given logical bitstream or the entire + physical bitstream. If the file is open for random (seekable) access, it will + find the *actual* average bitrate. If the file is streaming (nonseekable), it + returns the nominal bitrate (if set) or else the average of the + upper/lower bounds (if set).
  5. +
    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_bitrate_instant.html b/libs/tremor/doc/ov_bitrate_instant.html new file mode 100644 index 0000000..874671f --- /dev/null +++ b/libs/tremor/doc/ov_bitrate_instant.html @@ -0,0 +1,65 @@ + + + +Tremor - function - ov_bitrate + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_bitrate_instant

    + +

    declared in "ivorbisfile.h";

    + +

    Used to find the most recent bitrate played back within the file. Will return 0 if the bitrate has not changed or it is the beginning of the file. + +

    + + + + +
    +
    
    +long ov_bitrate_instant(OggVorbis_File *vf);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions. +
    + + +

    Return Values

    +
    +
  6. 0 indicates the beginning of the file or unchanged bitrate info.
  7. +
  8. n indicates the actual bitrate since the last call.
  9. +
  10. OV_FALSE indicates that playback is not in progress, and thus there is no instantaneous bitrate information to report.
  11. +
  12. OV_EINVAL indicates that the stream represented by vf is not open.
  13. +
    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_callbacks.html b/libs/tremor/doc/ov_callbacks.html new file mode 100644 index 0000000..776352d --- /dev/null +++ b/libs/tremor/doc/ov_callbacks.html @@ -0,0 +1,78 @@ + + + +Tremor - datatype - ov_callbacks + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_callbacks

    + +

    declared in "ivorbiscodec.h"

    + +

    +The ov_callbacks structure contains file manipulation function prototypes necessary for opening, closing, seeking, and location. + +

    +The ov_callbacks structure does not need to be user-defined if you are +working with stdio-based file manipulation; the ov_open() call provides default callbacks for +stdio. ov_callbacks are defined and passed to ov_open_callbacks() when +implementing non-stdio based stream manipulation (such as playback +from a memory buffer). +

    + + + + + +
    +
    typedef struct {
    +  size_t (*read_func)  (void *ptr, size_t size, size_t nmemb, void *datasource);
    +  int    (*seek_func)  (void *datasource, ogg_int64_t offset, int whence);
    +  int    (*close_func) (void *datasource);
    +  long   (*tell_func)  (void *datasource);
    +} ov_callbacks;
    +
    + +

    Relevant Struct Members

    +
    +
    read_func
    +
    Pointer to custom data reading function.
    +
    seek_func
    +
    Pointer to custom data seeking function. If the data source is not seekable (or the application wants the data source to be treated as unseekable at all times), the provided seek callback should always return -1 (failure).
    +
    close_func
    +
    Pointer to custom data source closure function.
    +
    tell_func
    +
    Pointer to custom data location function.
    +
    + +

    + +See the callbacks and non-stdio I/O document for more +detailed information on required behavior of the various callback +functions.

    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_clear.html b/libs/tremor/doc/ov_clear.html new file mode 100644 index 0000000..7c51bb7 --- /dev/null +++ b/libs/tremor/doc/ov_clear.html @@ -0,0 +1,64 @@ + + + +Tremor - function - ov_clear + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_clear

    + +

    declared in "ivorbisfile.h";

    + +

    After a bitstream has been opened using ov_open()/ov_open_callbacks() and decoding is complete, the application must call ov_clear() to clear +the decoder's buffers and close the file.

    + +ov_clear() must also be called after a successful call to ov_test() or ov_test_callbacks().

    + +

    + + + + +
    +
    
    +int ov_clear(OggVorbis_File *vf);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions. After ov_clear has been called, the structure is deallocated and can no longer be used.
    +
    + + +

    Return Values

    +
    +
  14. 0 for success
  15. +
    + + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_comment.html b/libs/tremor/doc/ov_comment.html new file mode 100644 index 0000000..5d9cc0b --- /dev/null +++ b/libs/tremor/doc/ov_comment.html @@ -0,0 +1,66 @@ + + + +Tremor - function - ov_bitrate + + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_comment

    + +

    declared in "ivorbisfile.h";

    + +

    Returns a pointer to the vorbis_comment struct for the specified bitstream. For nonseekable streams, returns the struct for the current bitstream. +

    + +

    + + + + +
    +
    
    +vorbis_comment *ov_comment(OggVorbis_File *vf,int link);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    i
    +
    Link to the desired logical bitstream. For nonseekable files, this argument is ignored. To retrieve the vorbis_comment struct for the current bitstream, this parameter should be set to -1.
    +
    + + +

    Return Values

    +
    +
  16. Returns the vorbis_comment struct for the specified bitstream.
  17. +
  18. NULL if the specified bitstream does not exist or the file has been initialized improperly.
  19. +
    +

    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_info.html b/libs/tremor/doc/ov_info.html new file mode 100644 index 0000000..d783bf3 --- /dev/null +++ b/libs/tremor/doc/ov_info.html @@ -0,0 +1,64 @@ + + + +Tremor - function - ov_info + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_info

    + +

    declared in "ivorbisfile.h";

    + +

    Returns the vorbis_info struct for the specified bitstream. For nonseekable files, always returns the current vorbis_info struct. + +

    + + + + +
    +
    
    +vorbis_info *ov_info(OggVorbis_File *vf,int link);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    i
    +
    Link to the desired logical bitstream. For nonseekable files, this argument is ignored. To retrieve the vorbis_info struct for the current bitstream, this parameter should be set to -1.
    +
    + + +

    Return Values

    +
    +
  20. Returns the vorbis_info struct for the specified bitstream. Returns vorbis_info for current bitstream if the file is nonseekable or i=-1.
  21. +
  22. NULL if the specified bitstream does not exist or the file has been initialized improperly.
  23. +
    +

    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_open.html b/libs/tremor/doc/ov_open.html new file mode 100644 index 0000000..654cae8 --- /dev/null +++ b/libs/tremor/doc/ov_open.html @@ -0,0 +1,115 @@ + + + +Tremor - function - ov_open + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_open

    + +

    declared in "ivorbisfile.h";

    + +

    This is the main function used to open and initialize an OggVorbis_File +structure. It sets up all the related decoding structure. +

    The first argument must be a file pointer to an already opened file +or pipe (it need not be seekable--though this obviously restricts what +can be done with the bitstream). vf should be a pointer to the +OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions. Once this has been called, the same OggVorbis_File +struct should be passed to all the libvorbisidec functions. +

    Also, you should be aware that ov_open(), once successful, takes complete possession of the file resource. After you have opened a file using ov_open(), you MUST close it using ov_clear(), not fclose() or any other function. +

    +It is often useful to call ov_open() +simply to determine whether a given file is a vorbis bitstream. If the +ov_open() +call fails, then the file is not recognizable as such. +When you use ov_open() +for +this, you should fclose() the file pointer if, and only if, the +ov_open() +call fails. If it succeeds, you must call ov_clear() to clear +the decoder's buffers and close the file for you.

    + +(Note that ov_test() provides a less expensive way to test a file for Vorbisness.)

    + +

    + + + + +
    +
    
    +int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes);
    +
    +
    + +

    Parameters

    +
    +
    f
    +
    File pointer to an already opened file +or pipe (it need not be seekable--though this obviously restricts what +can be done with the bitstream).
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions. Once this has been called, the same OggVorbis_File +struct should be passed to all the libvorbisidec functions.
    +
    initial
    +
    Typically set to NULL. This parameter is useful if some data has already been +read from the file and the stream is not seekable. It is used in conjunction with ibytes. In this case, initial +should be a pointer to a buffer containing the data read.
    +
    ibytes
    +
    Typically set to 0. This parameter is useful if some data has already been +read from the file and the stream is not seekable. In this case, ibytes +should contain the length (in bytes) of the buffer. Used together with initial
    +
    + + +

    Return Values

    +
    +
  24. 0 indicates success
  25. + +
  26. less than zero for failure:
  27. +
      +
    • OV_EREAD - A read from media returned an error.
    • +
    • OV_ENOTVORBIS - Bitstream is not Vorbis data.
    • +
    • OV_EVERSION - Vorbis version mismatch.
    • +
    • OV_EBADHEADER - Invalid Vorbis bitstream header.
    • +
    • OV_EFAULT - Internal logic fault; indicates a bug or heap/stack corruption.
    • +
    +
    +

    + +

    Notes

    +

    If your decoder is threaded, it is recommended that you NOT call +ov_open() +in the main control thread--instead, call ov_open() IN your decode/playback +thread. This is important because ov_open() may be a fairly time-consuming +call, given that the full structure of the file is determined at this point, +which may require reading large parts of the file under certain circumstances +(determining all the logical bitstreams in one physical bitstream, for +example). See Thread Safety for other information on using libvorbisidec with threads. + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_open_callbacks.html b/libs/tremor/doc/ov_open_callbacks.html new file mode 100644 index 0000000..64a2a92 --- /dev/null +++ b/libs/tremor/doc/ov_open_callbacks.html @@ -0,0 +1,110 @@ + + + +Tremor - function - ov_open_callbacks + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_open_callbacks

    + +

    declared in "ivorbisfile.h";

    + +

    This is an alternative function used to open and initialize an OggVorbis_File +structure when using a data source other than a file. It allows you to specify custom file manipulation routines and sets up all the related decoding structure. +

    Once this has been called, the same OggVorbis_File +struct should be passed to all the libvorbisidec functions. +

    +It is often useful to call ov_open_callbacks() +simply to determine whether a given file is a vorbis bitstream. If the +ov_open_callbacks() +call fails, then the file is not recognizable as such. When you use ov_open_callbacks() +for +this, you should fclose() the file pointer if, and only if, the +ov_open_callbacks() +call fails. If it succeeds, you must call ov_clear() to clear +the decoder's buffers and close the file for you.

    + +See also Callbacks and Non-stdio I/O for information on designing and specifying the required callback functions.

    + +

    + + + + +
    +
    
    +int ov_open_callbacks(void *datasource, OggVorbis_File *vf, char *initial, long ibytes, ov_callbacks callbacks);
    +
    +
    + +

    Parameters

    +
    +
    f
    +
    File pointer to an already opened file +or pipe (it need not be seekable--though this obviously restricts what +can be done with the bitstream).
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions. Once this has been called, the same OggVorbis_File +struct should be passed to all the libvorbisidec functions.
    +
    initial
    +
    Typically set to NULL. This parameter is useful if some data has already been +read from the file and the stream is not seekable. It is used in conjunction with ibytes. In this case, initial +should be a pointer to a buffer containing the data read.
    +
    ibytes
    +
    Typically set to 0. This parameter is useful if some data has already been +read from the file and the stream is not seekable. In this case, ibytes +should contain the length (in bytes) of the buffer. Used together with initial.
    +
    callbacks
    +
    Pointer to a completed ov_callbacks struct which indicates desired custom file manipulation routines.
    +
    + + +

    Return Values

    +
    +
  28. 0 for success
  29. +
  30. less than zero for failure:
  31. +
      +
    • OV_EREAD - A read from media returned an error.
    • +
    • OV_ENOTVORBIS - Bitstream is not Vorbis data.
    • +
    • OV_EVERSION - Vorbis version mismatch.
    • +
    • OV_EBADHEADER - Invalid Vorbis bitstream header.
    • +
    • OV_EFAULT - Internal logic fault; indicates a bug or heap/stack corruption.
    • +
    +
    +

    + +

    Notes

    +

    If your decoder is threaded, it is recommended that you NOT call +ov_open_callbacks() +in the main control thread--instead, call ov_open_callbacks() IN your decode/playback +thread. This is important because ov_open_callbacks() may be a fairly time-consuming +call, given that the full structure of the file is determined at this point, +which may require reading large parts of the file under certain circumstances +(determining all the logical bitstreams in one physical bitstream, for +example). +See Thread Safety for other information on using libvorbisidec with threads. + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_pcm_seek.html b/libs/tremor/doc/ov_pcm_seek.html new file mode 100644 index 0000000..cf0351e --- /dev/null +++ b/libs/tremor/doc/ov_pcm_seek.html @@ -0,0 +1,81 @@ + + + +Tremor - function - ov_pcm_seek + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_pcm_seek

    + +

    declared in "ivorbisfile.h";

    + +

    Seeks to the offset specified (in pcm samples) within the physical bitstream. This function only works for seekable streams. +

    This also updates everything needed within the +decoder, so you can immediately call ov_read() and get data from +the newly seeked to position. +

    + +

    + + + + +
    +
    
    +int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    pos
    +
    Position in pcm samples to seek to in the bitstream.
    +
    + + +

    Return Values

    +
    +
  32. 0 for success
  33. + +
  34. +nonzero indicates failure, described by several error codes:
  35. +
      +
    • OV_ENOSEEK - Bitstream is not seekable. +
    • +
    • OV_EINVAL - Invalid argument value. +
    • +
    • OV_EREAD - A read from media returned an error. +
    • +
    • OV_EFAULT - Internal logic fault; indicates a bug or heap/stack + corruption. +
    • +
    • OV_EBADLINK - Invalid stream section supplied to libvorbisidec, or the requested link is corrupt. +
    • +
    + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_pcm_seek_page.html b/libs/tremor/doc/ov_pcm_seek_page.html new file mode 100644 index 0000000..44468a8 --- /dev/null +++ b/libs/tremor/doc/ov_pcm_seek_page.html @@ -0,0 +1,83 @@ + + + +Tremor - function - ov_pcm_seek_page + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_pcm_seek_page

    + +

    declared in "ivorbisfile.h";

    + +

    Seeks to the closest page preceding the specified location (in pcm samples) within the physical bitstream. This function only works for seekable streams. +

    This function is faster than ov_pcm_seek because the function can begin decoding at a page boundary rather than seeking through any remaining samples before the specified location. However, it is less accurate. +

    This also updates everything needed within the +decoder, so you can immediately call ov_read() and get data from +the newly seeked to position. +

    + +

    + + + + +
    +
    
    +int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    pos
    +
    Position in pcm samples to seek to in the bitstream.
    +
    + + +

    Return Values

    +
    +
  36. +0 for success
  37. + +
  38. +nonzero indicates failure, described by several error codes:
  39. +
      +
    • OV_ENOSEEK - Bitstream is not seekable. +
    • +
    • OV_EINVAL - Invalid argument value. +
    • +
    • OV_EREAD - A read from media returned an error. +
    • +
    • OV_EFAULT - Internal logic fault; indicates a bug or heap/stack + corruption. +
    • +
    • OV_EBADLINK - Invalid stream section supplied to libvorbisidec, or the requested link is corrupt. +
    • +
    + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_pcm_tell.html b/libs/tremor/doc/ov_pcm_tell.html new file mode 100644 index 0000000..0bb98d7 --- /dev/null +++ b/libs/tremor/doc/ov_pcm_tell.html @@ -0,0 +1,63 @@ + + + +Tremor - function - ov_pcm_tell + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_pcm_tell

    + +

    declared in "ivorbisfile.h";

    + +

    Returns the current offset in samples. + +

    + + + + +
    +
    
    +ogg_int64_t ov_pcm_tell(OggVorbis_File *vf);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    + + +

    Return Values

    +
    +
  40. n indicates the current offset in samples.
  41. +
  42. OV_EINVAL means that the argument was invalid. In this case, the requested bitstream did not exist.
  43. +
    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_pcm_total.html b/libs/tremor/doc/ov_pcm_total.html new file mode 100644 index 0000000..a19744a --- /dev/null +++ b/libs/tremor/doc/ov_pcm_total.html @@ -0,0 +1,67 @@ + + + +Tremor - function - ov_pcm_total + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_pcm_total

    + +

    declared in "ivorbisfile.h";

    + +

    Returns the total pcm samples of the physical bitstream or a specified logical bitstream. + +

    + + + + +
    +
    
    +ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    i
    +
    Link to the desired logical bitstream. To retrieve the total pcm samples for the entire physical bitstream, this parameter should be set to -1.
    +
    + + +

    Return Values

    +
    +
  44. OV_EINVAL means that the argument was invalid. In this case, the requested bitstream did not exist or the bitstream is unseekable.
  45. +
  46. +total length in pcm samples of content if i=-1.
  47. +
  48. length in pcm samples of logical bitstream if i=1 to n.
  49. +
    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_raw_seek.html b/libs/tremor/doc/ov_raw_seek.html new file mode 100644 index 0000000..e7f0bd3 --- /dev/null +++ b/libs/tremor/doc/ov_raw_seek.html @@ -0,0 +1,75 @@ + + + +Tremor - function - ov_raw_seek + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_raw_seek

    + +

    declared in "ivorbisfile.h";

    + +

    Seeks to the offset specified (in compressed raw bytes) within the physical bitstream. This function only works for seekable streams. +

    This also updates everything needed within the +decoder, so you can immediately call ov_read() and get data from +the newly seeked to position. +

    When seek speed is a priority, this is the best seek funtion to use. +

    + + + + +
    +
    
    +int ov_raw_seek(OggVorbis_File *vf,long pos);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    pos
    +
    Position in compressed bytes to seek to in the bitstream.
    +
    + + +

    Return Values

    +
    +
  50. 0 indicates success
  51. +
  52. nonzero indicates failure, described by several error codes:
  53. +
      +
    • OV_ENOSEEK - Bitstream is not seekable. +
    • +
    • OV_EINVAL - Invalid argument value. +
    • +
    • OV_EBADLINK - Invalid stream section supplied to libvorbisidec, or the requested link is corrupt. +
    • +
    +
    +

    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_raw_tell.html b/libs/tremor/doc/ov_raw_tell.html new file mode 100644 index 0000000..f0d1f6a --- /dev/null +++ b/libs/tremor/doc/ov_raw_tell.html @@ -0,0 +1,63 @@ + + + +Tremor - function - ov_raw_tell + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_raw_tell

    + +

    declared in "ivorbisfile.h";

    + +

    Returns the current offset in raw compressed bytes. + +

    + + + + +
    +
    
    +ogg_int64_t ov_raw_tell(OggVorbis_File *vf);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    + + +

    Return Values

    +
    +
  54. n indicates the current offset in bytes.
  55. +
  56. OV_EINVAL means that the argument was invalid. In this case, the requested bitstream did not exist.
  57. +
    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_raw_total.html b/libs/tremor/doc/ov_raw_total.html new file mode 100644 index 0000000..d0af35f --- /dev/null +++ b/libs/tremor/doc/ov_raw_total.html @@ -0,0 +1,68 @@ + + + +Tremor - function - ov_raw_total + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_raw_total

    + +

    declared in "ivorbisfile.h";

    + +

    Returns the total (compressed) bytes of the physical bitstream or a specified logical bitstream. + +

    + + + + +
    +
    
    +ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    i
    +
    Link to the desired logical bitstream. To retrieve the total bytes for the entire physical bitstream, this parameter should be set to -1.
    +
    + + +

    Return Values

    +
    +
  58. OV_EINVAL means that the argument was invalid. In this case, the requested bitstream did not exist or the bitstream is nonseekable
  59. +
  60. n +total length in compressed bytes of content if i=-1.
  61. +
  62. n length in compressed bytes of logical bitstream if i=1 to n.
  63. +
    +

    + + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_read.html b/libs/tremor/doc/ov_read.html new file mode 100644 index 0000000..208ef18 --- /dev/null +++ b/libs/tremor/doc/ov_read.html @@ -0,0 +1,115 @@ + + + +Tremor - function - ov_read + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_read()

    + +

    declared in "ivorbisfile.h";

    + +

    + This is the main function used to decode a Vorbis file within a + loop. It returns up to the specified number of bytes of decoded audio + in host-endian, signed 16 bit PCM format. If the audio is + multichannel, the channels are interleaved in the output buffer. + If the passed in buffer is large, ov_read() will not fill + it; the passed in buffer size is treated as a limit and + not a request. +

    + +Note that up to this point, the Tremor API could more or less hide the + multiple logical bitstream nature of chaining from the toplevel + application if the toplevel application didn't particularly care. + However, when reading audio back, the application must be aware + that multiple bitstream sections do not necessarily use the same + number of channels or sampling rate.

    ov_read() passes + back the index of the sequential logical bitstream currently being + decoded (in *bitstream) along with the PCM data in order + that the toplevel application can handle channel and/or sample + rate changes. This number will be incremented at chaining + boundaries even for non-seekable streams. For seekable streams, it + represents the actual chaining index within the physical bitstream. +

    + +

    + + + + +
    +
    
    +long ov_read(OggVorbis_File *vf, char *buffer, int length, int *bitstream);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    buffer
    +
    A pointer to an output buffer. The decoded output is inserted into this buffer.
    +
    length
    +
    Number of bytes to be read into the buffer. Should be the same size as the buffer. A typical value is 4096.
    +
    bitstream
    +
    A pointer to the number of the current logical bitstream.
    +
    + + +

    Return Values

    +
    +
    +
    OV_HOLE
    +
    indicates there was an interruption in the data. +
    (one of: garbage between pages, loss of sync followed by + recapture, or a corrupt page)
    +
    OV_EBADLINK
    +
    indicates that an invalid stream section was supplied to + libvorbisidec, or the requested link is corrupt.
    +
    0
    +
    indicates EOF
    +
    n
    +
    indicates actual number of bytes read. ov_read() will + decode at most one vorbis packet per invocation, so the value + returned will generally be less than length. +
    +
    + +

    Notes

    +

    Typical usage: +

    +bytes_read = ov_read(&vf, +buffer, 4096,&current_section) +
    + +This reads up to 4096 bytes into a buffer, with signed 16-bit +little-endian samples. +

    + + + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_seekable.html b/libs/tremor/doc/ov_seekable.html new file mode 100644 index 0000000..9bd7fc3 --- /dev/null +++ b/libs/tremor/doc/ov_seekable.html @@ -0,0 +1,63 @@ + + + +Tremor - function - ov_seekable + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_seekable

    + +

    declared in "ivorbisfile.h";

    + +

    This indicates whether or not the bitstream is seekable. + + +

    + + + + +
    +
    
    +long ov_seekable(OggVorbis_File *vf);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    + + +

    Return Values

    +
    +
  64. 0 indicates that the file is not seekable.
  65. +
  66. nonzero indicates that the file is seekable.
  67. +
    +

    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_serialnumber.html b/libs/tremor/doc/ov_serialnumber.html new file mode 100644 index 0000000..d7d7c62 --- /dev/null +++ b/libs/tremor/doc/ov_serialnumber.html @@ -0,0 +1,67 @@ + + + +Tremor - function - ov_serialnumber + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_serialnumber

    + +

    declared in "ivorbisfile.h";

    + +

    Returns the serialnumber of the specified logical bitstream link number within the overall physical bitstream. + +

    + + + + +
    +
    
    +long ov_serialnumber(OggVorbis_File *vf,int i);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    i
    +
    Link to the desired logical bitstream. For nonseekable files, this argument is ignored. To retrieve the serial number of the current bitstream, this parameter should be set to -1.
    +
    + + +

    Return Values

    +
    +
  68. +-1 if the specified logical bitstream i does not exist.
  69. + +
  70. Returns the serial number of the logical bitstream i or the serial number of the current bitstream if the file is nonseekable.
  71. +
    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_streams.html b/libs/tremor/doc/ov_streams.html new file mode 100644 index 0000000..7ffee42 --- /dev/null +++ b/libs/tremor/doc/ov_streams.html @@ -0,0 +1,64 @@ + + + +Tremor - function - ov_streams + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_streams

    + +

    declared in "ivorbisfile.h";

    + +

    Returns the number of logical bitstreams within our physical bitstream. + +

    + + + + +
    +
    
    +long ov_streams(OggVorbis_File *vf);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    + + +

    Return Values

    +
    +
  72. +1 indicates a single logical bitstream or an unseekable file.
  73. +
  74. n indicates the number of logical bitstreams.
  75. +
    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_test.html b/libs/tremor/doc/ov_test.html new file mode 100644 index 0000000..96a9af0 --- /dev/null +++ b/libs/tremor/doc/ov_test.html @@ -0,0 +1,89 @@ + + + +Tremor - function - ov_test + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_test

    + +

    declared in "ivorbisfile.h";

    + +

    +This partially opens a vorbis file to test for Vorbis-ness. It loads +the headers for the first chain, and tests for seekability (but does not seek). +Use ov_test_open() to finish opening the file +or ov_clear to close/free it. +

    + + + + + +
    +
    
    +int ov_test(FILE *f,OggVorbis_File *vf,char *initial,long ibytes);
    +
    +
    + +

    Parameters

    +
    +
    f
    +
    File pointer to an already opened file +or pipe (it need not be seekable--though this obviously restricts what +can be done with the bitstream).
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions. Once this has been called, the same OggVorbis_File +struct should be passed to all the libvorbisidec functions.
    +
    initial
    +
    Typically set to NULL. This parameter is useful if some data has already been +read from the file and the stream is not seekable. It is used in conjunction with ibytes. In this case, initial +should be a pointer to a buffer containing the data read.
    +
    ibytes
    +
    Typically set to 0. This parameter is useful if some data has already been +read from the file and the stream is not seekable. In this case, ibytes +should contain the length (in bytes) of the buffer. Used together with initial
    +
    + + +

    Return Values

    +
    +
  76. 0 for success
  77. + +
  78. less than zero for failure:
  79. +
      +
    • OV_EREAD - A read from media returned an error.
    • +
    • OV_ENOTVORBIS - Bitstream is not Vorbis data.
    • +
    • OV_EVERSION - Vorbis version mismatch.
    • +
    • OV_EBADHEADER - Invalid Vorbis bitstream header.
    • +
    • OV_EFAULT - Internal logic fault; indicates a bug or heap/stack corruption.
    • +
    +
    +

    + + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_test_callbacks.html b/libs/tremor/doc/ov_test_callbacks.html new file mode 100644 index 0000000..4049548 --- /dev/null +++ b/libs/tremor/doc/ov_test_callbacks.html @@ -0,0 +1,90 @@ + + + +Tremor - function - ov_test_callbacks + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_test_callbacks

    + +

    declared in "ivorbisfile.h";

    + +

    This is an alternative function used to open and test an OggVorbis_File +structure when using a data source other than a file. It allows you to specify custom file manipulation routines and sets up all the related decoding structures. +

    Once this has been called, the same OggVorbis_File +struct should be passed to all the libvorbisidec functions. +

    +

    + + + + +
    +
    
    +int ov_test_callbacks(void *datasource, OggVorbis_File *vf, char *initial, long ibytes, ov_callbacks callbacks);
    +
    +
    + +

    Parameters

    +
    +
    f
    +
    File pointer to an already opened file +or pipe (it need not be seekable--though this obviously restricts what +can be done with the bitstream).
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions. Once this has been called, the same OggVorbis_File +struct should be passed to all the libvorbisidec functions.
    +
    initial
    +
    Typically set to NULL. This parameter is useful if some data has already been +read from the file and the stream is not seekable. It is used in conjunction with ibytes. In this case, initial +should be a pointer to a buffer containing the data read.
    +
    ibytes
    +
    Typically set to 0. This parameter is useful if some data has already been +read from the file and the stream is not seekable. In this case, ibytes +should contain the length (in bytes) of the buffer. Used together with initial.
    +
    callbacks
    +
    Pointer to a completed ov_callbacks struct which indicates desired custom file manipulation routines.
    +
    + + +

    Return Values

    +
    +
  80. 0 for success
  81. +
  82. less than zero for failure:
  83. +
      +
    • OV_EREAD - A read from media returned an error.
    • +
    • OV_ENOTVORBIS - Bitstream is not Vorbis data.
    • +
    • OV_EVERSION - Vorbis version mismatch.
    • +
    • OV_EBADHEADER - Invalid Vorbis bitstream header.
    • +
    • OV_EFAULT - Internal logic fault; indicates a bug or heap/stack corruption.
    • +
    +
    +

    + + + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_test_open.html b/libs/tremor/doc/ov_test_open.html new file mode 100644 index 0000000..74f4410 --- /dev/null +++ b/libs/tremor/doc/ov_test_open.html @@ -0,0 +1,82 @@ + + + +Tremor - function - ov_test_open + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_test_open

    + +

    declared in "ivorbisfile.h";

    + +

    +Finish opening a file partially opened with ov_test() +or ov_test_callbacks(). +

    + + + + + +
    +
    
    +int ov_test_open(OggVorbis_File *vf);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions. Once this has been called, the same OggVorbis_File +struct should be passed to all the libvorbisidec functions.
    +
    + + +

    Return Values

    +
    +
  84. +0 for success
  85. + +
  86. less than zero for failure:
  87. +
      +
    • OV_EREAD - A read from media returned an error.
    • +
    • OV_ENOTVORBIS - Bitstream is not Vorbis data.
    • +
    • OV_EVERSION - Vorbis version mismatch.
    • +
    • OV_EBADHEADER - Invalid Vorbis bitstream header.
    • +
    • OV_EFAULT - Internal logic fault; indicates a bug or heap/stack corruption.
    • +
    +
    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + + + + + + + + diff --git a/libs/tremor/doc/ov_time_seek.html b/libs/tremor/doc/ov_time_seek.html new file mode 100644 index 0000000..6dfa130 --- /dev/null +++ b/libs/tremor/doc/ov_time_seek.html @@ -0,0 +1,70 @@ + + + +Tremor - function - ov_time_seek + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_time_seek

    + +

    declared in "ivorbisfile.h";

    + +

    For seekable +streams, this seeks to the given time. For implementing seeking in a player, +this is the only function generally needed. This also updates everything needed within the +decoder, so you can immediately call ov_read() and get data from +the newly seeked to position. This function does not work for unseekable streams. + +

    + + + + +
    +
    
    +int ov_time_seek(OggVorbis_File *vf, ogg_int64_t ms);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    Pointer to our already opened and initialized OggVorbis_File structure.
    +
    ms
    +
    Location to seek to within the file, specified in milliseconds.
    +
    + + +

    Return Values

    +
    +
  88. +0 for success
  89. + +
  90. +Nonzero for failure
  91. +
    + + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_time_seek_page.html b/libs/tremor/doc/ov_time_seek_page.html new file mode 100644 index 0000000..83cfefb --- /dev/null +++ b/libs/tremor/doc/ov_time_seek_page.html @@ -0,0 +1,83 @@ + + + +Tremor - function - ov_time_seek_page + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_time_seek_page

    + +

    declared in "ivorbisfile.h";

    + +

    For seekable +streams, this seeks to closest full page preceding the given time. This function is faster than ov_time_seek because it doesn't seek through the last few samples to reach an exact time, but it is also less accurate. This should be used when speed is important. +

    This function also updates everything needed within the +decoder, so you can immediately call ov_read() and get data from +the newly seeked to position. +

    This function does not work for unseekable streams. + +

    + + + + +
    +
    
    +int ov_time_seek_page(OggVorbis_File *vf, ogg_int64_t ms);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    Pointer to our already opened and initialized OggVorbis_File structure.
    +
    ms
    +
    Location to seek to within the file, specified in milliseconds.
    +
    + + +

    Return Values

    +
    +
  92. +0 for success
  93. + +
  94. +nonzero indicates failure, described by several error codes:
  95. +
      +
    • OV_ENOSEEK - Bitstream is not seekable. +
    • +
    • OV_EINVAL - Invalid argument value. +
    • +
    • OV_EREAD - A read from media returned an error. +
    • +
    • OV_EFAULT - Internal logic fault; indicates a bug or heap/stack + corruption. +
    • +
    • OV_EBADLINK - Invalid stream section supplied to libvorbisidec, or the requested link is corrupt. +
    • +
    + + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_time_tell.html b/libs/tremor/doc/ov_time_tell.html new file mode 100644 index 0000000..25d159b --- /dev/null +++ b/libs/tremor/doc/ov_time_tell.html @@ -0,0 +1,63 @@ + + + +Tremor - function - ov_bitrate + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_time_tell

    + +

    declared in "ivorbisfile.h";

    + +

    Returns the current decoding offset in milliseconds. + +

    + + + + +
    +
    
    +ogg_int64_t ov_time_tell(OggVorbis_File *vf);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    + + +

    Return Values

    +
    +
  96. n indicates the current decoding time offset in milliseconds.
  97. +
  98. OV_EINVAL means that the argument was invalid. In this case, the requested bitstream did not exist.
  99. +
    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/ov_time_total.html b/libs/tremor/doc/ov_time_total.html new file mode 100644 index 0000000..7c26b92 --- /dev/null +++ b/libs/tremor/doc/ov_time_total.html @@ -0,0 +1,67 @@ + + + +Tremor - function - ov_time_total + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    ov_time_total

    + +

    declared in "ivorbisfile.h";

    + + +

    Returns the total time in seconds of the physical bitstream or a specified logical bitstream. + + +

    + + + + +
    +
    
    +ogg_int64_t ov_time_total(OggVorbis_File *vf,int i);
    +
    +
    + +

    Parameters

    +
    +
    vf
    +
    A pointer to the OggVorbis_File structure--this is used for ALL the externally visible libvorbisidec +functions.
    +
    i
    +
    Link to the desired logical bitstream. To retrieve the time total for the entire physical bitstream, this parameter should be set to -1.
    +
    + + +

    Return Values

    +
    +
  100. OV_EINVAL means that the argument was invalid. In this case, the requested bitstream did not exist or the bitstream is nonseekable.
  101. +
  102. n total length in milliseconds of content if i=-1.
  103. +
  104. n length in milliseconds of logical bitstream if i=1 to n.
  105. +
    +

    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/overview.html b/libs/tremor/doc/overview.html new file mode 100644 index 0000000..0c82cb2 --- /dev/null +++ b/libs/tremor/doc/overview.html @@ -0,0 +1,61 @@ + + + +Tremor - API Overview + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    Tremor API Overview

    + +

    The makeup of the Tremor libvorbisidec library API is relatively +simple. It revolves around a single file resource. This file resource is +passed to libvorbisidec, where it is opened, manipulated, and closed, +in the form of an OggVorbis_File +struct. +

    +The Tremor API consists of the following functional categories: +

    +

    +

    +In addition, the following subjects deserve attention additional to +the above general overview: +

    +

    +

    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + + diff --git a/libs/tremor/doc/reference.html b/libs/tremor/doc/reference.html new file mode 100644 index 0000000..20e0a5f --- /dev/null +++ b/libs/tremor/doc/reference.html @@ -0,0 +1,75 @@ + + + +Tremor API Reference + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    Tremor API Reference

    + +

    +Data Structures
    +OggVorbis_File
    +vorbis_comment
    +vorbis_info
    +ov_callbacks
    +
    +Setup/Teardown
    +ov_open()
    +ov_open_callbacks()
    +ov_clear()
    +ov_test()
    +ov_test_callbacks()
    +ov_test_open()
    +
    +Decoding
    +ov_read()
    +
    +Seeking
    +ov_raw_seek()
    +ov_pcm_seek()
    +ov_time_seek()
    +ov_pcm_seek_page()
    +ov_time_seek_page()
    +
    +File Information
    +ov_bitrate()
    +ov_bitrate_instant()
    +ov_streams()
    +ov_seekable()
    +ov_serialnumber()
    +ov_raw_total()
    +ov_pcm_total()
    +ov_time_total()
    +ov_raw_tell()
    +ov_pcm_tell()
    +ov_time_tell()
    +ov_info()
    +ov_comment()
    +
    +Return Codes
    + + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/return.html b/libs/tremor/doc/return.html new file mode 100644 index 0000000..0a3f96c --- /dev/null +++ b/libs/tremor/doc/return.html @@ -0,0 +1,77 @@ + + + +Tremor - Return Codes + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    Return Codes

    + +

    + +The following return codes are #defined in "ivorbiscodec.h" +may be returned by libvorbisidec. Descriptions of a code relevant to +a specific function are found in the reference description of that +function. + +

    + +
    OV_FALSE
    +
    Not true, or no data available
    + +
    OV_HOLE
    +
    Tremor encoutered missing or corrupt data in the bitstream. Recovery +is normally automatic and this return code is for informational purposes only.
    + +
    OV_EREAD
    +
    Read error while fetching compressed data for decode
    + +
    OV_EFAULT
    +
    Internal inconsistency in decode state. Continuing is likely not possible.
    + +
    OV_EIMPL
    +
    Feature not implemented
    + +
    OV_EINVAL
    +
    Either an invalid argument, or incompletely initialized argument passed to libvorbisidec call
    + +
    OV_ENOTVORBIS
    +
    The given file/data was not recognized as Ogg Vorbis data.
    + +
    OV_EBADHEADER
    +
    The file/data is apparently an Ogg Vorbis stream, but contains a corrupted or undecipherable header.
    + +
    OV_EVERSION
    +
    The bitstream format revision of the given stream is not supported.
    + +
    OV_EBADLINK
    +
    The given link exists in the Vorbis data stream, but is not decipherable due to garbacge or corruption.
    + +
    OV_ENOSEEK
    +
    The given stream is not seekable
    + +
    + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/seeking.html b/libs/tremor/doc/seeking.html new file mode 100644 index 0000000..652368a --- /dev/null +++ b/libs/tremor/doc/seeking.html @@ -0,0 +1,74 @@ + + + +Tremor - Seeking + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    Seeking

    +

    Seeking functions allow you to specify a specific point in the stream to begin or continue decoding. +

    +All libvorbisidec seeking routines are declared in "ivorbisfile.h". + +

    Certain seeking functions are best suited to different situations. +When speed is important and exact positioning isn't required, +page-level seeking should be used. Note also that Vorbis files do not +necessarily start at a sample number or time offset of zero. Do not +be surprised if a file begins at a positive offset of several minutes +or hours, such as would happen if a large stream (such as a concert +recording) is chopped into multiple separate files. Requesting to +seek to a position before the beginning of such a file will seek to +the position where audio begins.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    functionpurpose
    ov_raw_seekThis function seeks to a position specified in the compressed bitstream, specified in bytes.
    ov_pcm_seekThis function seeks to a specific audio sample number, specified in pcm samples.
    ov_pcm_seek_pageThis function seeks to the closest page preceding the specified audio sample number, specified in pcm samples.
    ov_time_seekThis function seeks to the specific time location in the bitstream, specified in integer milliseconds. Note that this differs from the reference vorbisfile implementation, which takes seconds as a float.
    ov_time_seek_pageThis function seeks to the closest page preceding the specified time position in the bitstream, specified in integer milliseconds.
    + +

    +


    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/style.css b/libs/tremor/doc/style.css new file mode 100644 index 0000000..81cf417 --- /dev/null +++ b/libs/tremor/doc/style.css @@ -0,0 +1,7 @@ +BODY { font-family: Helvetica, sans-serif } +TD { font-family: Helvetica, sans-serif } +P { font-family: Helvetica, sans-serif } +H1 { font-family: Helvetica, sans-serif } +H2 { font-family: Helvetica, sans-serif } +H4 { font-family: Helvetica, sans-serif } +P.tiny { font-size: 8pt } diff --git a/libs/tremor/doc/threads.html b/libs/tremor/doc/threads.html new file mode 100644 index 0000000..53ed76a --- /dev/null +++ b/libs/tremor/doc/threads.html @@ -0,0 +1,50 @@ + + + +Tremor - Thread Safety + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    Thread Safety

    + +Tremor's libvorbisidec may be used safely in a threading environment +so long as thread access to individual OggVorbis_File instances is serialized. +
      + +
    • Only one thread at a time may enter a function that takes a given OggVorbis_File instance, even if the +functions involved appear to be read-only.

      + +

    • Multiple threads may enter +libvorbisidec at a given time, so long as each thread's function calls +are using different OggVorbis_File +instances.

      + +

    • Any one OggVorbis_File instance may be used safely from multiple threads so long as only one thread at a time is making calls using that instance.

      +

    + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/vorbis_comment.html b/libs/tremor/doc/vorbis_comment.html new file mode 100644 index 0000000..3232d96 --- /dev/null +++ b/libs/tremor/doc/vorbis_comment.html @@ -0,0 +1,70 @@ + + + +Tremor - datatype - vorbis_comment + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    vorbis_comment

    + +

    declared in "ivorbiscodec.h"

    + +

    +The vorbis_comment structure defines an Ogg Vorbis comment. +

    +Only the fields the program needs must be defined. If a field isn't +defined by the application, it will either be blank (if it's a string value) +or set to some reasonable default (usually 0). +

    + + + + + +
    +
    typedef struct vorbis_comment{
    +  /* unlimited user comment fields. */
    +  char **user_comments;
    +  int  *comment_lengths;
    +  int  comments;
    +  char *vendor;
    +
    +} vorbis_comment;
    +
    + +

    Parameters

    +
    +
    user_comments
    +
    Unlimited user comment array. The individual strings in the array are 8 bit clean, by the Vorbis specification, and as such the comment_lengths array should be consulted to determine string length. For convenience, each string is also NULL-terminated by the decode library (although Vorbis comments are not NULL terminated within the bitstream itself).
    +
    comment_lengths
    +
    An int array that stores the length of each comment string
    +
    comments
    +
    Int signifying number of user comments in user_comments field.
    +
    vendor
    +
    Information about the creator of the file. Stored in a standard C 0-terminated string.
    +
    + + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/doc/vorbis_info.html b/libs/tremor/doc/vorbis_info.html new file mode 100644 index 0000000..bd938cd --- /dev/null +++ b/libs/tremor/doc/vorbis_info.html @@ -0,0 +1,80 @@ + + + +Tremor - datatype - vorbis_info + + + + + + + + + +

    Tremor documentation

    Tremor version 1.0 - 20020403

    + +

    vorbis_info

    + +

    declared in "ivorbiscodec.h"

    + +

    +The vorbis_info structure contains basic information about the audio in a vorbis bitstream. +

    + + + + + +
    +
    typedef struct vorbis_info{
    +  int version;
    +  int channels;
    +  long rate;
    +  
    +  long bitrate_upper;
    +  long bitrate_nominal;
    +  long bitrate_lower;
    +  long bitrate_window;
    +
    +  void *codec_setup;
    +
    +} vorbis_info;
    +
    + +

    Relevant Struct Members

    +
    +
    version
    +
    Vorbis encoder version used to create this bitstream.
    +
    channels
    +
    Int signifying number of channels in bitstream.
    +
    rate
    +
    Sampling rate of the bitstream.
    +
    bitrate_upper
    +
    Specifies the upper limit in a VBR bitstream. If the value matches the bitrate_nominal and bitrate_lower parameters, the stream is fixed bitrate. May be unset if no limit exists.
    +
    bitrate_nominal
    +
    Specifies the average bitrate for a VBR bitstream. May be unset. If the bitrate_upper and bitrate_lower parameters match, the stream is fixed bitrate.
    +
    bitrate_lower
    +
    Specifies the lower limit in a VBR bitstream. If the value matches the bitrate_nominal and bitrate_upper parameters, the stream is fixed bitrate. May be unset if no limit exists.
    +
    bitrate_window
    +
    Currently unset.
    + +
    codec_setup
    +
    Internal structure that contains the detailed/unpacked configuration for decoding the current Vorbis bitstream.
    +
    + + +

    +
    + + + + + + + + +

    copyright © 2002 Xiph.org

    Ogg Vorbis

    Tremor documentation

    Tremor version 1.0 - 20020403

    + + + + diff --git a/libs/tremor/floor0.c b/libs/tremor/floor0.c new file mode 100644 index 0000000..964383e --- /dev/null +++ b/libs/tremor/floor0.c @@ -0,0 +1,439 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: floor backend 0 implementation + + ********************************************************************/ + +#include +#include +#include +#include +#include "ivorbiscodec.h" +#include "codec_internal.h" +#include "registry.h" +#include "codebook.h" +#include "misc.h" +#include "block.h" + +#define LSP_FRACBITS 14 + +typedef struct { + long n; + int ln; + int m; + int *linearmap; + + vorbis_info_floor0 *vi; + ogg_int32_t *lsp_look; + +} vorbis_look_floor0; + +/*************** LSP decode ********************/ + +#include "lsp_lookup.h" + +/* interpolated 1./sqrt(p) where .5 <= a < 1. (.100000... to .111111...) in + 16.16 format + returns in m.8 format */ + +static long ADJUST_SQRT2[2]={8192,5792}; +STIN ogg_int32_t vorbis_invsqlook_i(long a,long e){ + long i=(a&0x7fff)>>(INVSQ_LOOKUP_I_SHIFT-1); + long d=a&INVSQ_LOOKUP_I_MASK; /* 0.10 */ + long val=INVSQ_LOOKUP_I[i]- /* 1.16 */ + ((INVSQ_LOOKUP_IDel[i]*d)>>INVSQ_LOOKUP_I_SHIFT); /* result 1.16 */ + val*=ADJUST_SQRT2[e&1]; + e=(e>>1)+21; + return(val>>e); +} + +/* interpolated lookup based fromdB function, domain -140dB to 0dB only */ +/* a is in n.12 format */ +STIN ogg_int32_t vorbis_fromdBlook_i(long a){ + int i=(-a)>>(12-FROMdB2_SHIFT); + if(i<0) return 0x7fffffff; + if(i>=(FROMdB_LOOKUP_SZ<>FROMdB_SHIFT] * FROMdB2_LOOKUP[i&FROMdB2_MASK]; +} + +/* interpolated lookup based cos function, domain 0 to PI only */ +/* a is in 0.16 format, where 0==0, 2^^16-1==PI, return 0.14 */ +STIN ogg_int32_t vorbis_coslook_i(long a){ + int i=a>>COS_LOOKUP_I_SHIFT; + int d=a&COS_LOOKUP_I_MASK; + return COS_LOOKUP_I[i]- ((d*(COS_LOOKUP_I[i]-COS_LOOKUP_I[i+1]))>> + COS_LOOKUP_I_SHIFT); +} + +/* interpolated lookup based cos function */ +/* a is in 0.16 format, where 0==0, 2^^16==PI, return .LSP_FRACBITS */ +STIN ogg_int32_t vorbis_coslook2_i(long a){ + a=a&0x1ffff; + + if(a>0x10000)a=0x20000-a; + { + int i=a>>COS_LOOKUP_I_SHIFT; + int d=a&COS_LOOKUP_I_MASK; + a=((COS_LOOKUP_I[i]<> + (COS_LOOKUP_I_SHIFT-LSP_FRACBITS+14); + } + + return(a); +} + +static const int barklook[28]={ + 0,100,200,301, 405,516,635,766, + 912,1077,1263,1476, 1720,2003,2333,2721, + 3184,3742,4428,5285, 6376,7791,9662,12181, + 15624,20397,27087,36554 +}; + +/* used in init only; interpolate the long way */ +STIN ogg_int32_t toBARK(int n){ + int i; + for(i=0;i<27;i++) + if(n>=barklook[i] && n>10)*0x517d)>>14; +#endif + + /* safeguard against a malicious stream */ + if(val<0 || (val>>COS_LOOKUP_I_SHIFT)>=COS_LOOKUP_I_SZ){ + memset(curve,0,sizeof(*curve)*n); + return; + } + + ilsp[i]=vorbis_coslook_i(val); + } + + i=0; + while(i>16); + qi=((qi*qi)>>16); + + if(m&1){ + qexp= qexp*2-28*((m+1)>>1)+m; + pi*=(1<<14)-((wi*wi)>>14); + qi+=pi>>14; + }else{ + qexp= qexp*2-13*m; + + pi*=(1<<14)-wi; + qi*=(1<<14)+wi; + + qi=(qi+pi)>>14; + } + + if(qi&0xffff0000){ /* checks for 1.xxxxxxxxxxxxxxxx */ + qi>>=1; qexp++; + }else + lsp_norm_asm(&qi,&qexp); + +#else + + j=1; + if(m>1){ + qi*=labs(ilsp[0]-wi); + pi*=labs(ilsp[1]-wi); + + for(j+=2;j>25])) + if(!(shift=MLOOP_2[(pi|qi)>>19])) + shift=MLOOP_3[(pi|qi)>>16]; + qi=(qi>>shift)*labs(ilsp[j-1]-wi); + pi=(pi>>shift)*labs(ilsp[j]-wi); + qexp+=shift; + } + } + if(!(shift=MLOOP_1[(pi|qi)>>25])) + if(!(shift=MLOOP_2[(pi|qi)>>19])) + shift=MLOOP_3[(pi|qi)>>16]; + + /* pi,qi normalized collectively, both tracked using qexp */ + + if(m&1){ + /* odd order filter; slightly assymetric */ + /* the last coefficient */ + qi=(qi>>shift)*labs(ilsp[j-1]-wi); + pi=(pi>>shift)<<14; + qexp+=shift; + + if(!(shift=MLOOP_1[(pi|qi)>>25])) + if(!(shift=MLOOP_2[(pi|qi)>>19])) + shift=MLOOP_3[(pi|qi)>>16]; + + pi>>=shift; + qi>>=shift; + qexp+=shift-14*((m+1)>>1); + + pi=((pi*pi)>>16); + qi=((qi*qi)>>16); + qexp=qexp*2+m; + + pi*=(1<<14)-((wi*wi)>>14); + qi+=pi>>14; + + }else{ + /* even order filter; still symmetric */ + + /* p*=p(1-w), q*=q(1+w), let normalization drift because it isn't + worth tracking step by step */ + + pi>>=shift; + qi>>=shift; + qexp+=shift-7*m; + + pi=((pi*pi)>>16); + qi=((qi*qi)>>16); + qexp=qexp*2+m; + + pi*=(1<<14)-wi; + qi*=(1<<14)+wi; + qi=(qi+pi)>>14; + + } + + + /* we've let the normalization drift because it wasn't important; + however, for the lookup, things must be normalized again. We + need at most one right shift or a number of left shifts */ + + if(qi&0xffff0000){ /* checks for 1.xxxxxxxxxxxxxxxx */ + qi>>=1; qexp++; + }else + while(qi && !(qi&0x8000)){ /* checks for 0.0xxxxxxxxxxxxxxx or less*/ + qi<<=1; qexp--; + } + +#endif + + amp=vorbis_fromdBlook_i(ampi* /* n.4 */ + vorbis_invsqlook_i(qi,qexp)- + /* m.8, m+n<=8 */ + ampoffseti); /* 8.12[0] */ + +#ifdef _LOW_ACCURACY_ + amp>>=9; +#endif + curve[i]= MULT31_SHIFT15(curve[i],amp); + while(map[++i]==k) curve[i]= MULT31_SHIFT15(curve[i],amp); + } +} + +/*************** vorbis decode glue ************/ + +static void floor0_free_info(vorbis_info_floor *i){ + vorbis_info_floor0 *info=(vorbis_info_floor0 *)i; + if(info){ + memset(info,0,sizeof(*info)); + _ogg_free(info); + } +} + +static void floor0_free_look(vorbis_look_floor *i){ + vorbis_look_floor0 *look=(vorbis_look_floor0 *)i; + if(look){ + + if(look->linearmap)_ogg_free(look->linearmap); + if(look->lsp_look)_ogg_free(look->lsp_look); + memset(look,0,sizeof(*look)); + _ogg_free(look); + } +} + +static vorbis_info_floor *floor0_unpack (vorbis_info *vi,oggpack_buffer *opb){ + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + int j; + + vorbis_info_floor0 *info=(vorbis_info_floor0 *)_ogg_malloc(sizeof(*info)); + info->order=oggpack_read(opb,8); + info->rate=oggpack_read(opb,16); + info->barkmap=oggpack_read(opb,16); + info->ampbits=oggpack_read(opb,6); + info->ampdB=oggpack_read(opb,8); + info->numbooks=oggpack_read(opb,4)+1; + + if(info->order<1)goto err_out; + if(info->rate<1)goto err_out; + if(info->barkmap<1)goto err_out; + if(info->numbooks<1)goto err_out; + + for(j=0;jnumbooks;j++){ + info->books[j]=oggpack_read(opb,8); + if(info->books[j]<0 || info->books[j]>=ci->books)goto err_out; + if(ci->book_param[info->books[j]]->maptype==0)goto err_out; + if(ci->book_param[info->books[j]]->dim<1)goto err_out; + } + return(info); + + err_out: + floor0_free_info(info); + return(NULL); +} + +/* initialize Bark scale and normalization lookups. We could do this + with static tables, but Vorbis allows a number of possible + combinations, so it's best to do it computationally. + + The below is authoritative in terms of defining scale mapping. + Note that the scale depends on the sampling rate as well as the + linear block and mapping sizes */ + +static vorbis_look_floor *floor0_look (vorbis_dsp_state *vd,vorbis_info_mode *mi, + vorbis_info_floor *i){ + int j; + vorbis_info *vi=vd->vi; + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + vorbis_info_floor0 *info=(vorbis_info_floor0 *)i; + vorbis_look_floor0 *look=(vorbis_look_floor0 *)_ogg_calloc(1,sizeof(*look)); + look->m=info->order; + look->n=ci->blocksizes[mi->blockflag]/2; + look->ln=info->barkmap; + look->vi=info; + + /* the mapping from a linear scale to a smaller bark scale is + straightforward. We do *not* make sure that the linear mapping + does not skip bark-scale bins; the decoder simply skips them and + the encoder may do what it wishes in filling them. They're + necessary in some mapping combinations to keep the scale spacing + accurate */ + look->linearmap=(int *)_ogg_malloc((look->n+1)*sizeof(*look->linearmap)); + for(j=0;jn;j++){ + + int val=(look->ln* + ((toBARK(info->rate/2*j/look->n)<<11)/toBARK(info->rate/2)))>>11; + + if(val>=look->ln)val=look->ln-1; /* guard against the approximation */ + look->linearmap[j]=val; + } + look->linearmap[j]=-1; + + look->lsp_look=(ogg_int32_t *)_ogg_malloc(look->ln*sizeof(*look->lsp_look)); + for(j=0;jln;j++) + look->lsp_look[j]=vorbis_coslook2_i(0x10000*j/look->ln); + + return look; +} + +static void *floor0_inverse1(vorbis_block *vb,vorbis_look_floor *i){ + vorbis_look_floor0 *look=(vorbis_look_floor0 *)i; + vorbis_info_floor0 *info=look->vi; + int j,k; + + int ampraw=oggpack_read(&vb->opb,info->ampbits); + if(ampraw>0){ /* also handles the -1 out of data case */ + long maxval=(1<ampbits)-1; + int amp=((ampraw*info->ampdB)<<4)/maxval; + int booknum=oggpack_read(&vb->opb,_ilog(info->numbooks)); + + if(booknum!=-1 && booknumnumbooks){ /* be paranoid */ + codec_setup_info *ci=(codec_setup_info *)vb->vd->vi->codec_setup; + codebook *b=ci->fullbooks+info->books[booknum]; + ogg_int32_t last=0; + ogg_int32_t *lsp=(ogg_int32_t *)_vorbis_block_alloc(vb,sizeof(*lsp)*(look->m+1)); + + if(vorbis_book_decodev_set(b,lsp,&vb->opb,look->m,-24)==-1)goto eop; + for(j=0;jm;){ + for(k=0;jm && kdim;k++,j++)lsp[j]+=last; + last=lsp[j-1]; + } + + lsp[look->m]=amp; + return(lsp); + } + } + eop: + return(NULL); +} + +static int floor0_inverse2(vorbis_block *vb,vorbis_look_floor *i, + void *memo,ogg_int32_t *out){ + vorbis_look_floor0 *look=(vorbis_look_floor0 *)i; + vorbis_info_floor0 *info=look->vi; + + if(memo){ + ogg_int32_t *lsp=(ogg_int32_t *)memo; + ogg_int32_t amp=lsp[look->m]; + + /* take the coefficients back to a spectral envelope curve */ + vorbis_lsp_to_curve(out,look->linearmap,look->n,look->ln, + lsp,look->m,amp,info->ampdB,look->lsp_look); + return(1); + } + memset(out,0,sizeof(*out)*look->n); + return(0); +} + +/* export hooks */ +vorbis_func_floor floor0_exportbundle={ + &floor0_unpack,&floor0_look,&floor0_free_info, + &floor0_free_look,&floor0_inverse1,&floor0_inverse2 +}; + + diff --git a/libs/tremor/floor1.c b/libs/tremor/floor1.c new file mode 100644 index 0000000..e63ae9f --- /dev/null +++ b/libs/tremor/floor1.c @@ -0,0 +1,460 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: floor backend 1 implementation + + ********************************************************************/ + +#include +#include +#include +#include +#include "ivorbiscodec.h" +#include "codec_internal.h" +#include "registry.h" +#include "codebook.h" +#include "misc.h" +#include "block.h" + +#define floor1_rangedB 140 /* floor 1 fixed at -140dB to 0dB range */ + +typedef struct { + int forward_index[VIF_POSIT+2]; + + int hineighbor[VIF_POSIT]; + int loneighbor[VIF_POSIT]; + int posts; + + int n; + int quant_q; + vorbis_info_floor1 *vi; + +} vorbis_look_floor1; + +/***********************************************/ + +static void floor1_free_info(vorbis_info_floor *i){ + vorbis_info_floor1 *info=(vorbis_info_floor1 *)i; + if(info){ + memset(info,0,sizeof(*info)); + _ogg_free(info); + } +} + +static void floor1_free_look(vorbis_look_floor *i){ + vorbis_look_floor1 *look=(vorbis_look_floor1 *)i; + if(look){ + memset(look,0,sizeof(*look)); + _ogg_free(look); + } +} + +static int ilog(unsigned int v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +static int icomp(const void *a,const void *b){ + return(**(int **)a-**(int **)b); +} + +static vorbis_info_floor *floor1_unpack (vorbis_info *vi,oggpack_buffer *opb){ + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + int j,k,count=0,maxclass=-1,rangebits; + + vorbis_info_floor1 *info=(vorbis_info_floor1 *)_ogg_calloc(1,sizeof(*info)); + /* read partitions */ + info->partitions=oggpack_read(opb,5); /* only 0 to 31 legal */ + for(j=0;jpartitions;j++){ + info->partitionclass[j]=oggpack_read(opb,4); /* only 0 to 15 legal */ + if(info->partitionclass[j]<0)goto err_out; + if(maxclasspartitionclass[j])maxclass=info->partitionclass[j]; + } + + /* read partition classes */ + for(j=0;jclass_dim[j]=oggpack_read(opb,3)+1; /* 1 to 8 */ + info->class_subs[j]=oggpack_read(opb,2); /* 0,1,2,3 bits */ + if(info->class_subs[j]<0) + goto err_out; + if(info->class_subs[j])info->class_book[j]=oggpack_read(opb,8); + if(info->class_book[j]<0 || info->class_book[j]>=ci->books) + goto err_out; + for(k=0;k<(1<class_subs[j]);k++){ + info->class_subbook[j][k]=oggpack_read(opb,8)-1; + if(info->class_subbook[j][k]<-1 || info->class_subbook[j][k]>=ci->books) + goto err_out; + } + } + + /* read the post list */ + info->mult=oggpack_read(opb,2)+1; /* only 1,2,3,4 legal now */ + rangebits=oggpack_read(opb,4); + if(rangebits<0)goto err_out; + + for(j=0,k=0;jpartitions;j++){ + count+=info->class_dim[info->partitionclass[j]]; + if(count>VIF_POSIT)goto err_out; + for(;kpostlist[k+2]=oggpack_read(opb,rangebits); + if(t<0 || t>=(1<postlist[0]=0; + info->postlist[1]=1<postlist+j; + qsort(sortpointer,count+2,sizeof(*sortpointer),icomp); + + for(j=1;jvi=info; + look->n=info->postlist[1]; + + /* we drop each position value in-between already decoded values, + and use linear interpolation to predict each new value past the + edges. The positions are read in the order of the position + list... we precompute the bounding positions in the lookup. Of + course, the neighbors can change (if a position is declined), but + this is an initial mapping */ + + for(i=0;ipartitions;i++)n+=info->class_dim[info->partitionclass[i]]; + n+=2; + look->posts=n; + + /* also store a sorted position index */ + for(i=0;ipostlist+i; + qsort(sortpointer,n,sizeof(*sortpointer),icomp); + + /* points from sort order back to range number */ + for(i=0;iforward_index[i]=sortpointer[i]-info->postlist; + + /* quantize values to multiplier spec */ + switch(info->mult){ + case 1: /* 1024 -> 256 */ + look->quant_q=256; + break; + case 2: /* 1024 -> 128 */ + look->quant_q=128; + break; + case 3: /* 1024 -> 86 */ + look->quant_q=86; + break; + case 4: /* 1024 -> 64 */ + look->quant_q=64; + break; + } + + /* discover our neighbors for decode where we don't use fit flags + (that would push the neighbors outward) */ + for(i=0;in; + int currentx=info->postlist[i+2]; + for(j=0;jpostlist[j]; + if(x>lx && xcurrentx){ + hi=j; + hx=x; + } + } + look->loneighbor[i]=lo; + look->hineighbor[i]=hi; + } + + return(look); +} + +static int render_point(int x0,int x1,int y0,int y1,int x){ + y0&=0x7fff; /* mask off flag */ + y1&=0x7fff; + + { + int dy=y1-y0; + int adx=x1-x0; + int ady=abs(dy); + int err=ady*(x-x0); + + int off=err/adx; + if(dy<0)return(y0-off); + return(y0+off); + } +} + +#ifdef _LOW_ACCURACY_ +# define XdB(n) ((((n)>>8)+1)>>1) +#else +# define XdB(n) (n) +#endif + +static const ogg_int32_t FLOOR_fromdB_LOOKUP[256]={ + XdB(0x000000e5), XdB(0x000000f4), XdB(0x00000103), XdB(0x00000114), + XdB(0x00000126), XdB(0x00000139), XdB(0x0000014e), XdB(0x00000163), + XdB(0x0000017a), XdB(0x00000193), XdB(0x000001ad), XdB(0x000001c9), + XdB(0x000001e7), XdB(0x00000206), XdB(0x00000228), XdB(0x0000024c), + XdB(0x00000272), XdB(0x0000029b), XdB(0x000002c6), XdB(0x000002f4), + XdB(0x00000326), XdB(0x0000035a), XdB(0x00000392), XdB(0x000003cd), + XdB(0x0000040c), XdB(0x00000450), XdB(0x00000497), XdB(0x000004e4), + XdB(0x00000535), XdB(0x0000058c), XdB(0x000005e8), XdB(0x0000064a), + XdB(0x000006b3), XdB(0x00000722), XdB(0x00000799), XdB(0x00000818), + XdB(0x0000089e), XdB(0x0000092e), XdB(0x000009c6), XdB(0x00000a69), + XdB(0x00000b16), XdB(0x00000bcf), XdB(0x00000c93), XdB(0x00000d64), + XdB(0x00000e43), XdB(0x00000f30), XdB(0x0000102d), XdB(0x0000113a), + XdB(0x00001258), XdB(0x0000138a), XdB(0x000014cf), XdB(0x00001629), + XdB(0x0000179a), XdB(0x00001922), XdB(0x00001ac4), XdB(0x00001c82), + XdB(0x00001e5c), XdB(0x00002055), XdB(0x0000226f), XdB(0x000024ac), + XdB(0x0000270e), XdB(0x00002997), XdB(0x00002c4b), XdB(0x00002f2c), + XdB(0x0000323d), XdB(0x00003581), XdB(0x000038fb), XdB(0x00003caf), + XdB(0x000040a0), XdB(0x000044d3), XdB(0x0000494c), XdB(0x00004e10), + XdB(0x00005323), XdB(0x0000588a), XdB(0x00005e4b), XdB(0x0000646b), + XdB(0x00006af2), XdB(0x000071e5), XdB(0x0000794c), XdB(0x0000812e), + XdB(0x00008993), XdB(0x00009283), XdB(0x00009c09), XdB(0x0000a62d), + XdB(0x0000b0f9), XdB(0x0000bc79), XdB(0x0000c8b9), XdB(0x0000d5c4), + XdB(0x0000e3a9), XdB(0x0000f274), XdB(0x00010235), XdB(0x000112fd), + XdB(0x000124dc), XdB(0x000137e4), XdB(0x00014c29), XdB(0x000161bf), + XdB(0x000178bc), XdB(0x00019137), XdB(0x0001ab4a), XdB(0x0001c70e), + XdB(0x0001e4a1), XdB(0x0002041f), XdB(0x000225aa), XdB(0x00024962), + XdB(0x00026f6d), XdB(0x000297f0), XdB(0x0002c316), XdB(0x0002f109), + XdB(0x000321f9), XdB(0x00035616), XdB(0x00038d97), XdB(0x0003c8b4), + XdB(0x000407a7), XdB(0x00044ab2), XdB(0x00049218), XdB(0x0004de23), + XdB(0x00052f1e), XdB(0x0005855c), XdB(0x0005e135), XdB(0x00064306), + XdB(0x0006ab33), XdB(0x00071a24), XdB(0x0007904b), XdB(0x00080e20), + XdB(0x00089422), XdB(0x000922da), XdB(0x0009bad8), XdB(0x000a5cb6), + XdB(0x000b091a), XdB(0x000bc0b1), XdB(0x000c8436), XdB(0x000d5471), + XdB(0x000e3233), XdB(0x000f1e5f), XdB(0x001019e4), XdB(0x001125c1), + XdB(0x00124306), XdB(0x001372d5), XdB(0x0014b663), XdB(0x00160ef7), + XdB(0x00177df0), XdB(0x001904c1), XdB(0x001aa4f9), XdB(0x001c603d), + XdB(0x001e384f), XdB(0x00202f0f), XdB(0x0022467a), XdB(0x002480b1), + XdB(0x0026dff7), XdB(0x002966b3), XdB(0x002c1776), XdB(0x002ef4fc), + XdB(0x0032022d), XdB(0x00354222), XdB(0x0038b828), XdB(0x003c67c2), + XdB(0x004054ae), XdB(0x004482e8), XdB(0x0048f6af), XdB(0x004db488), + XdB(0x0052c142), XdB(0x005821ff), XdB(0x005ddc33), XdB(0x0063f5b0), + XdB(0x006a74a7), XdB(0x00715faf), XdB(0x0078bdce), XdB(0x0080967f), + XdB(0x0088f1ba), XdB(0x0091d7f9), XdB(0x009b5247), XdB(0x00a56a41), + XdB(0x00b02a27), XdB(0x00bb9ce2), XdB(0x00c7ce12), XdB(0x00d4ca17), + XdB(0x00e29e20), XdB(0x00f15835), XdB(0x0101074b), XdB(0x0111bb4e), + XdB(0x01238531), XdB(0x01367704), XdB(0x014aa402), XdB(0x016020a7), + XdB(0x017702c3), XdB(0x018f6190), XdB(0x01a955cb), XdB(0x01c4f9cf), + XdB(0x01e269a8), XdB(0x0201c33b), XdB(0x0223265a), XdB(0x0246b4ea), + XdB(0x026c9302), XdB(0x0294e716), XdB(0x02bfda13), XdB(0x02ed9793), + XdB(0x031e4e09), XdB(0x03522ee4), XdB(0x03896ed0), XdB(0x03c445e2), + XdB(0x0402efd6), XdB(0x0445ac4b), XdB(0x048cbefc), XdB(0x04d87013), + XdB(0x05290c67), XdB(0x057ee5ca), XdB(0x05da5364), XdB(0x063bb204), + XdB(0x06a36485), XdB(0x0711d42b), XdB(0x0787710e), XdB(0x0804b299), + XdB(0x088a17ef), XdB(0x0918287e), XdB(0x09af747c), XdB(0x0a50957e), + XdB(0x0afc2f19), XdB(0x0bb2ef7f), XdB(0x0c759034), XdB(0x0d44d6ca), + XdB(0x0e2195bc), XdB(0x0f0cad0d), XdB(0x10070b62), XdB(0x1111aeea), + XdB(0x122da66c), XdB(0x135c120f), XdB(0x149e24d9), XdB(0x15f525b1), + XdB(0x176270e3), XdB(0x18e7794b), XdB(0x1a85c9ae), XdB(0x1c3f06d1), + XdB(0x1e14f07d), XdB(0x200963d7), XdB(0x221e5ccd), XdB(0x2455f870), + XdB(0x26b2770b), XdB(0x29363e2b), XdB(0x2be3db5c), XdB(0x2ebe06b6), + XdB(0x31c7a55b), XdB(0x3503ccd4), XdB(0x3875c5aa), XdB(0x3c210f44), + XdB(0x4009632b), XdB(0x4432b8cf), XdB(0x48a149bc), XdB(0x4d59959e), + XdB(0x52606733), XdB(0x57bad899), XdB(0x5d6e593a), XdB(0x6380b298), + XdB(0x69f80e9a), XdB(0x70dafda8), XdB(0x78307d76), XdB(0x7fffffff), +}; + +static void render_line(int n, int x0,int x1,int y0,int y1,ogg_int32_t *d){ + int dy=y1-y0; + int adx=x1-x0; + int ady=abs(dy); + int base=dy/adx; + int sy=(dy<0?base-1:base+1); + int x=x0; + int y=y0; + int err=0; + + if(n>x1)n=x1; + ady-=abs(base*adx); + + if(x=adx){ + err-=adx; + y+=sy; + }else{ + y+=base; + } + d[x]= MULT31_SHIFT15(d[x],FLOOR_fromdB_LOOKUP[y]); + } +} + +static void *floor1_inverse1(vorbis_block *vb,vorbis_look_floor *in){ + vorbis_look_floor1 *look=(vorbis_look_floor1 *)in; + vorbis_info_floor1 *info=look->vi; + codec_setup_info *ci=(codec_setup_info *)vb->vd->vi->codec_setup; + + int i,j,k; + codebook *books=ci->fullbooks; + + /* unpack wrapped/predicted values from stream */ + if(oggpack_read(&vb->opb,1)==1){ + int *fit_value=(int *)_vorbis_block_alloc(vb,(look->posts)*sizeof(*fit_value)); + + fit_value[0]=oggpack_read(&vb->opb,ilog(look->quant_q-1)); + fit_value[1]=oggpack_read(&vb->opb,ilog(look->quant_q-1)); + + /* partition by partition */ + /* partition by partition */ + for(i=0,j=2;ipartitions;i++){ + int classv=info->partitionclass[i]; + int cdim=info->class_dim[classv]; + int csubbits=info->class_subs[classv]; + int csub=1<class_book[classv],&vb->opb); + + if(cval==-1)goto eop; + } + + for(k=0;kclass_subbook[classv][cval&(csub-1)]; + cval>>=csubbits; + if(book>=0){ + if((fit_value[j+k]=vorbis_book_decode(books+book,&vb->opb))==-1) + goto eop; + }else{ + fit_value[j+k]=0; + } + } + j+=cdim; + } + + /* unwrap positive values and reconsitute via linear interpolation */ + for(i=2;iposts;i++){ + int predicted=render_point(info->postlist[look->loneighbor[i-2]], + info->postlist[look->hineighbor[i-2]], + fit_value[look->loneighbor[i-2]], + fit_value[look->hineighbor[i-2]], + info->postlist[i]); + int hiroom=look->quant_q-predicted; + int loroom=predicted; + int room=(hiroom=room){ + if(hiroom>loroom){ + val = val-loroom; + }else{ + val = -1-(val-hiroom); + } + }else{ + if(val&1){ + val= -((val+1)>>1); + }else{ + val>>=1; + } + } + + fit_value[i]=(val+predicted)&0x7fff;; + fit_value[look->loneighbor[i-2]]&=0x7fff; + fit_value[look->hineighbor[i-2]]&=0x7fff; + + }else{ + fit_value[i]=predicted|0x8000; + } + + } + + return(fit_value); + } + eop: + return(NULL); +} + +static int floor1_inverse2(vorbis_block *vb,vorbis_look_floor *in,void *memo, + ogg_int32_t *out){ + vorbis_look_floor1 *look=(vorbis_look_floor1 *)in; + vorbis_info_floor1 *info=look->vi; + + codec_setup_info *ci=(codec_setup_info *)vb->vd->vi->codec_setup; + int n=ci->blocksizes[vb->W]/2; + int j; + + if(memo){ + /* render the lines */ + int *fit_value=(int *)memo; + int hx=0; + int lx=0; + int ly=fit_value[0]*info->mult; + /* guard lookup against out-of-range values */ + ly=(ly<0?0:ly>255?255:ly); + + for(j=1;jposts;j++){ + int current=look->forward_index[j]; + int hy=fit_value[current]&0x7fff; + if(hy==fit_value[current]){ + + hx=info->postlist[current]; + hy*=info->mult; + /* guard lookup against out-of-range values */ + hy=(hy<0?0:hy>255?255:hy); + + render_line(n,lx,hx,ly,hy,out); + + lx=hx; + ly=hy; + } + } + for(j=hx;j header packets + + ********************************************************************/ + +/* general handling of the header and the vorbis_info structure (and + substructures) */ + +#include +#include +#include +#include +#include +#include "ivorbiscodec.h" +#include "codec_internal.h" +#include "codebook.h" +#include "registry.h" +#include "window.h" +#include "misc.h" + +/* helpers */ +static void _v_readstring(oggpack_buffer *o,char *buf,int bytes){ + while(bytes--){ + *buf++=oggpack_read(o,8); + } +} + +void vorbis_comment_init(vorbis_comment *vc){ + memset(vc,0,sizeof(*vc)); +} + +/* This is more or less the same as strncasecmp - but that doesn't exist + * everywhere, and this is a fairly trivial function, so we include it */ +static int tagcompare(const char *s1, const char *s2, int n){ + int c=0; + while(c < n){ + if(toupper(s1[c]) != toupper(s2[c])) + return !0; + c++; + } + return 0; +} + +char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count){ + long i; + int found = 0; + int taglen = strlen(tag)+1; /* +1 for the = we append */ + char *fulltag = (char *)alloca(taglen+ 1); + + strcpy(fulltag, tag); + strcat(fulltag, "="); + + for(i=0;icomments;i++){ + if(!tagcompare(vc->user_comments[i], fulltag, taglen)){ + if(count == found) + /* We return a pointer to the data, not a copy */ + return vc->user_comments[i] + taglen; + else + found++; + } + } + return NULL; /* didn't find anything */ +} + +int vorbis_comment_query_count(vorbis_comment *vc, char *tag){ + int i,count=0; + int taglen = strlen(tag)+1; /* +1 for the = we append */ + char *fulltag = (char *)alloca(taglen+1); + strcpy(fulltag,tag); + strcat(fulltag, "="); + + for(i=0;icomments;i++){ + if(!tagcompare(vc->user_comments[i], fulltag, taglen)) + count++; + } + + return count; +} + +void vorbis_comment_clear(vorbis_comment *vc){ + if(vc){ + long i; + if(vc->user_comments){ + for(i=0;icomments;i++) + if(vc->user_comments[i])_ogg_free(vc->user_comments[i]); + _ogg_free(vc->user_comments); + } + if(vc->comment_lengths)_ogg_free(vc->comment_lengths); + if(vc->vendor)_ogg_free(vc->vendor); + memset(vc,0,sizeof(*vc)); + } +} + +/* blocksize 0 is guaranteed to be short, 1 is guarantted to be long. + They may be equal, but short will never ge greater than long */ +int vorbis_info_blocksize(vorbis_info *vi,int zo){ + codec_setup_info *ci = (codec_setup_info *)vi->codec_setup; + return ci ? ci->blocksizes[zo] : -1; +} + +/* used by synthesis, which has a full, alloced vi */ +void vorbis_info_init(vorbis_info *vi){ + memset(vi,0,sizeof(*vi)); + vi->codec_setup=(codec_setup_info *)_ogg_calloc(1,sizeof(codec_setup_info)); +} + +void vorbis_info_clear(vorbis_info *vi){ + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + int i; + + if(ci){ + + for(i=0;imodes;i++) + if(ci->mode_param[i])_ogg_free(ci->mode_param[i]); + + for(i=0;imaps;i++) /* unpack does the range checking */ + if(ci->map_param[i]) + _mapping_P[ci->map_type[i]]->free_info(ci->map_param[i]); + + for(i=0;ifloors;i++) /* unpack does the range checking */ + if(ci->floor_param[i]) + _floor_P[ci->floor_type[i]]->free_info(ci->floor_param[i]); + + for(i=0;iresidues;i++) /* unpack does the range checking */ + if(ci->residue_param[i]) + _residue_P[ci->residue_type[i]]->free_info(ci->residue_param[i]); + + for(i=0;ibooks;i++){ + if(ci->book_param[i]){ + /* knows if the book was not alloced */ + vorbis_staticbook_destroy(ci->book_param[i]); + } + if(ci->fullbooks) + vorbis_book_clear(ci->fullbooks+i); + } + if(ci->fullbooks) + _ogg_free(ci->fullbooks); + + _ogg_free(ci); + } + + memset(vi,0,sizeof(*vi)); +} + +/* Header packing/unpacking ********************************************/ + +static int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb){ + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + if(!ci)return(OV_EFAULT); + + vi->version=oggpack_read(opb,32); + if(vi->version!=0)return(OV_EVERSION); + + vi->channels=oggpack_read(opb,8); + vi->rate=oggpack_read(opb,32); + + vi->bitrate_upper=oggpack_read(opb,32); + vi->bitrate_nominal=oggpack_read(opb,32); + vi->bitrate_lower=oggpack_read(opb,32); + + ci->blocksizes[0]=1<blocksizes[1]=1<rate<1)goto err_out; + if(vi->channels<1)goto err_out; + if(ci->blocksizes[0]<64)goto err_out; + if(ci->blocksizes[1]blocksizes[0])goto err_out; + if(ci->blocksizes[1]>8192)goto err_out; + + if(oggpack_read(opb,1)!=1)goto err_out; /* EOP check */ + + return(0); + err_out: + vorbis_info_clear(vi); + return(OV_EBADHEADER); +} + +static int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb){ + int i; + int vendorlen; + vendorlen=oggpack_read(opb,32); + if(vendorlen<0)goto err_out; + if(vendorlen>opb->storage-oggpack_bytes(opb))goto err_out; + vc->vendor=(char *)_ogg_calloc(vendorlen+1,1); + if(vc->vendor==NULL)goto err_out; + _v_readstring(opb,vc->vendor,vendorlen); + i=oggpack_read(opb,32); + if(i<0||i>=INT_MAX||i>(opb->storage-oggpack_bytes(opb))>>2)goto err_out; + vc->user_comments=(char **)_ogg_calloc(i+1,sizeof(*vc->user_comments)); + vc->comment_lengths=(int *)_ogg_calloc(i+1, sizeof(*vc->comment_lengths)); + if(vc->user_comments==NULL||vc->comment_lengths==NULL)goto err_out; + vc->comments=i; + + for(i=0;icomments;i++){ + int len=oggpack_read(opb,32); + if(len<0||len>opb->storage-oggpack_bytes(opb))goto err_out; + vc->comment_lengths[i]=len; + vc->user_comments[i]=(char *)_ogg_calloc(len+1,1); + if(vc->user_comments[i]==NULL){ + vc->comments=i; + goto err_out; + } + _v_readstring(opb,vc->user_comments[i],len); + } + if(oggpack_read(opb,1)!=1)goto err_out; /* EOP check */ + + return(0); + err_out: + vorbis_comment_clear(vc); + return(OV_EBADHEADER); +} + +/* all of the real encoding details are here. The modes, books, + everything */ +static int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb){ + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + int i; + if(!ci)return(OV_EFAULT); + + /* codebooks */ + ci->books=oggpack_read(opb,8)+1; + if(ci->books<=0)goto err_out; + for(i=0;ibooks;i++){ + ci->book_param[i]=vorbis_staticbook_unpack(opb); + if(!ci->book_param[i])goto err_out; + } + + /* time backend settings */ + ci->times=oggpack_read(opb,6)+1; + if(ci->times<=0)goto err_out; + for(i=0;itimes;i++){ + ci->time_type[i]=oggpack_read(opb,16); + if(ci->time_type[i]<0 || ci->time_type[i]>=VI_TIMEB)goto err_out; + /* ci->time_param[i]=_time_P[ci->time_type[i]]->unpack(vi,opb); + Vorbis I has no time backend */ + /*if(!ci->time_param[i])goto err_out;*/ + } + + /* floor backend settings */ + ci->floors=oggpack_read(opb,6)+1; + if(ci->floors<=0)goto err_out; + for(i=0;ifloors;i++){ + ci->floor_type[i]=oggpack_read(opb,16); + if(ci->floor_type[i]<0 || ci->floor_type[i]>=VI_FLOORB)goto err_out; + ci->floor_param[i]=_floor_P[ci->floor_type[i]]->unpack(vi,opb); + if(!ci->floor_param[i])goto err_out; + } + + /* residue backend settings */ + ci->residues=oggpack_read(opb,6)+1; + if(ci->residues<=0)goto err_out; + for(i=0;iresidues;i++){ + ci->residue_type[i]=oggpack_read(opb,16); + if(ci->residue_type[i]<0 || ci->residue_type[i]>=VI_RESB)goto err_out; + ci->residue_param[i]=_residue_P[ci->residue_type[i]]->unpack(vi,opb); + if(!ci->residue_param[i])goto err_out; + } + + /* map backend settings */ + ci->maps=oggpack_read(opb,6)+1; + if(ci->maps<=0)goto err_out; + for(i=0;imaps;i++){ + ci->map_type[i]=oggpack_read(opb,16); + if(ci->map_type[i]<0 || ci->map_type[i]>=VI_MAPB)goto err_out; + ci->map_param[i]=_mapping_P[ci->map_type[i]]->unpack(vi,opb); + if(!ci->map_param[i])goto err_out; + } + + /* mode settings */ + ci->modes=oggpack_read(opb,6)+1; + if(ci->modes<=0)goto err_out; + for(i=0;imodes;i++){ + ci->mode_param[i]=(vorbis_info_mode *)_ogg_calloc(1,sizeof(*ci->mode_param[i])); + ci->mode_param[i]->blockflag=oggpack_read(opb,1); + ci->mode_param[i]->windowtype=oggpack_read(opb,16); + ci->mode_param[i]->transformtype=oggpack_read(opb,16); + ci->mode_param[i]->mapping=oggpack_read(opb,8); + + if(ci->mode_param[i]->windowtype>=VI_WINDOWB)goto err_out; + if(ci->mode_param[i]->transformtype>=VI_WINDOWB)goto err_out; + if(ci->mode_param[i]->mapping>=ci->maps)goto err_out; + if(ci->mode_param[i]->mapping<0)goto err_out; + } + + if(oggpack_read(opb,1)!=1)goto err_out; /* top level EOP check */ + + return(0); + err_out: + vorbis_info_clear(vi); + return(OV_EBADHEADER); +} + +/* Is this packet a vorbis ID header? */ +int vorbis_synthesis_idheader(ogg_packet *op){ + oggpack_buffer opb; + char buffer[6]; + + if(op){ + oggpack_readinit(&opb,op->packet,op->bytes); + + if(!op->b_o_s) + return(0); /* Not the initial packet */ + + if(oggpack_read(&opb,8) != 1) + return 0; /* not an ID header */ + + memset(buffer,0,6); + _v_readstring(&opb,buffer,6); + if(memcmp(buffer,"vorbis",6)) + return 0; /* not vorbis */ + + return 1; + } + + return 0; +} + +/* The Vorbis header is in three packets; the initial small packet in + the first page that identifies basic parameters, a second packet + with bitstream comments and a third packet that holds the + codebook. */ + +int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,ogg_packet *op){ + oggpack_buffer opb; + + if(op){ + oggpack_readinit(&opb,op->packet,op->bytes); + + /* Which of the three types of header is this? */ + /* Also verify header-ness, vorbis */ + { + char buffer[6]; + int packtype=oggpack_read(&opb,8); + memset(buffer,0,6); + _v_readstring(&opb,buffer,6); + if(memcmp(buffer,"vorbis",6)){ + /* not a vorbis header */ + return(OV_ENOTVORBIS); + } + switch(packtype){ + case 0x01: /* least significant *bit* is read first */ + if(!op->b_o_s){ + /* Not the initial packet */ + return(OV_EBADHEADER); + } + if(vi->rate!=0){ + /* previously initialized info header */ + return(OV_EBADHEADER); + } + + return(_vorbis_unpack_info(vi,&opb)); + + case 0x03: /* least significant *bit* is read first */ + if(vi->rate==0){ + /* um... we didn't get the initial header */ + return(OV_EBADHEADER); + } + + return(_vorbis_unpack_comment(vc,&opb)); + + case 0x05: /* least significant *bit* is read first */ + if(vi->rate==0 || vc->vendor==NULL){ + /* um... we didn;t get the initial header or comments yet */ + return(OV_EBADHEADER); + } + + return(_vorbis_unpack_books(vi,&opb)); + + default: + /* Not a valid vorbis header type */ + return(OV_EBADHEADER); + break; + } + } + } + return(OV_EBADHEADER); +} + diff --git a/libs/tremor/iseeking_example.dontcompile b/libs/tremor/iseeking_example.dontcompile new file mode 100644 index 0000000..dc971ba --- /dev/null +++ b/libs/tremor/iseeking_example.dontcompile @@ -0,0 +1,265 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: illustrate seeking, and test it too + last mod: $Id$ + + ********************************************************************/ + +#include +#include +#include "ivorbiscodec.h" +#include "ivorbisfile.h" + +#ifdef _WIN32 /* We need the following two to set stdin/stdout to binary */ +# include +# include +#endif + +void _verify(OggVorbis_File *ov, + ogg_int64_t val, + ogg_int64_t pcmval, + ogg_int64_t timeval, + ogg_int64_t pcmlength, + char *bigassbuffer){ + int j; + long bread; + char buffer[4096]; + int dummy; + ogg_int64_t pos; + + /* verify the raw position, the pcm position and position decode */ + if(val!=-1 && ov_raw_tell(ov)pcmval){ + fprintf(stderr,"pcm position out of tolerance: requested %ld, got %ld\n", + (long)pcmval,(long)ov_pcm_tell(ov)); + exit(1); + } + if(timeval!=-1 && ov_time_tell(ov)>timeval){ + fprintf(stderr,"time position out of tolerance: requested %ld, got %ld\n", + (long)timeval,(long)ov_time_tell(ov)); + exit(1); + } + pos=ov_pcm_tell(ov); + if(pos<0 || pos>pcmlength){ + fprintf(stderr,"pcm position out of bounds: got %ld\n",(long)pos); + exit(1); + } + bread=ov_read(ov,buffer,4096,&dummy); + if(bigassbuffer){ + for(j=0;jchannels!=2){ + fprintf(stderr,"Sorry; right now seeking_test can only use Vorbis files\n" + "that are entirely stereo.\n\n"); + exit(1); + } + } + + /* because we want to do sample-level verification that the seek + does what it claimed, decode the entire file into memory */ + pcmlength=ov_pcm_total(&ov,-1); + timelength=ov_time_total(&ov,-1); + bigassbuffer=malloc(pcmlength*4); /* w00t */ + if(bigassbuffer){ + i=0; + while(ival+1){ + fprintf(stderr,"Declared position didn't perfectly match request: %ld != %ld\n", + (long)val,(long)ov_time_tell(&ov)); + exit(1); + } + + _verify(&ov,-1,-1,val,pcmlength,bigassbuffer); + + } + } + + fprintf(stderr,"\r \nOK.\n\n"); + + + }else{ + fprintf(stderr,"Standard input was not seekable.\n"); + } + + ov_clear(&ov); + return 0; +} + + + + + + + + + + + + + diff --git a/libs/tremor/ivorbiscodec.h b/libs/tremor/ivorbiscodec.h new file mode 100644 index 0000000..17eab58 --- /dev/null +++ b/libs/tremor/ivorbiscodec.h @@ -0,0 +1,204 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: libvorbis codec headers + + ********************************************************************/ + +#ifndef _vorbis_codec_h_ +#define _vorbis_codec_h_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include + +typedef struct vorbis_info{ + int version; + int channels; + long rate; + + /* The below bitrate declarations are *hints*. + Combinations of the three values carry the following implications: + + all three set to the same value: + implies a fixed rate bitstream + only nominal set: + implies a VBR stream that averages the nominal bitrate. No hard + upper/lower limit + upper and or lower set: + implies a VBR bitstream that obeys the bitrate limits. nominal + may also be set to give a nominal rate. + none set: + the coder does not care to speculate. + */ + + long bitrate_upper; + long bitrate_nominal; + long bitrate_lower; + long bitrate_window; + + void *codec_setup; +} vorbis_info; + +/* vorbis_dsp_state buffers the current vorbis audio + analysis/synthesis state. The DSP state belongs to a specific + logical bitstream ****************************************************/ +typedef struct vorbis_dsp_state{ + int analysisp; + vorbis_info *vi; + + ogg_int32_t **pcm; + ogg_int32_t **pcmret; + int pcm_storage; + int pcm_current; + int pcm_returned; + + int preextrapolate; + int eofflag; + + long lW; + long W; + long nW; + long centerW; + + ogg_int64_t granulepos; + ogg_int64_t sequence; + + void *backend_state; +} vorbis_dsp_state; + +typedef struct vorbis_block{ + /* necessary stream state for linking to the framing abstraction */ + ogg_int32_t **pcm; /* this is a pointer into local storage */ + oggpack_buffer opb; + + long lW; + long W; + long nW; + int pcmend; + int mode; + + int eofflag; + ogg_int64_t granulepos; + ogg_int64_t sequence; + vorbis_dsp_state *vd; /* For read-only access of configuration */ + + /* local storage to avoid remallocing; it's up to the mapping to + structure it */ + void *localstore; + long localtop; + long localalloc; + long totaluse; + struct alloc_chain *reap; + +} vorbis_block; + +/* vorbis_block is a single block of data to be processed as part of +the analysis/synthesis stream; it belongs to a specific logical +bitstream, but is independant from other vorbis_blocks belonging to +that logical bitstream. *************************************************/ + +struct alloc_chain{ + void *ptr; + struct alloc_chain *next; +}; + +/* vorbis_info contains all the setup information specific to the + specific compression/decompression mode in progress (eg, + psychoacoustic settings, channel setup, options, codebook + etc). vorbis_info and substructures are in backends.h. +*********************************************************************/ + +/* the comments are not part of vorbis_info so that vorbis_info can be + static storage */ +typedef struct vorbis_comment{ + /* unlimited user comment fields. libvorbis writes 'libvorbis' + whatever vendor is set to in encode */ + char **user_comments; + int *comment_lengths; + int comments; + char *vendor; + +} vorbis_comment; + + +/* libvorbis encodes in two abstraction layers; first we perform DSP + and produce a packet (see docs/analysis.txt). The packet is then + coded into a framed OggSquish bitstream by the second layer (see + docs/framing.txt). Decode is the reverse process; we sync/frame + the bitstream and extract individual packets, then decode the + packet back into PCM audio. + + The extra framing/packetizing is used in streaming formats, such as + files. Over the net (such as with UDP), the framing and + packetization aren't necessary as they're provided by the transport + and the streaming layer is not used */ + +/* Vorbis PRIMITIVES: general ***************************************/ + +extern void vorbis_info_init(vorbis_info *vi); +extern void vorbis_info_clear(vorbis_info *vi); +extern int vorbis_info_blocksize(vorbis_info *vi,int zo); +extern void vorbis_comment_init(vorbis_comment *vc); +extern void vorbis_comment_add(vorbis_comment *vc, char *comment); +extern void vorbis_comment_add_tag(vorbis_comment *vc, + char *tag, char *contents); +extern char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count); +extern int vorbis_comment_query_count(vorbis_comment *vc, char *tag); +extern void vorbis_comment_clear(vorbis_comment *vc); + +extern int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb); +extern int vorbis_block_clear(vorbis_block *vb); +extern void vorbis_dsp_clear(vorbis_dsp_state *v); + +/* Vorbis PRIMITIVES: synthesis layer *******************************/ +extern int vorbis_synthesis_idheader(ogg_packet *op); +extern int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc, + ogg_packet *op); + +extern int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi); +extern int vorbis_synthesis_restart(vorbis_dsp_state *v); +extern int vorbis_synthesis(vorbis_block *vb,ogg_packet *op); +extern int vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op); +extern int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb); +extern int vorbis_synthesis_pcmout(vorbis_dsp_state *v,ogg_int32_t ***pcm); +extern int vorbis_synthesis_read(vorbis_dsp_state *v,int samples); +extern long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op); + +/* Vorbis ERRORS and return codes ***********************************/ + +#define OV_FALSE -1 +#define OV_EOF -2 +#define OV_HOLE -3 + +#define OV_EREAD -128 +#define OV_EFAULT -129 +#define OV_EIMPL -130 +#define OV_EINVAL -131 +#define OV_ENOTVORBIS -132 +#define OV_EBADHEADER -133 +#define OV_EVERSION -134 +#define OV_ENOTAUDIO -135 +#define OV_EBADPACKET -136 +#define OV_EBADLINK -137 +#define OV_ENOSEEK -138 + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + diff --git a/libs/tremor/ivorbisfile.h b/libs/tremor/ivorbisfile.h new file mode 100644 index 0000000..f6ecb0e --- /dev/null +++ b/libs/tremor/ivorbisfile.h @@ -0,0 +1,131 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: stdio-based convenience library for opening/seeking/decoding + + ********************************************************************/ + +#ifndef _OV_FILE_H_ +#define _OV_FILE_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include +#include "ivorbiscodec.h" + +#define CHUNKSIZE 65535 +#define READSIZE 1024 +/* The function prototypes for the callbacks are basically the same as for + * the stdio functions fread, fseek, fclose, ftell. + * The one difference is that the FILE * arguments have been replaced with + * a void * - this is to be used as a pointer to whatever internal data these + * functions might need. In the stdio case, it's just a FILE * cast to a void * + * + * If you use other functions, check the docs for these functions and return + * the right values. For seek_func(), you *MUST* return -1 if the stream is + * unseekable + */ +typedef struct { + size_t (*read_func) (void *ptr, size_t size, size_t nmemb, void *datasource); + int (*seek_func) (void *datasource, ogg_int64_t offset, int whence); + int (*close_func) (void *datasource); + long (*tell_func) (void *datasource); +} ov_callbacks; + +#define NOTOPEN 0 +#define PARTOPEN 1 +#define OPENED 2 +#define STREAMSET 3 +#define INITSET 4 + +typedef struct OggVorbis_File { + void *datasource; /* Pointer to a FILE *, etc. */ + int seekable; + ogg_int64_t offset; + ogg_int64_t end; + ogg_sync_state oy; + + /* If the FILE handle isn't seekable (eg, a pipe), only the current + stream appears */ + int links; + ogg_int64_t *offsets; + ogg_int64_t *dataoffsets; + ogg_uint32_t *serialnos; + ogg_int64_t *pcmlengths; + vorbis_info *vi; + vorbis_comment *vc; + + /* Decoding working state local storage */ + ogg_int64_t pcm_offset; + int ready_state; + ogg_uint32_t current_serialno; + int current_link; + + ogg_int64_t bittrack; + ogg_int64_t samptrack; + + ogg_stream_state os; /* take physical pages, weld into a logical + stream of packets */ + vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ + vorbis_block vb; /* local working space for packet->PCM decode */ + + ov_callbacks callbacks; + +} OggVorbis_File; + +extern int ov_clear(OggVorbis_File *vf); +extern int ov_open(FILE *f,OggVorbis_File *vf,const char *initial,long ibytes); +extern int ov_open_callbacks(void *datasource, OggVorbis_File *vf, + const char *initial, long ibytes, ov_callbacks callbacks); + +extern int ov_test(FILE *f,OggVorbis_File *vf,const char *initial,long ibytes); +extern int ov_test_callbacks(void *datasource, OggVorbis_File *vf, + const char *initial, long ibytes, ov_callbacks callbacks); +extern int ov_test_open(OggVorbis_File *vf); + +extern long ov_bitrate(OggVorbis_File *vf,int i); +extern long ov_bitrate_instant(OggVorbis_File *vf); +extern long ov_streams(OggVorbis_File *vf); +extern long ov_seekable(OggVorbis_File *vf); +extern long ov_serialnumber(OggVorbis_File *vf,int i); + +extern ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i); +extern ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i); +extern ogg_int64_t ov_time_total(OggVorbis_File *vf,int i); + +extern int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_time_seek(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_time_seek_page(OggVorbis_File *vf,ogg_int64_t pos); + +extern ogg_int64_t ov_raw_tell(OggVorbis_File *vf); +extern ogg_int64_t ov_pcm_tell(OggVorbis_File *vf); +extern ogg_int64_t ov_time_tell(OggVorbis_File *vf); + +extern vorbis_info *ov_info(OggVorbis_File *vf,int link); +extern vorbis_comment *ov_comment(OggVorbis_File *vf,int link); + +extern long ov_read(OggVorbis_File *vf,char *buffer,int length, + int *bitstream); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + + diff --git a/libs/tremor/ivorbisfile_example.dontcompile b/libs/tremor/ivorbisfile_example.dontcompile new file mode 100644 index 0000000..7b0cf10 --- /dev/null +++ b/libs/tremor/ivorbisfile_example.dontcompile @@ -0,0 +1,91 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: simple example decoder using vorbisidec + + ********************************************************************/ + +/* Takes a vorbis bitstream from stdin and writes raw stereo PCM to + stdout using vorbisfile. Using vorbisfile is much simpler than + dealing with libvorbis. */ + +#include +#include +#include "ivorbiscodec.h" +#include "ivorbisfile.h" + +#ifdef _WIN32 /* We need the following two to set stdin/stdout to binary */ +#include +#include +#endif + +char pcmout[4096]; /* take 4k out of the data segment, not the stack */ + +int main(){ + OggVorbis_File vf; + int eof=0; + int current_section; + +#ifdef _WIN32 /* We need to set stdin/stdout to binary mode. Damn windows. */ + /* Beware the evil ifdef. We avoid these where we can, but this one we + cannot. Don't add any more, you'll probably go to hell if you do. */ + _setmode( _fileno( stdin ), _O_BINARY ); + _setmode( _fileno( stdout ), _O_BINARY ); +#endif + + if(ov_open(stdin, &vf, NULL, 0) < 0) { + fprintf(stderr,"Input does not appear to be an Ogg bitstream.\n"); + exit(1); + } + + /* Throw the comments plus a few lines about the bitstream we're + decoding */ + { + char **ptr=ov_comment(&vf,-1)->user_comments; + vorbis_info *vi=ov_info(&vf,-1); + while(*ptr){ + fprintf(stderr,"%s\n",*ptr); + ++ptr; + } + fprintf(stderr,"\nBitstream is %d channel, %ldHz\n",vi->channels,vi->rate); + fprintf(stderr,"\nDecoded length: %ld samples\n", + (long)ov_pcm_total(&vf,-1)); + fprintf(stderr,"Encoded by: %s\n\n",ov_comment(&vf,-1)->vendor); + } + + while(!eof){ + long ret=ov_read(&vf,pcmout,sizeof(pcmout),¤t_section); + if (ret == 0) { + /* EOF */ + eof=1; + } else if (ret < 0) { + if(ret==OV_EBADLINK){ + fprintf(stderr,"Corrupt bitstream section! Exiting.\n"); + exit(1); + } + + /* some other error in the stream. Not a problem, just reporting it in + case we (the app) cares. In this case, we don't. */ + } else { + /* we don't bother dealing with sample rate changes, etc, but + you'll have to*/ + fwrite(pcmout,1,ret,stdout); + } + } + + /* cleanup */ + ov_clear(&vf); + + fprintf(stderr,"Done.\n"); + return(0); +} diff --git a/libs/tremor/lsp_lookup.h b/libs/tremor/lsp_lookup.h new file mode 100644 index 0000000..7162392 --- /dev/null +++ b/libs/tremor/lsp_lookup.h @@ -0,0 +1,136 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: lookup data + + ********************************************************************/ + +#ifndef _V_LOOKUP_DATA_H_ +#define _V_LOOKUP_DATA_H_ + +#include + +#define FROMdB_LOOKUP_SZ 35 +#define FROMdB2_LOOKUP_SZ 32 +#define FROMdB_SHIFT 5 +#define FROMdB2_SHIFT 3 +#define FROMdB2_MASK 31 + +static const ogg_int32_t FROMdB_LOOKUP[FROMdB_LOOKUP_SZ]={ + 0x003fffff, 0x0028619b, 0x00197a96, 0x0010137a, + 0x000a24b0, 0x00066666, 0x000409c3, 0x00028c42, + 0x00019b8c, 0x000103ab, 0x0000a3d7, 0x00006760, + 0x0000413a, 0x00002928, 0x000019f8, 0x00001062, + 0x00000a56, 0x00000686, 0x0000041e, 0x00000299, + 0x000001a3, 0x00000109, 0x000000a7, 0x00000069, + 0x00000042, 0x0000002a, 0x0000001a, 0x00000011, + 0x0000000b, 0x00000007, 0x00000004, 0x00000003, + 0x00000002, 0x00000001, 0x00000001}; + +static const ogg_int32_t FROMdB2_LOOKUP[FROMdB2_LOOKUP_SZ]={ + 0x000001fc, 0x000001f5, 0x000001ee, 0x000001e7, + 0x000001e0, 0x000001d9, 0x000001d2, 0x000001cc, + 0x000001c5, 0x000001bf, 0x000001b8, 0x000001b2, + 0x000001ac, 0x000001a6, 0x000001a0, 0x0000019a, + 0x00000194, 0x0000018e, 0x00000188, 0x00000183, + 0x0000017d, 0x00000178, 0x00000172, 0x0000016d, + 0x00000168, 0x00000163, 0x0000015e, 0x00000159, + 0x00000154, 0x0000014f, 0x0000014a, 0x00000145, +}; + +#define INVSQ_LOOKUP_I_SHIFT 10 +#define INVSQ_LOOKUP_I_MASK 1023 +static const long INVSQ_LOOKUP_I[64+1]={ + 92682, 91966, 91267, 90583, + 89915, 89261, 88621, 87995, + 87381, 86781, 86192, 85616, + 85051, 84497, 83953, 83420, + 82897, 82384, 81880, 81385, + 80899, 80422, 79953, 79492, + 79039, 78594, 78156, 77726, + 77302, 76885, 76475, 76072, + 75674, 75283, 74898, 74519, + 74146, 73778, 73415, 73058, + 72706, 72359, 72016, 71679, + 71347, 71019, 70695, 70376, + 70061, 69750, 69444, 69141, + 68842, 68548, 68256, 67969, + 67685, 67405, 67128, 66855, + 66585, 66318, 66054, 65794, + 65536, +}; + +static const long INVSQ_LOOKUP_IDel[64]={ + 716, 699, 684, 668, + 654, 640, 626, 614, + 600, 589, 576, 565, + 554, 544, 533, 523, + 513, 504, 495, 486, + 477, 469, 461, 453, + 445, 438, 430, 424, + 417, 410, 403, 398, + 391, 385, 379, 373, + 368, 363, 357, 352, + 347, 343, 337, 332, + 328, 324, 319, 315, + 311, 306, 303, 299, + 294, 292, 287, 284, + 280, 277, 273, 270, + 267, 264, 260, 258, +}; + +#define COS_LOOKUP_I_SHIFT 9 +#define COS_LOOKUP_I_MASK 511 +#define COS_LOOKUP_I_SZ 128 +static const ogg_int32_t COS_LOOKUP_I[COS_LOOKUP_I_SZ+1]={ + 16384, 16379, 16364, 16340, + 16305, 16261, 16207, 16143, + 16069, 15986, 15893, 15791, + 15679, 15557, 15426, 15286, + 15137, 14978, 14811, 14635, + 14449, 14256, 14053, 13842, + 13623, 13395, 13160, 12916, + 12665, 12406, 12140, 11866, + 11585, 11297, 11003, 10702, + 10394, 10080, 9760, 9434, + 9102, 8765, 8423, 8076, + 7723, 7366, 7005, 6639, + 6270, 5897, 5520, 5139, + 4756, 4370, 3981, 3590, + 3196, 2801, 2404, 2006, + 1606, 1205, 804, 402, + 0, -401, -803, -1204, + -1605, -2005, -2403, -2800, + -3195, -3589, -3980, -4369, + -4755, -5138, -5519, -5896, + -6269, -6638, -7004, -7365, + -7722, -8075, -8422, -8764, + -9101, -9433, -9759, -10079, + -10393, -10701, -11002, -11296, + -11584, -11865, -12139, -12405, + -12664, -12915, -13159, -13394, + -13622, -13841, -14052, -14255, + -14448, -14634, -14810, -14977, + -15136, -15285, -15425, -15556, + -15678, -15790, -15892, -15985, + -16068, -16142, -16206, -16260, + -16304, -16339, -16363, -16378, + -16383, +}; + +#endif + + + + + diff --git a/libs/tremor/mapping0.c b/libs/tremor/mapping0.c new file mode 100644 index 0000000..aa03e85 --- /dev/null +++ b/libs/tremor/mapping0.c @@ -0,0 +1,328 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: channel mapping 0 implementation + + ********************************************************************/ + +#include +#include +#include +#include +#include +#include "ivorbiscodec.h" +#include "mdct.h" +#include "codec_internal.h" +#include "codebook.h" +#include "window.h" +#include "registry.h" +#include "misc.h" + +/* simplistic, wasteful way of doing this (unique lookup for each + mode/submapping); there should be a central repository for + identical lookups. That will require minor work, so I'm putting it + off as low priority. + + Why a lookup for each backend in a given mode? Because the + blocksize is set by the mode, and low backend lookups may require + parameters from other areas of the mode/mapping */ + +typedef struct { + vorbis_info_mode *mode; + vorbis_info_mapping0 *map; + + vorbis_look_floor **floor_look; + + vorbis_look_residue **residue_look; + + vorbis_func_floor **floor_func; + vorbis_func_residue **residue_func; + + int ch; + long lastframe; /* if a different mode is called, we need to + invalidate decay */ +} vorbis_look_mapping0; + +static void mapping0_free_info(vorbis_info_mapping *i){ + vorbis_info_mapping0 *info=(vorbis_info_mapping0 *)i; + if(info){ + memset(info,0,sizeof(*info)); + _ogg_free(info); + } +} + +static void mapping0_free_look(vorbis_look_mapping *look){ + int i; + vorbis_look_mapping0 *l=(vorbis_look_mapping0 *)look; + if(l){ + + for(i=0;imap->submaps;i++){ + l->floor_func[i]->free_look(l->floor_look[i]); + l->residue_func[i]->free_look(l->residue_look[i]); + } + + _ogg_free(l->floor_func); + _ogg_free(l->residue_func); + _ogg_free(l->floor_look); + _ogg_free(l->residue_look); + memset(l,0,sizeof(*l)); + _ogg_free(l); + } +} + +static vorbis_look_mapping *mapping0_look(vorbis_dsp_state *vd,vorbis_info_mode *vm, + vorbis_info_mapping *m){ + int i; + vorbis_info *vi=vd->vi; + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + vorbis_look_mapping0 *look=(vorbis_look_mapping0 *)_ogg_calloc(1,sizeof(*look)); + vorbis_info_mapping0 *info=look->map=(vorbis_info_mapping0 *)m; + look->mode=vm; + + look->floor_look=(vorbis_look_floor **)_ogg_calloc(info->submaps,sizeof(*look->floor_look)); + + look->residue_look=(vorbis_look_residue **)_ogg_calloc(info->submaps,sizeof(*look->residue_look)); + + look->floor_func=(vorbis_func_floor **)_ogg_calloc(info->submaps,sizeof(*look->floor_func)); + look->residue_func=(vorbis_func_residue **)_ogg_calloc(info->submaps,sizeof(*look->residue_func)); + + for(i=0;isubmaps;i++){ + int floornum=info->floorsubmap[i]; + int resnum=info->residuesubmap[i]; + + look->floor_func[i]=_floor_P[ci->floor_type[floornum]]; + look->floor_look[i]=look->floor_func[i]-> + look(vd,vm,ci->floor_param[floornum]); + look->residue_func[i]=_residue_P[ci->residue_type[resnum]]; + look->residue_look[i]=look->residue_func[i]-> + look(vd,vm,ci->residue_param[resnum]); + + } + + look->ch=vi->channels; + + return(look); +} + +static int ilog(unsigned int v){ + int ret=0; + if(v)--v; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +/* also responsible for range checking */ +static vorbis_info_mapping *mapping0_unpack(vorbis_info *vi,oggpack_buffer *opb){ + int i,b; + vorbis_info_mapping0 *info=(vorbis_info_mapping0 *)_ogg_calloc(1,sizeof(*info)); + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + memset(info,0,sizeof(*info)); + + b=oggpack_read(opb,1); + if(b<0)goto err_out; + if(b){ + info->submaps=oggpack_read(opb,4)+1; + if(info->submaps<=0)goto err_out; + }else + info->submaps=1; + + b=oggpack_read(opb,1); + if(b<0)goto err_out; + if(b){ + info->coupling_steps=oggpack_read(opb,8)+1; + if(info->coupling_steps<=0)goto err_out; + for(i=0;icoupling_steps;i++){ + int testM=info->coupling_mag[i]=oggpack_read(opb,ilog(vi->channels)); + int testA=info->coupling_ang[i]=oggpack_read(opb,ilog(vi->channels)); + + if(testM<0 || + testA<0 || + testM==testA || + testM>=vi->channels || + testA>=vi->channels) goto err_out; + } + + } + + if(oggpack_read(opb,2)!=0)goto err_out; /* 2,3:reserved */ + + if(info->submaps>1){ + for(i=0;ichannels;i++){ + info->chmuxlist[i]=oggpack_read(opb,4); + if(info->chmuxlist[i]>=info->submaps || info->chmuxlist[i]<0)goto err_out; + } + } + for(i=0;isubmaps;i++){ + int temp=oggpack_read(opb,8); + if(temp>=ci->times)goto err_out; + info->floorsubmap[i]=oggpack_read(opb,8); + if(info->floorsubmap[i]>=ci->floors || info->floorsubmap[i]<0)goto err_out; + info->residuesubmap[i]=oggpack_read(opb,8); + if(info->residuesubmap[i]>=ci->residues || info->residuesubmap[i]<0) + goto err_out; + } + + return info; + + err_out: + mapping0_free_info(info); + return(NULL); +} + +static int seq=0; +static int mapping0_inverse(vorbis_block *vb,vorbis_look_mapping *l){ + vorbis_dsp_state *vd=vb->vd; + vorbis_info *vi=vd->vi; + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + private_state *b=(private_state *)vd->backend_state; + vorbis_look_mapping0 *look=(vorbis_look_mapping0 *)l; + vorbis_info_mapping0 *info=look->map; + + int i,j; + long n=vb->pcmend=ci->blocksizes[vb->W]; + + ogg_int32_t **pcmbundle=(ogg_int32_t **)alloca(sizeof(*pcmbundle)*vi->channels); + int *zerobundle=(int *)alloca(sizeof(*zerobundle)*vi->channels); + + int *nonzero =(int *)alloca(sizeof(*nonzero)*vi->channels); + void **floormemo=(void **)alloca(sizeof(*floormemo)*vi->channels); + + /* time domain information decode (note that applying the + information would have to happen later; we'll probably add a + function entry to the harness for that later */ + /* NOT IMPLEMENTED */ + + /* recover the spectral envelope; store it in the PCM vector for now */ + for(i=0;ichannels;i++){ + int submap=info->chmuxlist[i]; + floormemo[i]=look->floor_func[submap]-> + inverse1(vb,look->floor_look[submap]); + if(floormemo[i]) + nonzero[i]=1; + else + nonzero[i]=0; + memset(vb->pcm[i],0,sizeof(*vb->pcm[i])*n/2); + } + + /* channel coupling can 'dirty' the nonzero listing */ + for(i=0;icoupling_steps;i++){ + if(nonzero[info->coupling_mag[i]] || + nonzero[info->coupling_ang[i]]){ + nonzero[info->coupling_mag[i]]=1; + nonzero[info->coupling_ang[i]]=1; + } + } + + /* recover the residue into our working vectors */ + for(i=0;isubmaps;i++){ + int ch_in_bundle=0; + for(j=0;jchannels;j++){ + if(info->chmuxlist[j]==i){ + if(nonzero[j]) + zerobundle[ch_in_bundle]=1; + else + zerobundle[ch_in_bundle]=0; + pcmbundle[ch_in_bundle++]=vb->pcm[j]; + } + } + + look->residue_func[i]->inverse(vb,look->residue_look[i], + pcmbundle,zerobundle,ch_in_bundle); + } + + //for(j=0;jchannels;j++) + //_analysis_output("coupled",seq+j,vb->pcm[j],-8,n/2,0,0); + + + /* channel coupling */ + for(i=info->coupling_steps-1;i>=0;i--){ + ogg_int32_t *pcmM=vb->pcm[info->coupling_mag[i]]; + ogg_int32_t *pcmA=vb->pcm[info->coupling_ang[i]]; + + for(j=0;j0) + if(ang>0){ + pcmM[j]=mag; + pcmA[j]=mag-ang; + }else{ + pcmA[j]=mag; + pcmM[j]=mag+ang; + } + else + if(ang>0){ + pcmM[j]=mag; + pcmA[j]=mag+ang; + }else{ + pcmA[j]=mag; + pcmM[j]=mag-ang; + } + } + } + + //for(j=0;jchannels;j++) + //_analysis_output("residue",seq+j,vb->pcm[j],-8,n/2,0,0); + + /* compute and apply spectral envelope */ + for(i=0;ichannels;i++){ + ogg_int32_t *pcm=vb->pcm[i]; + int submap=info->chmuxlist[i]; + look->floor_func[submap]-> + inverse2(vb,look->floor_look[submap],floormemo[i],pcm); + } + + //for(j=0;jchannels;j++) + //_analysis_output("mdct",seq+j,vb->pcm[j],-24,n/2,0,1); + + /* transform the PCM data; takes PCM vector, vb; modifies PCM vector */ + /* only MDCT right now.... */ + for(i=0;ichannels;i++){ + ogg_int32_t *pcm=vb->pcm[i]; + mdct_backward(n,pcm,pcm); + } + + //for(j=0;jchannels;j++) + //_analysis_output("imdct",seq+j,vb->pcm[j],-24,n,0,0); + + /* window the data */ + for(i=0;ichannels;i++){ + ogg_int32_t *pcm=vb->pcm[i]; + if(nonzero[i]) + _vorbis_apply_window(pcm,b->window,ci->blocksizes,vb->lW,vb->W,vb->nW); + else + for(j=0;jchannels;j++) + //_analysis_output("window",seq+j,vb->pcm[j],-24,n,0,0); + + seq+=vi->channels; + /* all done! */ + return(0); +} + +/* export hooks */ +vorbis_func_mapping mapping0_exportbundle={ + &mapping0_unpack, + &mapping0_look, + &mapping0_free_info, + &mapping0_free_look, + &mapping0_inverse +}; diff --git a/libs/tremor/mdct.c b/libs/tremor/mdct.c new file mode 100644 index 0000000..2aed62c --- /dev/null +++ b/libs/tremor/mdct.c @@ -0,0 +1,510 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: normalized modified discrete cosine transform + power of two length transform only [64 <= n ] + last mod: $Id$ + + Original algorithm adapted long ago from _The use of multirate filter + banks for coding of high quality digital audio_, by T. Sporer, + K. Brandenburg and B. Edler, collection of the European Signal + Processing Conference (EUSIPCO), Amsterdam, June 1992, Vol.1, pp + 211-214 + + The below code implements an algorithm that no longer looks much like + that presented in the paper, but the basic structure remains if you + dig deep enough to see it. + + This module DOES NOT INCLUDE code to generate/apply the window + function. Everybody has their own weird favorite including me... I + happen to like the properties of y=sin(.5PI*sin^2(x)), but others may + vehemently disagree. + + ********************************************************************/ + +#include "ivorbiscodec.h" +#include "codebook.h" +#include "misc.h" +#include "mdct.h" +#include "mdct_lookup.h" + + +/* 8 point butterfly (in place) */ +STIN void mdct_butterfly_8(DATA_TYPE *x){ + + REG_TYPE r0 = x[4] + x[0]; + REG_TYPE r1 = x[4] - x[0]; + REG_TYPE r2 = x[5] + x[1]; + REG_TYPE r3 = x[5] - x[1]; + REG_TYPE r4 = x[6] + x[2]; + REG_TYPE r5 = x[6] - x[2]; + REG_TYPE r6 = x[7] + x[3]; + REG_TYPE r7 = x[7] - x[3]; + + x[0] = r5 + r3; + x[1] = r7 - r1; + x[2] = r5 - r3; + x[3] = r7 + r1; + x[4] = r4 - r0; + x[5] = r6 - r2; + x[6] = r4 + r0; + x[7] = r6 + r2; + MB(); +} + +/* 16 point butterfly (in place, 4 register) */ +STIN void mdct_butterfly_16(DATA_TYPE *x){ + + REG_TYPE r0, r1; + + r0 = x[ 0] - x[ 8]; x[ 8] += x[ 0]; + r1 = x[ 1] - x[ 9]; x[ 9] += x[ 1]; + x[ 0] = MULT31((r0 + r1) , cPI2_8); + x[ 1] = MULT31((r1 - r0) , cPI2_8); + MB(); + + r0 = x[10] - x[ 2]; x[10] += x[ 2]; + r1 = x[ 3] - x[11]; x[11] += x[ 3]; + x[ 2] = r1; x[ 3] = r0; + MB(); + + r0 = x[12] - x[ 4]; x[12] += x[ 4]; + r1 = x[13] - x[ 5]; x[13] += x[ 5]; + x[ 4] = MULT31((r0 - r1) , cPI2_8); + x[ 5] = MULT31((r0 + r1) , cPI2_8); + MB(); + + r0 = x[14] - x[ 6]; x[14] += x[ 6]; + r1 = x[15] - x[ 7]; x[15] += x[ 7]; + x[ 6] = r0; x[ 7] = r1; + MB(); + + mdct_butterfly_8(x); + mdct_butterfly_8(x+8); +} + +/* 32 point butterfly (in place, 4 register) */ +STIN void mdct_butterfly_32(DATA_TYPE *x){ + + REG_TYPE r0, r1; + + r0 = x[30] - x[14]; x[30] += x[14]; + r1 = x[31] - x[15]; x[31] += x[15]; + x[14] = r0; x[15] = r1; + MB(); + + r0 = x[28] - x[12]; x[28] += x[12]; + r1 = x[29] - x[13]; x[29] += x[13]; + XNPROD31( r0, r1, cPI1_8, cPI3_8, &x[12], &x[13] ); + MB(); + + r0 = x[26] - x[10]; x[26] += x[10]; + r1 = x[27] - x[11]; x[27] += x[11]; + x[10] = MULT31((r0 - r1) , cPI2_8); + x[11] = MULT31((r0 + r1) , cPI2_8); + MB(); + + r0 = x[24] - x[ 8]; x[24] += x[ 8]; + r1 = x[25] - x[ 9]; x[25] += x[ 9]; + XNPROD31( r0, r1, cPI3_8, cPI1_8, &x[ 8], &x[ 9] ); + MB(); + + r0 = x[22] - x[ 6]; x[22] += x[ 6]; + r1 = x[ 7] - x[23]; x[23] += x[ 7]; + x[ 6] = r1; x[ 7] = r0; + MB(); + + r0 = x[ 4] - x[20]; x[20] += x[ 4]; + r1 = x[ 5] - x[21]; x[21] += x[ 5]; + XPROD31 ( r0, r1, cPI3_8, cPI1_8, &x[ 4], &x[ 5] ); + MB(); + + r0 = x[ 2] - x[18]; x[18] += x[ 2]; + r1 = x[ 3] - x[19]; x[19] += x[ 3]; + x[ 2] = MULT31((r1 + r0) , cPI2_8); + x[ 3] = MULT31((r1 - r0) , cPI2_8); + MB(); + + r0 = x[ 0] - x[16]; x[16] += x[ 0]; + r1 = x[ 1] - x[17]; x[17] += x[ 1]; + XPROD31 ( r0, r1, cPI1_8, cPI3_8, &x[ 0], &x[ 1] ); + MB(); + + mdct_butterfly_16(x); + mdct_butterfly_16(x+16); +} + +/* N/stage point generic N stage butterfly (in place, 2 register) */ +STIN void mdct_butterfly_generic(DATA_TYPE *x,int points,int step){ + + LOOKUP_T *T = sincos_lookup0; + DATA_TYPE *x1 = x + points - 8; + DATA_TYPE *x2 = x + (points>>1) - 8; + REG_TYPE r0; + REG_TYPE r1; + + do{ + r0 = x1[6] - x2[6]; x1[6] += x2[6]; + r1 = x2[7] - x1[7]; x1[7] += x2[7]; + XPROD31( r1, r0, T[0], T[1], &x2[6], &x2[7] ); T+=step; + + r0 = x1[4] - x2[4]; x1[4] += x2[4]; + r1 = x2[5] - x1[5]; x1[5] += x2[5]; + XPROD31( r1, r0, T[0], T[1], &x2[4], &x2[5] ); T+=step; + + r0 = x1[2] - x2[2]; x1[2] += x2[2]; + r1 = x2[3] - x1[3]; x1[3] += x2[3]; + XPROD31( r1, r0, T[0], T[1], &x2[2], &x2[3] ); T+=step; + + r0 = x1[0] - x2[0]; x1[0] += x2[0]; + r1 = x2[1] - x1[1]; x1[1] += x2[1]; + XPROD31( r1, r0, T[0], T[1], &x2[0], &x2[1] ); T+=step; + + x1-=8; x2-=8; + }while(Tsincos_lookup0); + do{ + r0 = x2[6] - x1[6]; x1[6] += x2[6]; + r1 = x2[7] - x1[7]; x1[7] += x2[7]; + XPROD31( r0, r1, T[0], T[1], &x2[6], &x2[7] ); T+=step; + + r0 = x2[4] - x1[4]; x1[4] += x2[4]; + r1 = x2[5] - x1[5]; x1[5] += x2[5]; + XPROD31( r0, r1, T[0], T[1], &x2[4], &x2[5] ); T+=step; + + r0 = x2[2] - x1[2]; x1[2] += x2[2]; + r1 = x2[3] - x1[3]; x1[3] += x2[3]; + XPROD31( r0, r1, T[0], T[1], &x2[2], &x2[3] ); T+=step; + + r0 = x2[0] - x1[0]; x1[0] += x2[0]; + r1 = x2[1] - x1[1]; x1[1] += x2[1]; + XPROD31( r0, r1, T[0], T[1], &x2[0], &x2[1] ); T+=step; + + x1-=8; x2-=8; + }while(Tsincos_lookup0); +} + +STIN void mdct_butterflies(DATA_TYPE *x,int points,int shift){ + + int stages=8-shift; + int i,j; + + for(i=0;--stages>0;i++){ + for(j=0;j<(1<>i)*j,points>>i,4<<(i+shift)); + } + + for(j=0;j>8]|(bitrev[(x&0x0f0)>>4]<<4)|(((int)bitrev[x&0x00f])<<8); +} + +STIN void mdct_bitreverse(DATA_TYPE *x,int n,int step,int shift){ + + int bit = 0; + DATA_TYPE *w0 = x; + DATA_TYPE *w1 = x = w0+(n>>1); + LOOKUP_T *T = (step>=4)?(sincos_lookup0+(step>>1)):sincos_lookup1; + LOOKUP_T *Ttop = T+1024; + DATA_TYPE r2; + + do{ + DATA_TYPE r3 = bitrev12(bit++); + DATA_TYPE *x0 = x + ((r3 ^ 0xfff)>>shift) -1; + DATA_TYPE *x1 = x + (r3>>shift); + + REG_TYPE r0 = x0[0] + x1[0]; + REG_TYPE r1 = x1[1] - x0[1]; + + XPROD32( r0, r1, T[1], T[0], &r2, &r3 ); T+=step; + + w1 -= 4; + + r0 = (x0[1] + x1[1])>>1; + r1 = (x0[0] - x1[0])>>1; + w0[0] = r0 + r2; + w0[1] = r1 + r3; + w1[2] = r0 - r2; + w1[3] = r3 - r1; + + r3 = bitrev12(bit++); + x0 = x + ((r3 ^ 0xfff)>>shift) -1; + x1 = x + (r3>>shift); + + r0 = x0[0] + x1[0]; + r1 = x1[1] - x0[1]; + + XPROD32( r0, r1, T[1], T[0], &r2, &r3 ); T+=step; + + r0 = (x0[1] + x1[1])>>1; + r1 = (x0[0] - x1[0])>>1; + w0[2] = r0 + r2; + w0[3] = r1 + r3; + w1[0] = r0 - r2; + w1[1] = r3 - r1; + + w0 += 4; + }while(T>shift) -1; + DATA_TYPE *x1 = x + (r3>>shift); + + REG_TYPE r0 = x0[0] + x1[0]; + REG_TYPE r1 = x1[1] - x0[1]; + + T-=step; XPROD32( r0, r1, T[0], T[1], &r2, &r3 ); + + w1 -= 4; + + r0 = (x0[1] + x1[1])>>1; + r1 = (x0[0] - x1[0])>>1; + w0[0] = r0 + r2; + w0[1] = r1 + r3; + w1[2] = r0 - r2; + w1[3] = r3 - r1; + + r3 = bitrev12(bit++); + x0 = x + ((r3 ^ 0xfff)>>shift) -1; + x1 = x + (r3>>shift); + + r0 = x0[0] + x1[0]; + r1 = x1[1] - x0[1]; + + T-=step; XPROD32( r0, r1, T[0], T[1], &r2, &r3 ); + + r0 = (x0[1] + x1[1])>>1; + r1 = (x0[0] - x1[0])>>1; + w0[2] = r0 + r2; + w0[3] = r1 + r3; + w1[0] = r0 - r2; + w1[1] = r3 - r1; + + w0 += 4; + }while(w0>1; + int n4=n>>2; + DATA_TYPE *iX; + DATA_TYPE *oX; + LOOKUP_T *T; + LOOKUP_T *V; + int shift; + int step; + + for (shift=6;!(n&(1<=in+n4); + do{ + oX-=4; + XPROD31( iX[4], iX[6], T[1], T[0], &oX[2], &oX[3] ); T-=step; + XPROD31( iX[0], iX[2], T[1], T[0], &oX[0], &oX[1] ); T-=step; + iX-=8; + }while(iX>=in); + + iX = in+n2-8; + oX = out+n2+n4; + T = sincos_lookup0; + + do{ + T+=step; XNPROD31( iX[6], iX[4], T[0], T[1], &oX[0], &oX[1] ); + T+=step; XNPROD31( iX[2], iX[0], T[0], T[1], &oX[2], &oX[3] ); + iX-=8; + oX+=4; + }while(iX>=in+n4); + do{ + T-=step; XNPROD31( iX[6], iX[4], T[1], T[0], &oX[0], &oX[1] ); + T-=step; XNPROD31( iX[2], iX[0], T[1], T[0], &oX[2], &oX[3] ); + iX-=8; + oX+=4; + }while(iX>=in); + + mdct_butterflies(out+n2,n2,shift); + mdct_bitreverse(out,n,step,shift); + + /* rotate + window */ + + step>>=2; + { + DATA_TYPE *oX1=out+n2+n4; + DATA_TYPE *oX2=out+n2+n4; + DATA_TYPE *iX =out; + + switch(step) { + default: { + T=(step>=4)?(sincos_lookup0+(step>>1)):sincos_lookup1; + do{ + oX1-=4; + XPROD31( iX[0], -iX[1], T[0], T[1], &oX1[3], &oX2[0] ); T+=step; + XPROD31( iX[2], -iX[3], T[0], T[1], &oX1[2], &oX2[1] ); T+=step; + XPROD31( iX[4], -iX[5], T[0], T[1], &oX1[1], &oX2[2] ); T+=step; + XPROD31( iX[6], -iX[7], T[0], T[1], &oX1[0], &oX2[3] ); T+=step; + oX2+=4; + iX+=8; + }while(iX>1; + t1 = (*T++)>>1; + do{ + oX1-=4; + + t0 += (v0 = (*V++)>>1); + t1 += (v1 = (*V++)>>1); + XPROD31( iX[0], -iX[1], t0, t1, &oX1[3], &oX2[0] ); + v0 += (t0 = (*T++)>>1); + v1 += (t1 = (*T++)>>1); + XPROD31( iX[2], -iX[3], v0, v1, &oX1[2], &oX2[1] ); + t0 += (v0 = (*V++)>>1); + t1 += (v1 = (*V++)>>1); + XPROD31( iX[4], -iX[5], t0, t1, &oX1[1], &oX2[2] ); + v0 += (t0 = (*T++)>>1); + v1 += (t1 = (*T++)>>1); + XPROD31( iX[6], -iX[7], v0, v1, &oX1[0], &oX2[3] ); + + oX2+=4; + iX+=8; + }while(iX>2); + t1 += (q1 = (v1-t1)>>2); + XPROD31( iX[0], -iX[1], t0, t1, &oX1[3], &oX2[0] ); + t0 = v0-q0; + t1 = v1-q1; + XPROD31( iX[2], -iX[3], t0, t1, &oX1[2], &oX2[1] ); + + t0 = *T++; + t1 = *T++; + v0 += (q0 = (t0-v0)>>2); + v1 += (q1 = (t1-v1)>>2); + XPROD31( iX[4], -iX[5], v0, v1, &oX1[1], &oX2[2] ); + v0 = t0-q0; + v1 = t1-q1; + XPROD31( iX[6], -iX[7], v0, v1, &oX1[0], &oX2[3] ); + + oX2+=4; + iX+=8; + }while(iXoX2); + } +} + diff --git a/libs/tremor/mdct.h b/libs/tremor/mdct.h new file mode 100644 index 0000000..6d88907 --- /dev/null +++ b/libs/tremor/mdct.h @@ -0,0 +1,52 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: modified discrete cosine transform prototypes + + ********************************************************************/ + +#ifndef _OGG_mdct_H_ +#define _OGG_mdct_H_ + +#include "ivorbiscodec.h" +#include "misc.h" + +#define DATA_TYPE ogg_int32_t +#define REG_TYPE register ogg_int32_t + +#ifdef _LOW_ACCURACY_ +#define cPI3_8 (0x0062) +#define cPI2_8 (0x00b5) +#define cPI1_8 (0x00ed) +#else +#define cPI3_8 (0x30fbc54d) +#define cPI2_8 (0x5a82799a) +#define cPI1_8 (0x7641af3d) +#endif + +extern void mdct_forward(int n, DATA_TYPE *in, DATA_TYPE *out); +extern void mdct_backward(int n, DATA_TYPE *in, DATA_TYPE *out); + +#endif + + + + + + + + + + + + diff --git a/libs/tremor/mdct_lookup.h b/libs/tremor/mdct_lookup.h new file mode 100644 index 0000000..ee4f101 --- /dev/null +++ b/libs/tremor/mdct_lookup.h @@ -0,0 +1,540 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: sin,cos lookup tables + + ********************************************************************/ + +#include "misc.h" + +/* {sin(2*i*PI/4096), cos(2*i*PI/4096)}, with i = 0 to 512 */ +static const LOOKUP_T sincos_lookup0[1026] = { + X(0x00000000), X(0x7fffffff), X(0x003243f5), X(0x7ffff621), + X(0x006487e3), X(0x7fffd886), X(0x0096cbc1), X(0x7fffa72c), + X(0x00c90f88), X(0x7fff6216), X(0x00fb5330), X(0x7fff0943), + X(0x012d96b1), X(0x7ffe9cb2), X(0x015fda03), X(0x7ffe1c65), + X(0x01921d20), X(0x7ffd885a), X(0x01c45ffe), X(0x7ffce093), + X(0x01f6a297), X(0x7ffc250f), X(0x0228e4e2), X(0x7ffb55ce), + X(0x025b26d7), X(0x7ffa72d1), X(0x028d6870), X(0x7ff97c18), + X(0x02bfa9a4), X(0x7ff871a2), X(0x02f1ea6c), X(0x7ff75370), + X(0x03242abf), X(0x7ff62182), X(0x03566a96), X(0x7ff4dbd9), + X(0x0388a9ea), X(0x7ff38274), X(0x03bae8b2), X(0x7ff21553), + X(0x03ed26e6), X(0x7ff09478), X(0x041f6480), X(0x7feeffe1), + X(0x0451a177), X(0x7fed5791), X(0x0483ddc3), X(0x7feb9b85), + X(0x04b6195d), X(0x7fe9cbc0), X(0x04e8543e), X(0x7fe7e841), + X(0x051a8e5c), X(0x7fe5f108), X(0x054cc7b1), X(0x7fe3e616), + X(0x057f0035), X(0x7fe1c76b), X(0x05b137df), X(0x7fdf9508), + X(0x05e36ea9), X(0x7fdd4eec), X(0x0615a48b), X(0x7fdaf519), + X(0x0647d97c), X(0x7fd8878e), X(0x067a0d76), X(0x7fd6064c), + X(0x06ac406f), X(0x7fd37153), X(0x06de7262), X(0x7fd0c8a3), + X(0x0710a345), X(0x7fce0c3e), X(0x0742d311), X(0x7fcb3c23), + X(0x077501be), X(0x7fc85854), X(0x07a72f45), X(0x7fc560cf), + X(0x07d95b9e), X(0x7fc25596), X(0x080b86c2), X(0x7fbf36aa), + X(0x083db0a7), X(0x7fbc040a), X(0x086fd947), X(0x7fb8bdb8), + X(0x08a2009a), X(0x7fb563b3), X(0x08d42699), X(0x7fb1f5fc), + X(0x09064b3a), X(0x7fae7495), X(0x09386e78), X(0x7faadf7c), + X(0x096a9049), X(0x7fa736b4), X(0x099cb0a7), X(0x7fa37a3c), + X(0x09cecf89), X(0x7f9faa15), X(0x0a00ece8), X(0x7f9bc640), + X(0x0a3308bd), X(0x7f97cebd), X(0x0a6522fe), X(0x7f93c38c), + X(0x0a973ba5), X(0x7f8fa4b0), X(0x0ac952aa), X(0x7f8b7227), + X(0x0afb6805), X(0x7f872bf3), X(0x0b2d7baf), X(0x7f82d214), + X(0x0b5f8d9f), X(0x7f7e648c), X(0x0b919dcf), X(0x7f79e35a), + X(0x0bc3ac35), X(0x7f754e80), X(0x0bf5b8cb), X(0x7f70a5fe), + X(0x0c27c389), X(0x7f6be9d4), X(0x0c59cc68), X(0x7f671a05), + X(0x0c8bd35e), X(0x7f62368f), X(0x0cbdd865), X(0x7f5d3f75), + X(0x0cefdb76), X(0x7f5834b7), X(0x0d21dc87), X(0x7f531655), + X(0x0d53db92), X(0x7f4de451), X(0x0d85d88f), X(0x7f489eaa), + X(0x0db7d376), X(0x7f434563), X(0x0de9cc40), X(0x7f3dd87c), + X(0x0e1bc2e4), X(0x7f3857f6), X(0x0e4db75b), X(0x7f32c3d1), + X(0x0e7fa99e), X(0x7f2d1c0e), X(0x0eb199a4), X(0x7f2760af), + X(0x0ee38766), X(0x7f2191b4), X(0x0f1572dc), X(0x7f1baf1e), + X(0x0f475bff), X(0x7f15b8ee), X(0x0f7942c7), X(0x7f0faf25), + X(0x0fab272b), X(0x7f0991c4), X(0x0fdd0926), X(0x7f0360cb), + X(0x100ee8ad), X(0x7efd1c3c), X(0x1040c5bb), X(0x7ef6c418), + X(0x1072a048), X(0x7ef05860), X(0x10a4784b), X(0x7ee9d914), + X(0x10d64dbd), X(0x7ee34636), X(0x11082096), X(0x7edc9fc6), + X(0x1139f0cf), X(0x7ed5e5c6), X(0x116bbe60), X(0x7ecf1837), + X(0x119d8941), X(0x7ec8371a), X(0x11cf516a), X(0x7ec14270), + X(0x120116d5), X(0x7eba3a39), X(0x1232d979), X(0x7eb31e78), + X(0x1264994e), X(0x7eabef2c), X(0x1296564d), X(0x7ea4ac58), + X(0x12c8106f), X(0x7e9d55fc), X(0x12f9c7aa), X(0x7e95ec1a), + X(0x132b7bf9), X(0x7e8e6eb2), X(0x135d2d53), X(0x7e86ddc6), + X(0x138edbb1), X(0x7e7f3957), X(0x13c0870a), X(0x7e778166), + X(0x13f22f58), X(0x7e6fb5f4), X(0x1423d492), X(0x7e67d703), + X(0x145576b1), X(0x7e5fe493), X(0x148715ae), X(0x7e57dea7), + X(0x14b8b17f), X(0x7e4fc53e), X(0x14ea4a1f), X(0x7e47985b), + X(0x151bdf86), X(0x7e3f57ff), X(0x154d71aa), X(0x7e37042a), + X(0x157f0086), X(0x7e2e9cdf), X(0x15b08c12), X(0x7e26221f), + X(0x15e21445), X(0x7e1d93ea), X(0x16139918), X(0x7e14f242), + X(0x16451a83), X(0x7e0c3d29), X(0x1676987f), X(0x7e0374a0), + X(0x16a81305), X(0x7dfa98a8), X(0x16d98a0c), X(0x7df1a942), + X(0x170afd8d), X(0x7de8a670), X(0x173c6d80), X(0x7ddf9034), + X(0x176dd9de), X(0x7dd6668f), X(0x179f429f), X(0x7dcd2981), + X(0x17d0a7bc), X(0x7dc3d90d), X(0x1802092c), X(0x7dba7534), + X(0x183366e9), X(0x7db0fdf8), X(0x1864c0ea), X(0x7da77359), + X(0x18961728), X(0x7d9dd55a), X(0x18c7699b), X(0x7d9423fc), + X(0x18f8b83c), X(0x7d8a5f40), X(0x192a0304), X(0x7d808728), + X(0x195b49ea), X(0x7d769bb5), X(0x198c8ce7), X(0x7d6c9ce9), + X(0x19bdcbf3), X(0x7d628ac6), X(0x19ef0707), X(0x7d58654d), + X(0x1a203e1b), X(0x7d4e2c7f), X(0x1a517128), X(0x7d43e05e), + X(0x1a82a026), X(0x7d3980ec), X(0x1ab3cb0d), X(0x7d2f0e2b), + X(0x1ae4f1d6), X(0x7d24881b), X(0x1b161479), X(0x7d19eebf), + X(0x1b4732ef), X(0x7d0f4218), X(0x1b784d30), X(0x7d048228), + X(0x1ba96335), X(0x7cf9aef0), X(0x1bda74f6), X(0x7ceec873), + X(0x1c0b826a), X(0x7ce3ceb2), X(0x1c3c8b8c), X(0x7cd8c1ae), + X(0x1c6d9053), X(0x7ccda169), X(0x1c9e90b8), X(0x7cc26de5), + X(0x1ccf8cb3), X(0x7cb72724), X(0x1d00843d), X(0x7cabcd28), + X(0x1d31774d), X(0x7ca05ff1), X(0x1d6265dd), X(0x7c94df83), + X(0x1d934fe5), X(0x7c894bde), X(0x1dc4355e), X(0x7c7da505), + X(0x1df5163f), X(0x7c71eaf9), X(0x1e25f282), X(0x7c661dbc), + X(0x1e56ca1e), X(0x7c5a3d50), X(0x1e879d0d), X(0x7c4e49b7), + X(0x1eb86b46), X(0x7c4242f2), X(0x1ee934c3), X(0x7c362904), + X(0x1f19f97b), X(0x7c29fbee), X(0x1f4ab968), X(0x7c1dbbb3), + X(0x1f7b7481), X(0x7c116853), X(0x1fac2abf), X(0x7c0501d2), + X(0x1fdcdc1b), X(0x7bf88830), X(0x200d888d), X(0x7bebfb70), + X(0x203e300d), X(0x7bdf5b94), X(0x206ed295), X(0x7bd2a89e), + X(0x209f701c), X(0x7bc5e290), X(0x20d0089c), X(0x7bb9096b), + X(0x21009c0c), X(0x7bac1d31), X(0x21312a65), X(0x7b9f1de6), + X(0x2161b3a0), X(0x7b920b89), X(0x219237b5), X(0x7b84e61f), + X(0x21c2b69c), X(0x7b77ada8), X(0x21f3304f), X(0x7b6a6227), + X(0x2223a4c5), X(0x7b5d039e), X(0x225413f8), X(0x7b4f920e), + X(0x22847de0), X(0x7b420d7a), X(0x22b4e274), X(0x7b3475e5), + X(0x22e541af), X(0x7b26cb4f), X(0x23159b88), X(0x7b190dbc), + X(0x2345eff8), X(0x7b0b3d2c), X(0x23763ef7), X(0x7afd59a4), + X(0x23a6887f), X(0x7aef6323), X(0x23d6cc87), X(0x7ae159ae), + X(0x24070b08), X(0x7ad33d45), X(0x243743fa), X(0x7ac50dec), + X(0x24677758), X(0x7ab6cba4), X(0x2497a517), X(0x7aa8766f), + X(0x24c7cd33), X(0x7a9a0e50), X(0x24f7efa2), X(0x7a8b9348), + X(0x25280c5e), X(0x7a7d055b), X(0x2558235f), X(0x7a6e648a), + X(0x2588349d), X(0x7a5fb0d8), X(0x25b84012), X(0x7a50ea47), + X(0x25e845b6), X(0x7a4210d8), X(0x26184581), X(0x7a332490), + X(0x26483f6c), X(0x7a24256f), X(0x26783370), X(0x7a151378), + X(0x26a82186), X(0x7a05eead), X(0x26d809a5), X(0x79f6b711), + X(0x2707ebc7), X(0x79e76ca7), X(0x2737c7e3), X(0x79d80f6f), + X(0x27679df4), X(0x79c89f6e), X(0x27976df1), X(0x79b91ca4), + X(0x27c737d3), X(0x79a98715), X(0x27f6fb92), X(0x7999dec4), + X(0x2826b928), X(0x798a23b1), X(0x2856708d), X(0x797a55e0), + X(0x288621b9), X(0x796a7554), X(0x28b5cca5), X(0x795a820e), + X(0x28e5714b), X(0x794a7c12), X(0x29150fa1), X(0x793a6361), + X(0x2944a7a2), X(0x792a37fe), X(0x29743946), X(0x7919f9ec), + X(0x29a3c485), X(0x7909a92d), X(0x29d34958), X(0x78f945c3), + X(0x2a02c7b8), X(0x78e8cfb2), X(0x2a323f9e), X(0x78d846fb), + X(0x2a61b101), X(0x78c7aba2), X(0x2a911bdc), X(0x78b6fda8), + X(0x2ac08026), X(0x78a63d11), X(0x2aefddd8), X(0x789569df), + X(0x2b1f34eb), X(0x78848414), X(0x2b4e8558), X(0x78738bb3), + X(0x2b7dcf17), X(0x786280bf), X(0x2bad1221), X(0x7851633b), + X(0x2bdc4e6f), X(0x78403329), X(0x2c0b83fa), X(0x782ef08b), + X(0x2c3ab2b9), X(0x781d9b65), X(0x2c69daa6), X(0x780c33b8), + X(0x2c98fbba), X(0x77fab989), X(0x2cc815ee), X(0x77e92cd9), + X(0x2cf72939), X(0x77d78daa), X(0x2d263596), X(0x77c5dc01), + X(0x2d553afc), X(0x77b417df), X(0x2d843964), X(0x77a24148), + X(0x2db330c7), X(0x7790583e), X(0x2de2211e), X(0x777e5cc3), + X(0x2e110a62), X(0x776c4edb), X(0x2e3fec8b), X(0x775a2e89), + X(0x2e6ec792), X(0x7747fbce), X(0x2e9d9b70), X(0x7735b6af), + X(0x2ecc681e), X(0x77235f2d), X(0x2efb2d95), X(0x7710f54c), + X(0x2f29ebcc), X(0x76fe790e), X(0x2f58a2be), X(0x76ebea77), + X(0x2f875262), X(0x76d94989), X(0x2fb5fab2), X(0x76c69647), + X(0x2fe49ba7), X(0x76b3d0b4), X(0x30133539), X(0x76a0f8d2), + X(0x3041c761), X(0x768e0ea6), X(0x30705217), X(0x767b1231), + X(0x309ed556), X(0x76680376), X(0x30cd5115), X(0x7654e279), + X(0x30fbc54d), X(0x7641af3d), X(0x312a31f8), X(0x762e69c4), + X(0x3158970e), X(0x761b1211), X(0x3186f487), X(0x7607a828), + X(0x31b54a5e), X(0x75f42c0b), X(0x31e39889), X(0x75e09dbd), + X(0x3211df04), X(0x75ccfd42), X(0x32401dc6), X(0x75b94a9c), + X(0x326e54c7), X(0x75a585cf), X(0x329c8402), X(0x7591aedd), + X(0x32caab6f), X(0x757dc5ca), X(0x32f8cb07), X(0x7569ca99), + X(0x3326e2c3), X(0x7555bd4c), X(0x3354f29b), X(0x75419de7), + X(0x3382fa88), X(0x752d6c6c), X(0x33b0fa84), X(0x751928e0), + X(0x33def287), X(0x7504d345), X(0x340ce28b), X(0x74f06b9e), + X(0x343aca87), X(0x74dbf1ef), X(0x3468aa76), X(0x74c7663a), + X(0x34968250), X(0x74b2c884), X(0x34c4520d), X(0x749e18cd), + X(0x34f219a8), X(0x7489571c), X(0x351fd918), X(0x74748371), + X(0x354d9057), X(0x745f9dd1), X(0x357b3f5d), X(0x744aa63f), + X(0x35a8e625), X(0x74359cbd), X(0x35d684a6), X(0x74208150), + X(0x36041ad9), X(0x740b53fb), X(0x3631a8b8), X(0x73f614c0), + X(0x365f2e3b), X(0x73e0c3a3), X(0x368cab5c), X(0x73cb60a8), + X(0x36ba2014), X(0x73b5ebd1), X(0x36e78c5b), X(0x73a06522), + X(0x3714f02a), X(0x738acc9e), X(0x37424b7b), X(0x73752249), + X(0x376f9e46), X(0x735f6626), X(0x379ce885), X(0x73499838), + X(0x37ca2a30), X(0x7333b883), X(0x37f76341), X(0x731dc70a), + X(0x382493b0), X(0x7307c3d0), X(0x3851bb77), X(0x72f1aed9), + X(0x387eda8e), X(0x72db8828), X(0x38abf0ef), X(0x72c54fc1), + X(0x38d8fe93), X(0x72af05a7), X(0x39060373), X(0x7298a9dd), + X(0x3932ff87), X(0x72823c67), X(0x395ff2c9), X(0x726bbd48), + X(0x398cdd32), X(0x72552c85), X(0x39b9bebc), X(0x723e8a20), + X(0x39e6975e), X(0x7227d61c), X(0x3a136712), X(0x7211107e), + X(0x3a402dd2), X(0x71fa3949), X(0x3a6ceb96), X(0x71e35080), + X(0x3a99a057), X(0x71cc5626), X(0x3ac64c0f), X(0x71b54a41), + X(0x3af2eeb7), X(0x719e2cd2), X(0x3b1f8848), X(0x7186fdde), + X(0x3b4c18ba), X(0x716fbd68), X(0x3b78a007), X(0x71586b74), + X(0x3ba51e29), X(0x71410805), X(0x3bd19318), X(0x7129931f), + X(0x3bfdfecd), X(0x71120cc5), X(0x3c2a6142), X(0x70fa74fc), + X(0x3c56ba70), X(0x70e2cbc6), X(0x3c830a50), X(0x70cb1128), + X(0x3caf50da), X(0x70b34525), X(0x3cdb8e09), X(0x709b67c0), + X(0x3d07c1d6), X(0x708378ff), X(0x3d33ec39), X(0x706b78e3), + X(0x3d600d2c), X(0x70536771), X(0x3d8c24a8), X(0x703b44ad), + X(0x3db832a6), X(0x7023109a), X(0x3de4371f), X(0x700acb3c), + X(0x3e10320d), X(0x6ff27497), X(0x3e3c2369), X(0x6fda0cae), + X(0x3e680b2c), X(0x6fc19385), X(0x3e93e950), X(0x6fa90921), + X(0x3ebfbdcd), X(0x6f906d84), X(0x3eeb889c), X(0x6f77c0b3), + X(0x3f1749b8), X(0x6f5f02b2), X(0x3f430119), X(0x6f463383), + X(0x3f6eaeb8), X(0x6f2d532c), X(0x3f9a5290), X(0x6f1461b0), + X(0x3fc5ec98), X(0x6efb5f12), X(0x3ff17cca), X(0x6ee24b57), + X(0x401d0321), X(0x6ec92683), X(0x40487f94), X(0x6eaff099), + X(0x4073f21d), X(0x6e96a99d), X(0x409f5ab6), X(0x6e7d5193), + X(0x40cab958), X(0x6e63e87f), X(0x40f60dfb), X(0x6e4a6e66), + X(0x4121589b), X(0x6e30e34a), X(0x414c992f), X(0x6e174730), + X(0x4177cfb1), X(0x6dfd9a1c), X(0x41a2fc1a), X(0x6de3dc11), + X(0x41ce1e65), X(0x6dca0d14), X(0x41f93689), X(0x6db02d29), + X(0x42244481), X(0x6d963c54), X(0x424f4845), X(0x6d7c3a98), + X(0x427a41d0), X(0x6d6227fa), X(0x42a5311b), X(0x6d48047e), + X(0x42d0161e), X(0x6d2dd027), X(0x42faf0d4), X(0x6d138afb), + X(0x4325c135), X(0x6cf934fc), X(0x4350873c), X(0x6cdece2f), + X(0x437b42e1), X(0x6cc45698), X(0x43a5f41e), X(0x6ca9ce3b), + X(0x43d09aed), X(0x6c8f351c), X(0x43fb3746), X(0x6c748b3f), + X(0x4425c923), X(0x6c59d0a9), X(0x4450507e), X(0x6c3f055d), + X(0x447acd50), X(0x6c242960), X(0x44a53f93), X(0x6c093cb6), + X(0x44cfa740), X(0x6bee3f62), X(0x44fa0450), X(0x6bd3316a), + X(0x452456bd), X(0x6bb812d1), X(0x454e9e80), X(0x6b9ce39b), + X(0x4578db93), X(0x6b81a3cd), X(0x45a30df0), X(0x6b66536b), + X(0x45cd358f), X(0x6b4af279), X(0x45f7526b), X(0x6b2f80fb), + X(0x4621647d), X(0x6b13fef5), X(0x464b6bbe), X(0x6af86c6c), + X(0x46756828), X(0x6adcc964), X(0x469f59b4), X(0x6ac115e2), + X(0x46c9405c), X(0x6aa551e9), X(0x46f31c1a), X(0x6a897d7d), + X(0x471cece7), X(0x6a6d98a4), X(0x4746b2bc), X(0x6a51a361), + X(0x47706d93), X(0x6a359db9), X(0x479a1d67), X(0x6a1987b0), + X(0x47c3c22f), X(0x69fd614a), X(0x47ed5be6), X(0x69e12a8c), + X(0x4816ea86), X(0x69c4e37a), X(0x48406e08), X(0x69a88c19), + X(0x4869e665), X(0x698c246c), X(0x48935397), X(0x696fac78), + X(0x48bcb599), X(0x69532442), X(0x48e60c62), X(0x69368bce), + X(0x490f57ee), X(0x6919e320), X(0x49389836), X(0x68fd2a3d), + X(0x4961cd33), X(0x68e06129), X(0x498af6df), X(0x68c387e9), + X(0x49b41533), X(0x68a69e81), X(0x49dd282a), X(0x6889a4f6), + X(0x4a062fbd), X(0x686c9b4b), X(0x4a2f2be6), X(0x684f8186), + X(0x4a581c9e), X(0x683257ab), X(0x4a8101de), X(0x68151dbe), + X(0x4aa9dba2), X(0x67f7d3c5), X(0x4ad2a9e2), X(0x67da79c3), + X(0x4afb6c98), X(0x67bd0fbd), X(0x4b2423be), X(0x679f95b7), + X(0x4b4ccf4d), X(0x67820bb7), X(0x4b756f40), X(0x676471c0), + X(0x4b9e0390), X(0x6746c7d8), X(0x4bc68c36), X(0x67290e02), + X(0x4bef092d), X(0x670b4444), X(0x4c177a6e), X(0x66ed6aa1), + X(0x4c3fdff4), X(0x66cf8120), X(0x4c6839b7), X(0x66b187c3), + X(0x4c9087b1), X(0x66937e91), X(0x4cb8c9dd), X(0x6675658c), + X(0x4ce10034), X(0x66573cbb), X(0x4d092ab0), X(0x66390422), + X(0x4d31494b), X(0x661abbc5), X(0x4d595bfe), X(0x65fc63a9), + X(0x4d8162c4), X(0x65ddfbd3), X(0x4da95d96), X(0x65bf8447), + X(0x4dd14c6e), X(0x65a0fd0b), X(0x4df92f46), X(0x65826622), + X(0x4e210617), X(0x6563bf92), X(0x4e48d0dd), X(0x6545095f), + X(0x4e708f8f), X(0x6526438f), X(0x4e984229), X(0x65076e25), + X(0x4ebfe8a5), X(0x64e88926), X(0x4ee782fb), X(0x64c99498), + X(0x4f0f1126), X(0x64aa907f), X(0x4f369320), X(0x648b7ce0), + X(0x4f5e08e3), X(0x646c59bf), X(0x4f857269), X(0x644d2722), + X(0x4faccfab), X(0x642de50d), X(0x4fd420a4), X(0x640e9386), + X(0x4ffb654d), X(0x63ef3290), X(0x50229da1), X(0x63cfc231), + X(0x5049c999), X(0x63b0426d), X(0x5070e92f), X(0x6390b34a), + X(0x5097fc5e), X(0x637114cc), X(0x50bf031f), X(0x635166f9), + X(0x50e5fd6d), X(0x6331a9d4), X(0x510ceb40), X(0x6311dd64), + X(0x5133cc94), X(0x62f201ac), X(0x515aa162), X(0x62d216b3), + X(0x518169a5), X(0x62b21c7b), X(0x51a82555), X(0x6292130c), + X(0x51ced46e), X(0x6271fa69), X(0x51f576ea), X(0x6251d298), + X(0x521c0cc2), X(0x62319b9d), X(0x524295f0), X(0x6211557e), + X(0x5269126e), X(0x61f1003f), X(0x528f8238), X(0x61d09be5), + X(0x52b5e546), X(0x61b02876), X(0x52dc3b92), X(0x618fa5f7), + X(0x53028518), X(0x616f146c), X(0x5328c1d0), X(0x614e73da), + X(0x534ef1b5), X(0x612dc447), X(0x537514c2), X(0x610d05b7), + X(0x539b2af0), X(0x60ec3830), X(0x53c13439), X(0x60cb5bb7), + X(0x53e73097), X(0x60aa7050), X(0x540d2005), X(0x60897601), + X(0x5433027d), X(0x60686ccf), X(0x5458d7f9), X(0x604754bf), + X(0x547ea073), X(0x60262dd6), X(0x54a45be6), X(0x6004f819), + X(0x54ca0a4b), X(0x5fe3b38d), X(0x54efab9c), X(0x5fc26038), + X(0x55153fd4), X(0x5fa0fe1f), X(0x553ac6ee), X(0x5f7f8d46), + X(0x556040e2), X(0x5f5e0db3), X(0x5585adad), X(0x5f3c7f6b), + X(0x55ab0d46), X(0x5f1ae274), X(0x55d05faa), X(0x5ef936d1), + X(0x55f5a4d2), X(0x5ed77c8a), X(0x561adcb9), X(0x5eb5b3a2), + X(0x56400758), X(0x5e93dc1f), X(0x566524aa), X(0x5e71f606), + X(0x568a34a9), X(0x5e50015d), X(0x56af3750), X(0x5e2dfe29), + X(0x56d42c99), X(0x5e0bec6e), X(0x56f9147e), X(0x5de9cc33), + X(0x571deefa), X(0x5dc79d7c), X(0x5742bc06), X(0x5da5604f), + X(0x57677b9d), X(0x5d8314b1), X(0x578c2dba), X(0x5d60baa7), + X(0x57b0d256), X(0x5d3e5237), X(0x57d5696d), X(0x5d1bdb65), + X(0x57f9f2f8), X(0x5cf95638), X(0x581e6ef1), X(0x5cd6c2b5), + X(0x5842dd54), X(0x5cb420e0), X(0x58673e1b), X(0x5c9170bf), + X(0x588b9140), X(0x5c6eb258), X(0x58afd6bd), X(0x5c4be5b0), + X(0x58d40e8c), X(0x5c290acc), X(0x58f838a9), X(0x5c0621b2), + X(0x591c550e), X(0x5be32a67), X(0x594063b5), X(0x5bc024f0), + X(0x59646498), X(0x5b9d1154), X(0x598857b2), X(0x5b79ef96), + X(0x59ac3cfd), X(0x5b56bfbd), X(0x59d01475), X(0x5b3381ce), + X(0x59f3de12), X(0x5b1035cf), X(0x5a1799d1), X(0x5aecdbc5), + X(0x5a3b47ab), X(0x5ac973b5), X(0x5a5ee79a), X(0x5aa5fda5), + X(0x5a82799a), X(0x5a82799a) + }; + + /* {sin((2*i+1)*PI/4096), cos((2*i+1)*PI/4096)}, with i = 0 to 511 */ +static const LOOKUP_T sincos_lookup1[1024] = { + X(0x001921fb), X(0x7ffffd88), X(0x004b65ee), X(0x7fffe9cb), + X(0x007da9d4), X(0x7fffc251), X(0x00afeda8), X(0x7fff8719), + X(0x00e23160), X(0x7fff3824), X(0x011474f6), X(0x7ffed572), + X(0x0146b860), X(0x7ffe5f03), X(0x0178fb99), X(0x7ffdd4d7), + X(0x01ab3e97), X(0x7ffd36ee), X(0x01dd8154), X(0x7ffc8549), + X(0x020fc3c6), X(0x7ffbbfe6), X(0x024205e8), X(0x7ffae6c7), + X(0x027447b0), X(0x7ff9f9ec), X(0x02a68917), X(0x7ff8f954), + X(0x02d8ca16), X(0x7ff7e500), X(0x030b0aa4), X(0x7ff6bcf0), + X(0x033d4abb), X(0x7ff58125), X(0x036f8a51), X(0x7ff4319d), + X(0x03a1c960), X(0x7ff2ce5b), X(0x03d407df), X(0x7ff1575d), + X(0x040645c7), X(0x7fefcca4), X(0x04388310), X(0x7fee2e30), + X(0x046abfb3), X(0x7fec7c02), X(0x049cfba7), X(0x7feab61a), + X(0x04cf36e5), X(0x7fe8dc78), X(0x05017165), X(0x7fe6ef1c), + X(0x0533ab20), X(0x7fe4ee06), X(0x0565e40d), X(0x7fe2d938), + X(0x05981c26), X(0x7fe0b0b1), X(0x05ca5361), X(0x7fde7471), + X(0x05fc89b8), X(0x7fdc247a), X(0x062ebf22), X(0x7fd9c0ca), + X(0x0660f398), X(0x7fd74964), X(0x06932713), X(0x7fd4be46), + X(0x06c5598a), X(0x7fd21f72), X(0x06f78af6), X(0x7fcf6ce8), + X(0x0729bb4e), X(0x7fcca6a7), X(0x075bea8c), X(0x7fc9ccb2), + X(0x078e18a7), X(0x7fc6df08), X(0x07c04598), X(0x7fc3dda9), + X(0x07f27157), X(0x7fc0c896), X(0x08249bdd), X(0x7fbd9fd0), + X(0x0856c520), X(0x7fba6357), X(0x0888ed1b), X(0x7fb7132b), + X(0x08bb13c5), X(0x7fb3af4e), X(0x08ed3916), X(0x7fb037bf), + X(0x091f5d06), X(0x7facac7f), X(0x09517f8f), X(0x7fa90d8e), + X(0x0983a0a7), X(0x7fa55aee), X(0x09b5c048), X(0x7fa1949e), + X(0x09e7de6a), X(0x7f9dbaa0), X(0x0a19fb04), X(0x7f99ccf4), + X(0x0a4c1610), X(0x7f95cb9a), X(0x0a7e2f85), X(0x7f91b694), + X(0x0ab0475c), X(0x7f8d8de1), X(0x0ae25d8d), X(0x7f895182), + X(0x0b147211), X(0x7f850179), X(0x0b4684df), X(0x7f809dc5), + X(0x0b7895f0), X(0x7f7c2668), X(0x0baaa53b), X(0x7f779b62), + X(0x0bdcb2bb), X(0x7f72fcb4), X(0x0c0ebe66), X(0x7f6e4a5e), + X(0x0c40c835), X(0x7f698461), X(0x0c72d020), X(0x7f64aabf), + X(0x0ca4d620), X(0x7f5fbd77), X(0x0cd6da2d), X(0x7f5abc8a), + X(0x0d08dc3f), X(0x7f55a7fa), X(0x0d3adc4e), X(0x7f507fc7), + X(0x0d6cda53), X(0x7f4b43f2), X(0x0d9ed646), X(0x7f45f47b), + X(0x0dd0d01f), X(0x7f409164), X(0x0e02c7d7), X(0x7f3b1aad), + X(0x0e34bd66), X(0x7f359057), X(0x0e66b0c3), X(0x7f2ff263), + X(0x0e98a1e9), X(0x7f2a40d2), X(0x0eca90ce), X(0x7f247ba5), + X(0x0efc7d6b), X(0x7f1ea2dc), X(0x0f2e67b8), X(0x7f18b679), + X(0x0f604faf), X(0x7f12b67c), X(0x0f923546), X(0x7f0ca2e7), + X(0x0fc41876), X(0x7f067bba), X(0x0ff5f938), X(0x7f0040f6), + X(0x1027d784), X(0x7ef9f29d), X(0x1059b352), X(0x7ef390ae), + X(0x108b8c9b), X(0x7eed1b2c), X(0x10bd6356), X(0x7ee69217), + X(0x10ef377d), X(0x7edff570), X(0x11210907), X(0x7ed94538), + X(0x1152d7ed), X(0x7ed28171), X(0x1184a427), X(0x7ecbaa1a), + X(0x11b66dad), X(0x7ec4bf36), X(0x11e83478), X(0x7ebdc0c6), + X(0x1219f880), X(0x7eb6aeca), X(0x124bb9be), X(0x7eaf8943), + X(0x127d7829), X(0x7ea85033), X(0x12af33ba), X(0x7ea1039b), + X(0x12e0ec6a), X(0x7e99a37c), X(0x1312a230), X(0x7e922fd6), + X(0x13445505), X(0x7e8aa8ac), X(0x137604e2), X(0x7e830dff), + X(0x13a7b1bf), X(0x7e7b5fce), X(0x13d95b93), X(0x7e739e1d), + X(0x140b0258), X(0x7e6bc8eb), X(0x143ca605), X(0x7e63e03b), + X(0x146e4694), X(0x7e5be40c), X(0x149fe3fc), X(0x7e53d462), + X(0x14d17e36), X(0x7e4bb13c), X(0x1503153a), X(0x7e437a9c), + X(0x1534a901), X(0x7e3b3083), X(0x15663982), X(0x7e32d2f4), + X(0x1597c6b7), X(0x7e2a61ed), X(0x15c95097), X(0x7e21dd73), + X(0x15fad71b), X(0x7e194584), X(0x162c5a3b), X(0x7e109a24), + X(0x165dd9f0), X(0x7e07db52), X(0x168f5632), X(0x7dff0911), + X(0x16c0cef9), X(0x7df62362), X(0x16f2443e), X(0x7ded2a47), + X(0x1723b5f9), X(0x7de41dc0), X(0x17552422), X(0x7ddafdce), + X(0x17868eb3), X(0x7dd1ca75), X(0x17b7f5a3), X(0x7dc883b4), + X(0x17e958ea), X(0x7dbf298d), X(0x181ab881), X(0x7db5bc02), + X(0x184c1461), X(0x7dac3b15), X(0x187d6c82), X(0x7da2a6c6), + X(0x18aec0db), X(0x7d98ff17), X(0x18e01167), X(0x7d8f4409), + X(0x19115e1c), X(0x7d85759f), X(0x1942a6f3), X(0x7d7b93da), + X(0x1973ebe6), X(0x7d719eba), X(0x19a52ceb), X(0x7d679642), + X(0x19d669fc), X(0x7d5d7a74), X(0x1a07a311), X(0x7d534b50), + X(0x1a38d823), X(0x7d4908d9), X(0x1a6a0929), X(0x7d3eb30f), + X(0x1a9b361d), X(0x7d3449f5), X(0x1acc5ef6), X(0x7d29cd8c), + X(0x1afd83ad), X(0x7d1f3dd6), X(0x1b2ea43a), X(0x7d149ad5), + X(0x1b5fc097), X(0x7d09e489), X(0x1b90d8bb), X(0x7cff1af5), + X(0x1bc1ec9e), X(0x7cf43e1a), X(0x1bf2fc3a), X(0x7ce94dfb), + X(0x1c240786), X(0x7cde4a98), X(0x1c550e7c), X(0x7cd333f3), + X(0x1c861113), X(0x7cc80a0f), X(0x1cb70f43), X(0x7cbcccec), + X(0x1ce80906), X(0x7cb17c8d), X(0x1d18fe54), X(0x7ca618f3), + X(0x1d49ef26), X(0x7c9aa221), X(0x1d7adb73), X(0x7c8f1817), + X(0x1dabc334), X(0x7c837ad8), X(0x1ddca662), X(0x7c77ca65), + X(0x1e0d84f5), X(0x7c6c06c0), X(0x1e3e5ee5), X(0x7c602fec), + X(0x1e6f342c), X(0x7c5445e9), X(0x1ea004c1), X(0x7c4848ba), + X(0x1ed0d09d), X(0x7c3c3860), X(0x1f0197b8), X(0x7c3014de), + X(0x1f325a0b), X(0x7c23de35), X(0x1f63178f), X(0x7c179467), + X(0x1f93d03c), X(0x7c0b3777), X(0x1fc4840a), X(0x7bfec765), + X(0x1ff532f2), X(0x7bf24434), X(0x2025dcec), X(0x7be5ade6), + X(0x205681f1), X(0x7bd9047c), X(0x208721f9), X(0x7bcc47fa), + X(0x20b7bcfe), X(0x7bbf7860), X(0x20e852f6), X(0x7bb295b0), + X(0x2118e3dc), X(0x7ba59fee), X(0x21496fa7), X(0x7b989719), + X(0x2179f64f), X(0x7b8b7b36), X(0x21aa77cf), X(0x7b7e4c45), + X(0x21daf41d), X(0x7b710a49), X(0x220b6b32), X(0x7b63b543), + X(0x223bdd08), X(0x7b564d36), X(0x226c4996), X(0x7b48d225), + X(0x229cb0d5), X(0x7b3b4410), X(0x22cd12bd), X(0x7b2da2fa), + X(0x22fd6f48), X(0x7b1feee5), X(0x232dc66d), X(0x7b1227d3), + X(0x235e1826), X(0x7b044dc7), X(0x238e646a), X(0x7af660c2), + X(0x23beab33), X(0x7ae860c7), X(0x23eeec78), X(0x7ada4dd8), + X(0x241f2833), X(0x7acc27f7), X(0x244f5e5c), X(0x7abdef25), + X(0x247f8eec), X(0x7aafa367), X(0x24afb9da), X(0x7aa144bc), + X(0x24dfdf20), X(0x7a92d329), X(0x250ffeb7), X(0x7a844eae), + X(0x25401896), X(0x7a75b74f), X(0x25702cb7), X(0x7a670d0d), + X(0x25a03b11), X(0x7a584feb), X(0x25d0439f), X(0x7a497feb), + X(0x26004657), X(0x7a3a9d0f), X(0x26304333), X(0x7a2ba75a), + X(0x26603a2c), X(0x7a1c9ece), X(0x26902b39), X(0x7a0d836d), + X(0x26c01655), X(0x79fe5539), X(0x26effb76), X(0x79ef1436), + X(0x271fda96), X(0x79dfc064), X(0x274fb3ae), X(0x79d059c8), + X(0x277f86b5), X(0x79c0e062), X(0x27af53a6), X(0x79b15435), + X(0x27df1a77), X(0x79a1b545), X(0x280edb23), X(0x79920392), + X(0x283e95a1), X(0x79823f20), X(0x286e49ea), X(0x797267f2), + X(0x289df7f8), X(0x79627e08), X(0x28cd9fc1), X(0x79528167), + X(0x28fd4140), X(0x79427210), X(0x292cdc6d), X(0x79325006), + X(0x295c7140), X(0x79221b4b), X(0x298bffb2), X(0x7911d3e2), + X(0x29bb87bc), X(0x790179cd), X(0x29eb0957), X(0x78f10d0f), + X(0x2a1a847b), X(0x78e08dab), X(0x2a49f920), X(0x78cffba3), + X(0x2a796740), X(0x78bf56f9), X(0x2aa8ced3), X(0x78ae9fb0), + X(0x2ad82fd2), X(0x789dd5cb), X(0x2b078a36), X(0x788cf94c), + X(0x2b36ddf7), X(0x787c0a36), X(0x2b662b0e), X(0x786b088c), + X(0x2b957173), X(0x7859f44f), X(0x2bc4b120), X(0x7848cd83), + X(0x2bf3ea0d), X(0x7837942b), X(0x2c231c33), X(0x78264849), + X(0x2c52478a), X(0x7814e9df), X(0x2c816c0c), X(0x780378f1), + X(0x2cb089b1), X(0x77f1f581), X(0x2cdfa071), X(0x77e05f91), + X(0x2d0eb046), X(0x77ceb725), X(0x2d3db928), X(0x77bcfc3f), + X(0x2d6cbb10), X(0x77ab2ee2), X(0x2d9bb5f6), X(0x77994f11), + X(0x2dcaa9d5), X(0x77875cce), X(0x2df996a3), X(0x7775581d), + X(0x2e287c5a), X(0x776340ff), X(0x2e575af3), X(0x77511778), + X(0x2e863267), X(0x773edb8b), X(0x2eb502ae), X(0x772c8d3a), + X(0x2ee3cbc1), X(0x771a2c88), X(0x2f128d99), X(0x7707b979), + X(0x2f41482e), X(0x76f5340e), X(0x2f6ffb7a), X(0x76e29c4b), + X(0x2f9ea775), X(0x76cff232), X(0x2fcd4c19), X(0x76bd35c7), + X(0x2ffbe95d), X(0x76aa670d), X(0x302a7f3a), X(0x76978605), + X(0x30590dab), X(0x768492b4), X(0x308794a6), X(0x76718d1c), + X(0x30b61426), X(0x765e7540), X(0x30e48c22), X(0x764b4b23), + X(0x3112fc95), X(0x76380ec8), X(0x31416576), X(0x7624c031), + X(0x316fc6be), X(0x76115f63), X(0x319e2067), X(0x75fdec60), + X(0x31cc7269), X(0x75ea672a), X(0x31fabcbd), X(0x75d6cfc5), + X(0x3228ff5c), X(0x75c32634), X(0x32573a3f), X(0x75af6a7b), + X(0x32856d5e), X(0x759b9c9b), X(0x32b398b3), X(0x7587bc98), + X(0x32e1bc36), X(0x7573ca75), X(0x330fd7e1), X(0x755fc635), + X(0x333debab), X(0x754bafdc), X(0x336bf78f), X(0x7537876c), + X(0x3399fb85), X(0x75234ce8), X(0x33c7f785), X(0x750f0054), + X(0x33f5eb89), X(0x74faa1b3), X(0x3423d78a), X(0x74e63108), + X(0x3451bb81), X(0x74d1ae55), X(0x347f9766), X(0x74bd199f), + X(0x34ad6b32), X(0x74a872e8), X(0x34db36df), X(0x7493ba34), + X(0x3508fa66), X(0x747eef85), X(0x3536b5be), X(0x746a12df), + X(0x356468e2), X(0x74552446), X(0x359213c9), X(0x744023bc), + X(0x35bfb66e), X(0x742b1144), X(0x35ed50c9), X(0x7415ece2), + X(0x361ae2d3), X(0x7400b69a), X(0x36486c86), X(0x73eb6e6e), + X(0x3675edd9), X(0x73d61461), X(0x36a366c6), X(0x73c0a878), + X(0x36d0d746), X(0x73ab2ab4), X(0x36fe3f52), X(0x73959b1b), + X(0x372b9ee3), X(0x737ff9ae), X(0x3758f5f2), X(0x736a4671), + X(0x37864477), X(0x73548168), X(0x37b38a6d), X(0x733eaa96), + X(0x37e0c7cc), X(0x7328c1ff), X(0x380dfc8d), X(0x7312c7a5), + X(0x383b28a9), X(0x72fcbb8c), X(0x38684c19), X(0x72e69db7), + X(0x389566d6), X(0x72d06e2b), X(0x38c278d9), X(0x72ba2cea), + X(0x38ef821c), X(0x72a3d9f7), X(0x391c8297), X(0x728d7557), + X(0x39497a43), X(0x7276ff0d), X(0x39766919), X(0x7260771b), + X(0x39a34f13), X(0x7249dd86), X(0x39d02c2a), X(0x72333251), + X(0x39fd0056), X(0x721c7580), X(0x3a29cb91), X(0x7205a716), + X(0x3a568dd4), X(0x71eec716), X(0x3a834717), X(0x71d7d585), + X(0x3aaff755), X(0x71c0d265), X(0x3adc9e86), X(0x71a9bdba), + X(0x3b093ca3), X(0x71929789), X(0x3b35d1a5), X(0x717b5fd3), + X(0x3b625d86), X(0x7164169d), X(0x3b8ee03e), X(0x714cbbeb), + X(0x3bbb59c7), X(0x71354fc0), X(0x3be7ca1a), X(0x711dd220), + X(0x3c143130), X(0x7106430e), X(0x3c408f03), X(0x70eea28e), + X(0x3c6ce38a), X(0x70d6f0a4), X(0x3c992ec0), X(0x70bf2d53), + X(0x3cc5709e), X(0x70a7589f), X(0x3cf1a91c), X(0x708f728b), + X(0x3d1dd835), X(0x70777b1c), X(0x3d49fde1), X(0x705f7255), + X(0x3d761a19), X(0x70475839), X(0x3da22cd7), X(0x702f2ccd), + X(0x3dce3614), X(0x7016f014), X(0x3dfa35c8), X(0x6ffea212), + X(0x3e262bee), X(0x6fe642ca), X(0x3e52187f), X(0x6fcdd241), + X(0x3e7dfb73), X(0x6fb5507a), X(0x3ea9d4c3), X(0x6f9cbd79), + X(0x3ed5a46b), X(0x6f841942), X(0x3f016a61), X(0x6f6b63d8), + X(0x3f2d26a0), X(0x6f529d40), X(0x3f58d921), X(0x6f39c57d), + X(0x3f8481dd), X(0x6f20dc92), X(0x3fb020ce), X(0x6f07e285), + X(0x3fdbb5ec), X(0x6eeed758), X(0x40074132), X(0x6ed5bb10), + X(0x4032c297), X(0x6ebc8db0), X(0x405e3a16), X(0x6ea34f3d), + X(0x4089a7a8), X(0x6e89ffb9), X(0x40b50b46), X(0x6e709f2a), + X(0x40e064ea), X(0x6e572d93), X(0x410bb48c), X(0x6e3daaf8), + X(0x4136fa27), X(0x6e24175c), X(0x416235b2), X(0x6e0a72c5), + X(0x418d6729), X(0x6df0bd35), X(0x41b88e84), X(0x6dd6f6b1), + X(0x41e3abbc), X(0x6dbd1f3c), X(0x420ebecb), X(0x6da336dc), + X(0x4239c7aa), X(0x6d893d93), X(0x4264c653), X(0x6d6f3365), + X(0x428fbabe), X(0x6d551858), X(0x42baa4e6), X(0x6d3aec6e), + X(0x42e584c3), X(0x6d20afac), X(0x43105a50), X(0x6d066215), + X(0x433b2585), X(0x6cec03af), X(0x4365e65b), X(0x6cd1947c), + X(0x43909ccd), X(0x6cb71482), X(0x43bb48d4), X(0x6c9c83c3), + X(0x43e5ea68), X(0x6c81e245), X(0x44108184), X(0x6c67300b), + X(0x443b0e21), X(0x6c4c6d1a), X(0x44659039), X(0x6c319975), + X(0x449007c4), X(0x6c16b521), X(0x44ba74bd), X(0x6bfbc021), + X(0x44e4d71c), X(0x6be0ba7b), X(0x450f2edb), X(0x6bc5a431), + X(0x45397bf4), X(0x6baa7d49), X(0x4563be60), X(0x6b8f45c7), + X(0x458df619), X(0x6b73fdae), X(0x45b82318), X(0x6b58a503), + X(0x45e24556), X(0x6b3d3bcb), X(0x460c5cce), X(0x6b21c208), + X(0x46366978), X(0x6b0637c1), X(0x46606b4e), X(0x6aea9cf8), + X(0x468a624a), X(0x6acef1b2), X(0x46b44e65), X(0x6ab335f4), + X(0x46de2f99), X(0x6a9769c1), X(0x470805df), X(0x6a7b8d1e), + X(0x4731d131), X(0x6a5fa010), X(0x475b9188), X(0x6a43a29a), + X(0x478546de), X(0x6a2794c1), X(0x47aef12c), X(0x6a0b7689), + X(0x47d8906d), X(0x69ef47f6), X(0x48022499), X(0x69d3090e), + X(0x482badab), X(0x69b6b9d3), X(0x48552b9b), X(0x699a5a4c), + X(0x487e9e64), X(0x697dea7b), X(0x48a805ff), X(0x69616a65), + X(0x48d16265), X(0x6944da10), X(0x48fab391), X(0x6928397e), + X(0x4923f97b), X(0x690b88b5), X(0x494d341e), X(0x68eec7b9), + X(0x49766373), X(0x68d1f68f), X(0x499f8774), X(0x68b5153a), + X(0x49c8a01b), X(0x689823bf), X(0x49f1ad61), X(0x687b2224), + X(0x4a1aaf3f), X(0x685e106c), X(0x4a43a5b0), X(0x6840ee9b), + X(0x4a6c90ad), X(0x6823bcb7), X(0x4a957030), X(0x68067ac3), + X(0x4abe4433), X(0x67e928c5), X(0x4ae70caf), X(0x67cbc6c0), + X(0x4b0fc99d), X(0x67ae54ba), X(0x4b387af9), X(0x6790d2b6), + X(0x4b6120bb), X(0x677340ba), X(0x4b89badd), X(0x67559eca), + X(0x4bb24958), X(0x6737ecea), X(0x4bdacc28), X(0x671a2b20), + X(0x4c034345), X(0x66fc596f), X(0x4c2baea9), X(0x66de77dc), + X(0x4c540e4e), X(0x66c0866d), X(0x4c7c622d), X(0x66a28524), + X(0x4ca4aa41), X(0x66847408), X(0x4ccce684), X(0x6666531d), + X(0x4cf516ee), X(0x66482267), X(0x4d1d3b7a), X(0x6629e1ec), + X(0x4d455422), X(0x660b91af), X(0x4d6d60df), X(0x65ed31b5), + X(0x4d9561ac), X(0x65cec204), X(0x4dbd5682), X(0x65b0429f), + X(0x4de53f5a), X(0x6591b38c), X(0x4e0d1c30), X(0x657314cf), + X(0x4e34ecfc), X(0x6554666d), X(0x4e5cb1b9), X(0x6535a86b), + X(0x4e846a60), X(0x6516dacd), X(0x4eac16eb), X(0x64f7fd98), + X(0x4ed3b755), X(0x64d910d1), X(0x4efb4b96), X(0x64ba147d), + X(0x4f22d3aa), X(0x649b08a0), X(0x4f4a4f89), X(0x647bed3f), + X(0x4f71bf2e), X(0x645cc260), X(0x4f992293), X(0x643d8806), + X(0x4fc079b1), X(0x641e3e38), X(0x4fe7c483), X(0x63fee4f8), + X(0x500f0302), X(0x63df7c4d), X(0x50363529), X(0x63c0043b), + X(0x505d5af1), X(0x63a07cc7), X(0x50847454), X(0x6380e5f6), + X(0x50ab814d), X(0x63613fcd), X(0x50d281d5), X(0x63418a50), + X(0x50f975e6), X(0x6321c585), X(0x51205d7b), X(0x6301f171), + X(0x5147388c), X(0x62e20e17), X(0x516e0715), X(0x62c21b7e), + X(0x5194c910), X(0x62a219aa), X(0x51bb7e75), X(0x628208a1), + X(0x51e22740), X(0x6261e866), X(0x5208c36a), X(0x6241b8ff), + X(0x522f52ee), X(0x62217a72), X(0x5255d5c5), X(0x62012cc2), + X(0x527c4bea), X(0x61e0cff5), X(0x52a2b556), X(0x61c06410), + X(0x52c91204), X(0x619fe918), X(0x52ef61ee), X(0x617f5f12), + X(0x5315a50e), X(0x615ec603), X(0x533bdb5d), X(0x613e1df0), + X(0x536204d7), X(0x611d66de), X(0x53882175), X(0x60fca0d2), + X(0x53ae3131), X(0x60dbcbd1), X(0x53d43406), X(0x60bae7e1), + X(0x53fa29ed), X(0x6099f505), X(0x542012e1), X(0x6078f344), + X(0x5445eedb), X(0x6057e2a2), X(0x546bbdd7), X(0x6036c325), + X(0x54917fce), X(0x601594d1), X(0x54b734ba), X(0x5ff457ad), + X(0x54dcdc96), X(0x5fd30bbc), X(0x5502775c), X(0x5fb1b104), + X(0x55280505), X(0x5f90478a), X(0x554d858d), X(0x5f6ecf53), + X(0x5572f8ed), X(0x5f4d4865), X(0x55985f20), X(0x5f2bb2c5), + X(0x55bdb81f), X(0x5f0a0e77), X(0x55e303e6), X(0x5ee85b82), + X(0x5608426e), X(0x5ec699e9), X(0x562d73b2), X(0x5ea4c9b3), + X(0x565297ab), X(0x5e82eae5), X(0x5677ae54), X(0x5e60fd84), + X(0x569cb7a8), X(0x5e3f0194), X(0x56c1b3a1), X(0x5e1cf71c), + X(0x56e6a239), X(0x5dfade20), X(0x570b8369), X(0x5dd8b6a7), + X(0x5730572e), X(0x5db680b4), X(0x57551d80), X(0x5d943c4e), + X(0x5779d65b), X(0x5d71e979), X(0x579e81b8), X(0x5d4f883b), + X(0x57c31f92), X(0x5d2d189a), X(0x57e7afe4), X(0x5d0a9a9a), + X(0x580c32a7), X(0x5ce80e41), X(0x5830a7d6), X(0x5cc57394), + X(0x58550f6c), X(0x5ca2ca99), X(0x58796962), X(0x5c801354), + X(0x589db5b3), X(0x5c5d4dcc), X(0x58c1f45b), X(0x5c3a7a05), + X(0x58e62552), X(0x5c179806), X(0x590a4893), X(0x5bf4a7d2), + X(0x592e5e19), X(0x5bd1a971), X(0x595265df), X(0x5bae9ce7), + X(0x59765fde), X(0x5b8b8239), X(0x599a4c12), X(0x5b68596d), + X(0x59be2a74), X(0x5b452288), X(0x59e1faff), X(0x5b21dd90), + X(0x5a05bdae), X(0x5afe8a8b), X(0x5a29727b), X(0x5adb297d), + X(0x5a4d1960), X(0x5ab7ba6c), X(0x5a70b258), X(0x5a943d5e), +}; + diff --git a/libs/tremor/misc.h b/libs/tremor/misc.h new file mode 100644 index 0000000..ff1b400 --- /dev/null +++ b/libs/tremor/misc.h @@ -0,0 +1,252 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: miscellaneous math and prototypes + + ********************************************************************/ + +#ifndef _V_RANDOM_H_ +#define _V_RANDOM_H_ +#include "ivorbiscodec.h" +#include "os.h" + +#ifdef _LOW_ACCURACY_ +# define X(n) (((((n)>>22)+1)>>1) - ((((n)>>22)+1)>>9)) +# define LOOKUP_T const unsigned char +#else +# define X(n) (n) +# define LOOKUP_T const ogg_int32_t +#endif + +#include "asm_arm.h" +#include /* for abs() */ + +#ifndef _V_WIDE_MATH +#define _V_WIDE_MATH + +#ifndef _LOW_ACCURACY_ +/* 64 bit multiply */ + +#if !(defined WIN32 && defined WINCE) +#include +#endif + +#if BYTE_ORDER==LITTLE_ENDIAN +union magic { + struct { + ogg_int32_t lo; + ogg_int32_t hi; + } halves; + ogg_int64_t whole; +}; +#endif + +/*#if BYTE_ORDER==BIG_ENDIAN +union magic { + struct { + ogg_int32_t hi; + ogg_int32_t lo; + } halves; + ogg_int64_t whole; +}; +#endif*/ + +STIN ogg_int32_t MULT32(ogg_int32_t x, ogg_int32_t y) { + union magic magic; + magic.whole = (ogg_int64_t)x * y; + return magic.halves.hi; +} + +STIN ogg_int32_t MULT31(ogg_int32_t x, ogg_int32_t y) { + return MULT32(x,y)<<1; +} + +STIN ogg_int32_t MULT31_SHIFT15(ogg_int32_t x, ogg_int32_t y) { + union magic magic; + magic.whole = (ogg_int64_t)x * y; + return ((ogg_uint32_t)(magic.halves.lo)>>15) | ((magic.halves.hi)<<17); +} + +#else +/* 32 bit multiply, more portable but less accurate */ + +/* + * Note: Precision is biased towards the first argument therefore ordering + * is important. Shift values were chosen for the best sound quality after + * many listening tests. + */ + +/* + * For MULT32 and MULT31: The second argument is always a lookup table + * value already preshifted from 31 to 8 bits. We therefore take the + * opportunity to save on text space and use unsigned char for those + * tables in this case. + */ + +STIN ogg_int32_t MULT32(ogg_int32_t x, ogg_int32_t y) { + return (x >> 9) * y; /* y preshifted >>23 */ +} + +STIN ogg_int32_t MULT31(ogg_int32_t x, ogg_int32_t y) { + return (x >> 8) * y; /* y preshifted >>23 */ +} + +STIN ogg_int32_t MULT31_SHIFT15(ogg_int32_t x, ogg_int32_t y) { + return (x >> 6) * y; /* y preshifted >>9 */ +} + +#endif + +/* + * This should be used as a memory barrier, forcing all cached values in + * registers to wr writen back to memory. Might or might not be beneficial + * depending on the architecture and compiler. + */ +#define MB() + +/* + * The XPROD functions are meant to optimize the cross products found all + * over the place in mdct.c by forcing memory operation ordering to avoid + * unnecessary register reloads as soon as memory is being written to. + * However this is only beneficial on CPUs with a sane number of general + * purpose registers which exclude the Intel x86. On Intel, better let the + * compiler actually reload registers directly from original memory by using + * macros. + */ + +#ifdef __i386__ + +#define XPROD32(_a, _b, _t, _v, _x, _y) \ + { *(_x)=MULT32(_a,_t)+MULT32(_b,_v); \ + *(_y)=MULT32(_b,_t)-MULT32(_a,_v); } +#define XPROD31(_a, _b, _t, _v, _x, _y) \ + { *(_x)=MULT31(_a,_t)+MULT31(_b,_v); \ + *(_y)=MULT31(_b,_t)-MULT31(_a,_v); } +#define XNPROD31(_a, _b, _t, _v, _x, _y) \ + { *(_x)=MULT31(_a,_t)-MULT31(_b,_v); \ + *(_y)=MULT31(_b,_t)+MULT31(_a,_v); } + +#else + +STIN void XPROD32(ogg_int32_t a, ogg_int32_t b, + ogg_int32_t t, ogg_int32_t v, + ogg_int32_t *x, ogg_int32_t *y) +{ + *x = MULT32(a, t) + MULT32(b, v); + *y = MULT32(b, t) - MULT32(a, v); +} + +STIN void XPROD31(ogg_int32_t a, ogg_int32_t b, + ogg_int32_t t, ogg_int32_t v, + ogg_int32_t *x, ogg_int32_t *y) +{ + *x = MULT31(a, t) + MULT31(b, v); + *y = MULT31(b, t) - MULT31(a, v); +} + +STIN void XNPROD31(ogg_int32_t a, ogg_int32_t b, + ogg_int32_t t, ogg_int32_t v, + ogg_int32_t *x, ogg_int32_t *y) +{ + *x = MULT31(a, t) - MULT31(b, v); + *y = MULT31(b, t) + MULT31(a, v); +} + +#endif + +#endif + +#ifndef _V_CLIP_MATH +#define _V_CLIP_MATH + +STIN ogg_int32_t CLIP_TO_15(ogg_int32_t x) { + int ret=x; + ret-= ((x<=32767)-1)&(x-32767); + ret-= ((x>=-32768)-1)&(x+32768); + return(ret); +} + +#endif + +STIN ogg_int32_t VFLOAT_MULT(ogg_int32_t a,ogg_int32_t ap, + ogg_int32_t b,ogg_int32_t bp, + ogg_int32_t *p){ + if(a && b){ +#ifndef _LOW_ACCURACY_ + *p=ap+bp+32; + return MULT32(a,b); +#else + *p=ap+bp+31; + return (a>>15)*(b>>16); +#endif + }else + return 0; +} + +int _ilog(unsigned int); + +STIN ogg_int32_t VFLOAT_MULTI(ogg_int32_t a,ogg_int32_t ap, + ogg_int32_t i, + ogg_int32_t *p){ + + int ip=_ilog(abs(i))-31; + return VFLOAT_MULT(a,ap,i<<-ip,ip,p); +} + +STIN ogg_int32_t VFLOAT_ADD(ogg_int32_t a,ogg_int32_t ap, + ogg_int32_t b,ogg_int32_t bp, + ogg_int32_t *p){ + + if(!a){ + *p=bp; + return b; + }else if(!b){ + *p=ap; + return a; + } + + /* yes, this can leak a bit. */ + if(ap>bp){ + int shift=ap-bp+1; + *p=ap+1; + a>>=1; + if(shift<32){ + b=(b+(1<<(shift-1)))>>shift; + }else{ + b=0; + } + }else{ + int shift=bp-ap+1; + *p=bp+1; + b>>=1; + if(shift<32){ + a=(a+(1<<(shift-1)))>>shift; + }else{ + a=0; + } + } + + a+=b; + if((a&0xc0000000)==0xc0000000 || + (a&0xc0000000)==0){ + a<<=1; + (*p)--; + } + return(a); +} + +#endif + + + + diff --git a/libs/tremor/os.h b/libs/tremor/os.h new file mode 100644 index 0000000..130d27d --- /dev/null +++ b/libs/tremor/os.h @@ -0,0 +1,64 @@ +#ifndef _OS_H +#define _OS_H +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: #ifdef jail to whip a few platforms into the UNIX ideal. + + ********************************************************************/ + +#include +#include + +#ifndef _V_IFDEFJAIL_H_ +# define _V_IFDEFJAIL_H_ + +# ifdef __GNUC__ +# define STIN static __inline__ +# elif _WIN32 +# define STIN static __inline +# endif +#else +# define STIN static +#endif + +#ifndef M_PI +# define M_PI (3.1415926536f) +#endif + +#ifdef _WIN32 +# include +# define rint(x) (floor((x)+0.5f)) +# define NO_FLOAT_MATH_LIB +# define FAST_HYPOT(a, b) sqrt((a)*(a) + (b)*(b)) +# define LITTLE_ENDIAN 1 +# define BYTE_ORDER LITTLE_ENDIAN +#endif + +#ifdef HAVE_ALLOCA_H +# include +#endif + +#ifdef USE_MEMORY_H +# include +#endif + +#ifndef min +# define min(x,y) ((x)>(y)?(y):(x)) +#endif + +#ifndef max +# define max(x,y) ((x)<(y)?(y):(x)) +#endif + +#endif /* _OS_H */ diff --git a/libs/tremor/registry.c b/libs/tremor/registry.c new file mode 100644 index 0000000..c0b5fec --- /dev/null +++ b/libs/tremor/registry.c @@ -0,0 +1,50 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: registry for floor, res backends and channel mappings + + ********************************************************************/ + +#include "ivorbiscodec.h" +#include "codec_internal.h" +#include "registry.h" +#include "misc.h" + + +/* seems like major overkill now; the backend numbers will grow into + the infrastructure soon enough */ + +extern vorbis_func_floor floor0_exportbundle; +extern vorbis_func_floor floor1_exportbundle; +extern vorbis_func_residue residue0_exportbundle; +extern vorbis_func_residue residue1_exportbundle; +extern vorbis_func_residue residue2_exportbundle; +extern vorbis_func_mapping mapping0_exportbundle; + +vorbis_func_floor *_floor_P[]={ + &floor0_exportbundle, + &floor1_exportbundle, +}; + +vorbis_func_residue *_residue_P[]={ + &residue0_exportbundle, + &residue1_exportbundle, + &residue2_exportbundle, +}; + +vorbis_func_mapping *_mapping_P[]={ + &mapping0_exportbundle, +}; + + + diff --git a/libs/tremor/registry.h b/libs/tremor/registry.h new file mode 100644 index 0000000..2bc8068 --- /dev/null +++ b/libs/tremor/registry.h @@ -0,0 +1,40 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: registry for time, floor, res backends and channel mappings + + ********************************************************************/ + +#ifndef _V_REG_H_ +#define _V_REG_H_ + +#define VI_TRANSFORMB 1 +#define VI_WINDOWB 1 +#define VI_TIMEB 1 +#define VI_FLOORB 2 +#define VI_RESB 3 +#define VI_MAPB 1 + +#include "backends.h" + +#if defined(_WIN32) && defined(VORBISDLL_IMPORT) +# define EXTERN __declspec(dllimport) extern +#else +# define EXTERN extern +#endif + +EXTERN vorbis_func_floor *_floor_P[]; +EXTERN vorbis_func_residue *_residue_P[]; +EXTERN vorbis_func_mapping *_mapping_P[]; + +#endif diff --git a/libs/tremor/res012.c b/libs/tremor/res012.c new file mode 100644 index 0000000..f036caa --- /dev/null +++ b/libs/tremor/res012.c @@ -0,0 +1,374 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: residue backend 0, 1 and 2 implementation + + ********************************************************************/ + +#include +#include +#include +#include +#include "ivorbiscodec.h" +#include "codec_internal.h" +#include "registry.h" +#include "codebook.h" +#include "misc.h" +#include "os.h" +#include "block.h" + +typedef struct { + vorbis_info_residue0 *info; + int map; + + int parts; + int stages; + codebook *fullbooks; + codebook *phrasebook; + codebook ***partbooks; + + int partvals; + int **decodemap; + +} vorbis_look_residue0; + +void res0_free_info(vorbis_info_residue *i){ + vorbis_info_residue0 *info=(vorbis_info_residue0 *)i; + if(info){ + memset(info,0,sizeof(*info)); + _ogg_free(info); + } +} + +void res0_free_look(vorbis_look_residue *i){ + int j; + if(i){ + + vorbis_look_residue0 *look=(vorbis_look_residue0 *)i; + + for(j=0;jparts;j++) + if(look->partbooks[j])_ogg_free(look->partbooks[j]); + _ogg_free(look->partbooks); + for(j=0;jpartvals;j++) + _ogg_free(look->decodemap[j]); + _ogg_free(look->decodemap); + + memset(look,0,sizeof(*look)); + _ogg_free(look); + } +} + +static int ilog(unsigned int v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +static int icount(unsigned int v){ + int ret=0; + while(v){ + ret+=v&1; + v>>=1; + } + return(ret); +} + +/* vorbis_info is for range checking */ +vorbis_info_residue *res0_unpack(vorbis_info *vi,oggpack_buffer *opb){ + int j,acc=0; + vorbis_info_residue0 *info=(vorbis_info_residue0 *)_ogg_calloc(1,sizeof(*info)); + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + + info->begin=oggpack_read(opb,24); + info->end=oggpack_read(opb,24); + info->grouping=oggpack_read(opb,24)+1; + info->partitions=oggpack_read(opb,6)+1; + info->groupbook=oggpack_read(opb,8); + + /* check for premature EOP */ + if(info->groupbook<0)goto errout; + + for(j=0;jpartitions;j++){ + int cascade=oggpack_read(opb,3); + int cflag=oggpack_read(opb,1); + if(cflag<0) goto errout; + if(cflag){ + int c=oggpack_read(opb,5); + if(c<0) goto errout; + cascade|=(c<<3); + } + info->secondstages[j]=cascade; + + acc+=icount(cascade); + } + for(j=0;jbooklist[j]=book; + } + + if(info->groupbook>=ci->books)goto errout; + for(j=0;jbooklist[j]>=ci->books)goto errout; + if(ci->book_param[info->booklist[j]]->maptype==0)goto errout; + } + + /* verify the phrasebook is not specifying an impossible or + inconsistent partitioning scheme. */ + /* modify the phrasebook ranging check from r16327; an early beta + encoder had a bug where it used an oversized phrasebook by + accident. These files should continue to be playable, but don't + allow an exploit */ + { + int entries = ci->book_param[info->groupbook]->entries; + int dim = ci->book_param[info->groupbook]->dim; + int partvals = 1; + if (dim<1) goto errout; + while(dim>0){ + partvals *= info->partitions; + if(partvals > entries) goto errout; + dim--; + } + info->partvals = partvals; + } + + return(info); + errout: + res0_free_info(info); + return(NULL); +} + +vorbis_look_residue *res0_look(vorbis_dsp_state *vd,vorbis_info_mode *vm, + vorbis_info_residue *vr){ + vorbis_info_residue0 *info=(vorbis_info_residue0 *)vr; + vorbis_look_residue0 *look=(vorbis_look_residue0 *)_ogg_calloc(1,sizeof(*look)); + codec_setup_info *ci=(codec_setup_info *)vd->vi->codec_setup; + + int j,k,acc=0; + int dim; + int maxstage=0; + look->info=info; + look->map=vm->mapping; + + look->parts=info->partitions; + look->fullbooks=ci->fullbooks; + look->phrasebook=ci->fullbooks+info->groupbook; + dim=look->phrasebook->dim; + + look->partbooks=(codebook ***)_ogg_calloc(look->parts,sizeof(*look->partbooks)); + + for(j=0;jparts;j++){ + int stages=ilog(info->secondstages[j]); + if(stages){ + if(stages>maxstage)maxstage=stages; + look->partbooks[j]=(codebook **)_ogg_calloc(stages,sizeof(*look->partbooks[j])); + for(k=0;ksecondstages[j]&(1<partbooks[j][k]=ci->fullbooks+info->booklist[acc++]; +#ifdef TRAIN_RES + look->training_data[k][j]=calloc(look->partbooks[j][k]->entries, + sizeof(***look->training_data)); +#endif + } + } + } + + look->partvals=look->parts; + for(j=1;jpartvals*=look->parts; + look->stages=maxstage; + look->decodemap=(int **)_ogg_malloc(look->partvals*sizeof(*look->decodemap)); + for(j=0;jpartvals;j++){ + long val=j; + long mult=look->partvals/look->parts; + look->decodemap[j]=(int *)_ogg_malloc(dim*sizeof(*look->decodemap[j])); + for(k=0;kparts; + look->decodemap[j][k]=deco; + } + } + + return(look); +} + + +/* a truncated packet here just means 'stop working'; it's not an error */ +static int _01inverse(vorbis_block *vb,vorbis_look_residue *vl, + ogg_int32_t **in,int ch, + long (*decodepart)(codebook *, ogg_int32_t *, + oggpack_buffer *,int,int)){ + + long i,j,k,l,s; + vorbis_look_residue0 *look=(vorbis_look_residue0 *)vl; + vorbis_info_residue0 *info=look->info; + + /* move all this setup out later */ + int samples_per_partition=info->grouping; + int partitions_per_word=look->phrasebook->dim; + int max=vb->pcmend>>1; + int end=(info->endend:max); + int n=end-info->begin; + + if(n>0){ + int partvals=n/samples_per_partition; + int partwords=(partvals+partitions_per_word-1)/partitions_per_word; + int ***partword=(int ***)alloca(ch*sizeof(*partword)); + + for(j=0;jstages;s++){ + + /* each loop decodes on partition codeword containing + partitions_pre_word partitions */ + for(i=0,l=0;iphrasebook,&vb->opb); + if(temp==-1 || temp>=info->partvals)goto eopbreak; + partword[j][l]=look->decodemap[temp]; + if(partword[j][l]==NULL)goto errout; + } + } + + /* now we decode residual values for the partitions */ + for(k=0;kbegin+i*samples_per_partition; + if(info->secondstages[partword[j][l][k]]&(1<partbooks[partword[j][l][k]][s]; + if(stagebook){ + if(decodepart(stagebook,in[j]+offset,&vb->opb, + samples_per_partition,-8)==-1)goto eopbreak; + } + } + } + } + } + } + errout: + eopbreak: + return(0); +} + +int res0_inverse(vorbis_block *vb,vorbis_look_residue *vl, + ogg_int32_t **in,int *nonzero,int ch){ + int i,used=0; + for(i=0;iinfo; + + /* move all this setup out later */ + int samples_per_partition=info->grouping; + int partitions_per_word=look->phrasebook->dim; + int max=(vb->pcmend*ch)>>1; + int end=(info->endend:max); + int n=end-info->begin; + + if(n>0){ + + int partvals=n/samples_per_partition; + int partwords=(partvals+partitions_per_word-1)/partitions_per_word; + int **partword=(int **)_vorbis_block_alloc(vb,partwords*sizeof(*partword)); + int beginoff=info->begin/ch; + + for(i=0;istages;s++){ + for(i=0,l=0;iphrasebook,&vb->opb); + if(temp==-1 || temp>=info->partvals)goto eopbreak; + partword[l]=look->decodemap[temp]; + if(partword[l]==NULL)goto errout; + } + + /* now we decode residual values for the partitions */ + for(k=0;ksecondstages[partword[l][k]]&(1<partbooks[partword[l][k]][s]; + + if(stagebook){ + if(vorbis_book_decodevv_add(stagebook,in, + i*samples_per_partition+beginoff,ch, + &vb->opb, + samples_per_partition,-8)==-1) + goto eopbreak; + } + } + } + } + } + errout: + eopbreak: + return(0); +} + + +vorbis_func_residue residue0_exportbundle={ + &res0_unpack, + &res0_look, + &res0_free_info, + &res0_free_look, + &res0_inverse +}; + +vorbis_func_residue residue1_exportbundle={ + &res0_unpack, + &res0_look, + &res0_free_info, + &res0_free_look, + &res1_inverse +}; + +vorbis_func_residue residue2_exportbundle={ + &res0_unpack, + &res0_look, + &res0_free_info, + &res0_free_look, + &res2_inverse +}; diff --git a/libs/tremor/sharedbook.c b/libs/tremor/sharedbook.c new file mode 100644 index 0000000..188485e --- /dev/null +++ b/libs/tremor/sharedbook.c @@ -0,0 +1,447 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: basic shared codebook operations + + ********************************************************************/ + +#include +#include +#include +#include +#include "misc.h" +#include "ivorbiscodec.h" +#include "codebook.h" + +/**** pack/unpack helpers ******************************************/ +int _ilog(unsigned int v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +/* 32 bit float (not IEEE; nonnormalized mantissa + + biased exponent) : neeeeeee eeemmmmm mmmmmmmm mmmmmmmm + Why not IEEE? It's just not that important here. */ + +#define VQ_FEXP 10 +#define VQ_FMAN 21 +#define VQ_FEXP_BIAS 768 /* bias toward values smaller than 1. */ + +static ogg_int32_t _float32_unpack(long val,int *point){ + long mant=val&0x1fffff; + int sign=val&0x80000000; + long exp =(val&0x7fe00000L)>>VQ_FMAN; + + exp-=(VQ_FMAN-1)+VQ_FEXP_BIAS; + + if(mant){ + while(!(mant&0x40000000)){ + mant<<=1; + exp-=1; + } + + if(sign)mant= -mant; + }else{ + sign=0; + exp=-9999; + } + + *point=exp; + return mant; +} + +/* given a list of word lengths, generate a list of codewords. Works + for length ordered or unordered, always assigns the lowest valued + codewords first. Extended to handle unused entries (length 0) */ +ogg_uint32_t *_make_words(long *l,long n,long sparsecount){ + long i,j,count=0; + ogg_uint32_t marker[33]; + ogg_uint32_t *r=(ogg_uint32_t *)_ogg_malloc((sparsecount?sparsecount:n)*sizeof(*r)); + memset(marker,0,sizeof(marker)); + + for(i=0;i0){ + ogg_uint32_t entry=marker[length]; + + /* when we claim a node for an entry, we also claim the nodes + below it (pruning off the imagined tree that may have dangled + from it) as well as blocking the use of any nodes directly + above for leaves */ + + /* update ourself */ + if(length<32 && (entry>>length)){ + /* error condition; the lengths must specify an overpopulated tree */ + _ogg_free(r); + return(NULL); + } + r[count++]=entry; + + /* Look to see if the next shorter marker points to the node + above. if so, update it and repeat. */ + { + for(j=length;j>0;j--){ + + if(marker[j]&1){ + /* have to jump branches */ + if(j==1) + marker[1]++; + else + marker[j]=marker[j-1]<<1; + break; /* invariant says next upper marker would already + have been moved if it was on the same path */ + } + marker[j]++; + } + } + + /* prune the tree; the implicit invariant says all the longer + markers were dangling from our just-taken node. Dangle them + from our *new* node. */ + for(j=length+1;j<33;j++) + if((marker[j]>>1) == entry){ + entry=marker[j]; + marker[j]=marker[j-1]<<1; + }else + break; + }else + if(sparsecount==0)count++; + } + + /* sanity check the huffman tree; an underpopulated tree must be + rejected. The only exception is the one-node pseudo-nil tree, + which appears to be underpopulated because the tree doesn't + really exist; there's only one possible 'codeword' or zero bits, + but the above tree-gen code doesn't mark that. */ + if(sparsecount != 1){ + for(i=1;i<33;i++) + if(marker[i] & (0xffffffffUL>>(32-i))){ + _ogg_free(r); + return(NULL); + } + } + + /* bitreverse the words because our bitwise packer/unpacker is LSb + endian */ + for(i=0,count=0;i>j)&1; + } + + if(sparsecount){ + if(l[i]) + r[count++]=temp; + }else + r[count++]=temp; + } + + return(r); +} + +/* there might be a straightforward one-line way to do the below + that's portable and totally safe against roundoff, but I haven't + thought of it. Therefore, we opt on the side of caution */ +long _book_maptype1_quantvals(const static_codebook *b){ + /* get us a starting hint, we'll polish it below */ + int bits=_ilog(b->entries); + int vals=b->entries>>((bits-1)*(b->dim-1)/b->dim); + + while(1){ + long acc=1; + long acc1=1; + int i; + for(i=0;idim;i++){ + acc*=vals; + acc1*=vals+1; + } + if(acc<=b->entries && acc1>b->entries){ + return(vals); + }else{ + if(acc>b->entries){ + vals--; + }else{ + vals++; + } + } + } +} + +/* different than what _book_unquantize does for mainline: + we repack the book in a fixed point format that shares the same + binary point. Upon first use, we can shift point if needed */ + +/* we need to deal with two map types: in map type 1, the values are + generated algorithmically (each column of the vector counts through + the values in the quant vector). in map type 2, all the values came + in in an explicit list. Both value lists must be unpacked */ + +ogg_int32_t *_book_unquantize(const static_codebook *b,int n,int *sparsemap, + int *maxpoint){ + long j,k,count=0; + if(b->maptype==1 || b->maptype==2){ + int quantvals; + int minpoint,delpoint; + ogg_int32_t mindel=_float32_unpack(b->q_min,&minpoint); + ogg_int32_t delta=_float32_unpack(b->q_delta,&delpoint); + ogg_int32_t *r=(ogg_int32_t *)_ogg_calloc(n*b->dim,sizeof(*r)); + int *rp=(int *)_ogg_calloc(n*b->dim,sizeof(*rp)); + + *maxpoint=minpoint; + + /* maptype 1 and 2 both use a quantized value vector, but + different sizes */ + switch(b->maptype){ + case 1: + /* most of the time, entries%dimensions == 0, but we need to be + well defined. We define that the possible vales at each + scalar is values == entries/dim. If entries%dim != 0, we'll + have 'too few' values (values*dimentries;j++){ + if((sparsemap && b->lengthlist[j]) || !sparsemap){ + ogg_int32_t last=0; + int lastpoint=0; + int indexdiv=1; + for(k=0;kdim;k++){ + int index= (j/indexdiv)%quantvals; + int point=0; + int val=VFLOAT_MULTI(delta,delpoint, + abs(b->quantlist[index]),&point); + + val=VFLOAT_ADD(mindel,minpoint,val,point,&point); + val=VFLOAT_ADD(last,lastpoint,val,point,&point); + + if(b->q_sequencep){ + last=val; + lastpoint=point; + } + + if(sparsemap){ + r[sparsemap[count]*b->dim+k]=val; + rp[sparsemap[count]*b->dim+k]=point; + }else{ + r[count*b->dim+k]=val; + rp[count*b->dim+k]=point; + } + if(*maxpointentries;j++){ + if((sparsemap && b->lengthlist[j]) || !sparsemap){ + ogg_int32_t last=0; + int lastpoint=0; + + for(k=0;kdim;k++){ + int point=0; + int val=VFLOAT_MULTI(delta,delpoint, + abs(b->quantlist[j*b->dim+k]),&point); + + val=VFLOAT_ADD(mindel,minpoint,val,point,&point); + val=VFLOAT_ADD(last,lastpoint,val,point,&point); + + if(b->q_sequencep){ + last=val; + lastpoint=point; + } + + if(sparsemap){ + r[sparsemap[count]*b->dim+k]=val; + rp[sparsemap[count]*b->dim+k]=point; + }else{ + r[count*b->dim+k]=val; + rp[count*b->dim+k]=point; + } + if(*maxpointdim;j++) + if(rp[j]<*maxpoint) + r[j]>>=*maxpoint-rp[j]; + + _ogg_free(rp); + return(r); + } + return(NULL); +} + +void vorbis_staticbook_destroy(static_codebook *b){ + if(b->quantlist)_ogg_free(b->quantlist); + if(b->lengthlist)_ogg_free(b->lengthlist); + memset(b,0,sizeof(*b)); + _ogg_free(b); +} + +void vorbis_book_clear(codebook *b){ + /* static book is not cleared; we're likely called on the lookup and + the static codebook belongs to the info struct */ + if(b->valuelist)_ogg_free(b->valuelist); + if(b->codelist)_ogg_free(b->codelist); + + if(b->dec_index)_ogg_free(b->dec_index); + if(b->dec_codelengths)_ogg_free(b->dec_codelengths); + if(b->dec_firsttable)_ogg_free(b->dec_firsttable); + + memset(b,0,sizeof(*b)); +} + +static ogg_uint32_t bitreverse(ogg_uint32_t x){ + x= ((x>>16)&0x0000ffffUL) | ((x<<16)&0xffff0000UL); + x= ((x>> 8)&0x00ff00ffUL) | ((x<< 8)&0xff00ff00UL); + x= ((x>> 4)&0x0f0f0f0fUL) | ((x<< 4)&0xf0f0f0f0UL); + x= ((x>> 2)&0x33333333UL) | ((x<< 2)&0xccccccccUL); + return((x>> 1)&0x55555555UL) | ((x<< 1)&0xaaaaaaaaUL); +} + +static int sort32a(const void *a,const void *b){ + return (**(ogg_uint32_t **)a>**(ogg_uint32_t **)b)- + (**(ogg_uint32_t **)a<**(ogg_uint32_t **)b); +} + +/* decode codebook arrangement is more heavily optimized than encode */ +int vorbis_book_init_decode(codebook *c,const static_codebook *s){ + int i,j,n=0,tabn; + int *sortindex; + memset(c,0,sizeof(*c)); + + /* count actually used entries */ + for(i=0;ientries;i++) + if(s->lengthlist[i]>0) + n++; + + c->entries=s->entries; + c->used_entries=n; + c->dim=s->dim; + + if(n>0){ + /* two different remappings go on here. + + First, we collapse the likely sparse codebook down only to + actually represented values/words. This collapsing needs to be + indexed as map-valueless books are used to encode original entry + positions as integers. + + Second, we reorder all vectors, including the entry index above, + by sorted bitreversed codeword to allow treeless decode. */ + + /* perform sort */ + ogg_uint32_t *codes=_make_words(s->lengthlist,s->entries,c->used_entries); + ogg_uint32_t **codep=(ogg_uint32_t **)alloca(sizeof(*codep)*n); + + if(codes==NULL)goto err_out; + + for(i=0;icodelist=(ogg_uint32_t *)_ogg_malloc(n*sizeof(*c->codelist)); + /* the index is a reverse index */ + for(i=0;icodelist[sortindex[i]]=codes[i]; + _ogg_free(codes); + + + + c->valuelist=_book_unquantize(s,n,sortindex,&c->binarypoint); + c->dec_index=(int *)_ogg_malloc(n*sizeof(*c->dec_index)); + + for(n=0,i=0;ientries;i++) + if(s->lengthlist[i]>0) + c->dec_index[sortindex[n++]]=i; + + c->dec_codelengths=(char *)_ogg_malloc(n*sizeof(*c->dec_codelengths)); + for(n=0,i=0;ientries;i++) + if(s->lengthlist[i]>0) + c->dec_codelengths[sortindex[n++]]=s->lengthlist[i]; + + c->dec_firsttablen=_ilog(c->used_entries)-4; /* this is magic */ + if(c->dec_firsttablen<5)c->dec_firsttablen=5; + if(c->dec_firsttablen>8)c->dec_firsttablen=8; + + tabn=1<dec_firsttablen; + c->dec_firsttable=(ogg_uint32_t *)_ogg_calloc(tabn,sizeof(*c->dec_firsttable)); + c->dec_maxlength=0; + + for(i=0;idec_maxlengthdec_codelengths[i]) + c->dec_maxlength=c->dec_codelengths[i]; + if(c->dec_codelengths[i]<=c->dec_firsttablen){ + ogg_uint32_t orig=bitreverse(c->codelist[i]); + for(j=0;j<(1<<(c->dec_firsttablen-c->dec_codelengths[i]));j++) + c->dec_firsttable[orig|(j<dec_codelengths[i])]=i+1; + } + } + + /* now fill in 'unused' entries in the firsttable with hi/lo search + hints for the non-direct-hits */ + { + ogg_uint32_t mask=0xfffffffeUL<<(31-c->dec_firsttablen); + long lo=0,hi=0; + + for(i=0;idec_firsttablen); + if(c->dec_firsttable[bitreverse(word)]==0){ + while((lo+1)codelist[lo+1]<=word)lo++; + while( hi=(c->codelist[hi]&mask))hi++; + + /* we only actually have 15 bits per hint to play with here. + In order to overflow gracefully (nothing breaks, efficiency + just drops), encode as the difference from the extremes. */ + { + unsigned long loval=lo; + unsigned long hival=n-hi; + + if(loval>0x7fff)loval=0x7fff; + if(hival>0x7fff)hival=0x7fff; + c->dec_firsttable[bitreverse(word)]= + 0x80000000UL | (loval<<15) | hival; + } + } + } + } + } + + return(0); + err_out: + vorbis_book_clear(c); + return(-1); +} + diff --git a/libs/tremor/synthesis.c b/libs/tremor/synthesis.c new file mode 100644 index 0000000..d22cb82 --- /dev/null +++ b/libs/tremor/synthesis.c @@ -0,0 +1,131 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: single-block PCM synthesis + last mod: $Id: synthesis.c,v 1.4 2003/03/29 03:07:21 xiphmont Exp $ + + ********************************************************************/ + +#include +#include +#include "ivorbiscodec.h" +#include "codec_internal.h" +#include "registry.h" +#include "misc.h" +#include "block.h" + +static int _vorbis_synthesis1(vorbis_block *vb,ogg_packet *op,int decodep){ + vorbis_dsp_state *vd= vb ? vb->vd : 0; + private_state *b= vd ? (private_state *)vd->backend_state: 0; + vorbis_info *vi= vd ? vd->vi : 0; + codec_setup_info *ci= vi ? (codec_setup_info *)vi->codec_setup : 0; + oggpack_buffer *opb=vb ? &vb->opb : 0; + int type,mode,i; + + if (!vd || !b || !vi || !ci || !opb) { + return OV_EBADPACKET; + } + + /* first things first. Make sure decode is ready */ + _vorbis_block_ripcord(vb); + oggpack_readinit(opb,op->packet,op->bytes); + + /* Check the packet type */ + if(oggpack_read(opb,1)!=0){ + /* Oops. This is not an audio data packet */ + return(OV_ENOTAUDIO); + } + + /* read our mode and pre/post windowsize */ + mode=oggpack_read(opb,b->modebits); + if(mode==-1)return(OV_EBADPACKET); + + vb->mode=mode; + if(!ci->mode_param[mode]){ + return(OV_EBADPACKET); + } + + vb->W=ci->mode_param[mode]->blockflag; + if(vb->W){ + vb->lW=oggpack_read(opb,1); + vb->nW=oggpack_read(opb,1); + if(vb->nW==-1) return(OV_EBADPACKET); + }else{ + vb->lW=0; + vb->nW=0; + } + + /* more setup */ + vb->granulepos=op->granulepos; + vb->sequence=op->packetno; /* first block is third packet */ + vb->eofflag=op->e_o_s; + + if(decodep){ + /* alloc pcm passback storage */ + vb->pcmend=ci->blocksizes[vb->W]; + vb->pcm=(ogg_int32_t **)_vorbis_block_alloc(vb,sizeof(*vb->pcm)*vi->channels); + for(i=0;ichannels;i++) + vb->pcm[i]=(ogg_int32_t *)_vorbis_block_alloc(vb,vb->pcmend*sizeof(*vb->pcm[i])); + + /* unpack_header enforces range checking */ + type=ci->map_type[ci->mode_param[mode]->mapping]; + + return(_mapping_P[type]->inverse(vb,b->mode[mode])); + }else{ + /* no pcm */ + vb->pcmend=0; + vb->pcm=NULL; + + return(0); + } +} + +int vorbis_synthesis(vorbis_block *vb,ogg_packet *op){ + return _vorbis_synthesis1(vb,op,1); +} + +/* used to track pcm position without actually performing decode. + Useful for sequential 'fast forward' */ +int vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op){ + return _vorbis_synthesis1(vb,op,0); +} + +long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op){ + codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; + oggpack_buffer opb; + int mode; + + oggpack_readinit(&opb,op->packet,op->bytes); + + /* Check the packet type */ + if(oggpack_read(&opb,1)!=0){ + /* Oops. This is not an audio data packet */ + return(OV_ENOTAUDIO); + } + + { + int modebits=0; + int v=ci->modes; + while(v>1){ + modebits++; + v>>=1; + } + + /* read our mode and pre/post windowsize */ + mode=oggpack_read(&opb,modebits); + } + if(mode==-1 || !ci->mode_param[mode])return(OV_EBADPACKET); + return(ci->blocksizes[ci->mode_param[mode]->blockflag]); +} + + diff --git a/libs/tremor/vorbisfile.c b/libs/tremor/vorbisfile.c new file mode 100644 index 0000000..cd4814d --- /dev/null +++ b/libs/tremor/vorbisfile.c @@ -0,0 +1,1968 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2014 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: stdio-based convenience library for opening/seeking/decoding + last mod: $Id$ + + ********************************************************************/ + +#include +#include +#include +#include +#include + +#include "ivorbiscodec.h" +#include "ivorbisfile.h" + +#include "os.h" +#include "misc.h" + +/* A 'chained bitstream' is a Vorbis bitstream that contains more than + one logical bitstream arranged end to end (the only form of Ogg + multiplexing allowed in a Vorbis bitstream; grouping [parallel + multiplexing] is not allowed in Vorbis) */ + +/* A Vorbis file can be played beginning to end (streamed) without + worrying ahead of time about chaining (see decoder_example.c). If + we have the whole file, however, and want random access + (seeking/scrubbing) or desire to know the total length/time of a + file, we need to account for the possibility of chaining. */ + +/* We can handle things a number of ways; we can determine the entire + bitstream structure right off the bat, or find pieces on demand. + This example determines and caches structure for the entire + bitstream, but builds a virtual decoder on the fly when moving + between links in the chain. */ + +/* There are also different ways to implement seeking. Enough + information exists in an Ogg bitstream to seek to + sample-granularity positions in the output. Or, one can seek by + picking some portion of the stream roughly in the desired area if + we only want coarse navigation through the stream. */ + +/************************************************************************* + * Many, many internal helpers. The intention is not to be confusing; + * rampant duplication and monolithic function implementation would be + * harder to understand anyway. The high level functions are last. Begin + * grokking near the end of the file */ + + +/* read a little more data from the file/pipe into the ogg_sync framer */ +static long _get_data(OggVorbis_File *vf){ + errno=0; + if(!(vf->callbacks.read_func))return(-1); + if(vf->datasource){ + char *buffer=ogg_sync_buffer(&vf->oy,READSIZE); + long bytes=(vf->callbacks.read_func)(buffer,1,READSIZE,vf->datasource); + if(bytes>0)ogg_sync_wrote(&vf->oy,bytes); + if(bytes==0 && errno)return(-1); + return(bytes); + }else + return(0); +} + +/* save a tiny smidge of verbosity to make the code more readable */ +static int _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){ + if(vf->datasource){ + /* only seek if the file position isn't already there */ + if(vf->offset != offset){ + if(!(vf->callbacks.seek_func)|| + (vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET) == -1) + return OV_EREAD; + vf->offset=offset; + ogg_sync_reset(&vf->oy); + } + }else{ + /* shouldn't happen unless someone writes a broken callback */ + return OV_EFAULT; + } + return 0; +} + +/* The read/seek functions track absolute position within the stream */ + +/* from the head of the stream, get the next page. boundary specifies + if the function is allowed to fetch more data from the stream (and + how much) or only use internally buffered data. + + boundary: -1) unbounded search + 0) read no additional data; use cached only + n) search for a new page beginning for n bytes + + return: <0) did not find a page (OV_FALSE, OV_EOF, OV_EREAD) + n) found a page at absolute offset n */ + +static ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og, + ogg_int64_t boundary){ + if(boundary>0)boundary+=vf->offset; + while(1){ + long more; + + if(boundary>0 && vf->offset>=boundary)return(OV_FALSE); + more=ogg_sync_pageseek(&vf->oy,og); + + if(more<0){ + /* skipped n bytes */ + vf->offset-=more; + }else{ + if(more==0){ + /* send more paramedics */ + if(!boundary)return(OV_FALSE); + { + long ret=_get_data(vf); + if(ret==0)return(OV_EOF); + if(ret<0)return(OV_EREAD); + } + }else{ + /* got a page. Return the offset at the page beginning, + advance the internal offset past the page end */ + ogg_int64_t ret=vf->offset; + vf->offset+=more; + return(ret); + + } + } + } +} + +/* find the latest page beginning before the passed in position. Much + dirtier than the above as Ogg doesn't have any backward search + linkage. no 'readp' as it will certainly have to read. */ +/* returns offset or OV_EREAD, OV_FAULT */ +static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_int64_t begin,ogg_page *og){ + ogg_int64_t end = begin; + ogg_int64_t ret; + ogg_int64_t offset=-1; + + while(offset==-1){ + begin-=CHUNKSIZE; + if(begin<0) + begin=0; + + ret=_seek_helper(vf,begin); + if(ret)return(ret); + + while(vf->offsetoffset); + if(ret==OV_EREAD)return(OV_EREAD); + if(ret<0){ + break; + }else{ + offset=ret; + } + } + } + + /* In a fully compliant, non-multiplexed stream, we'll still be + holding the last page. In multiplexed (or noncompliant streams), + we will probably have to re-read the last page we saw */ + if(og->header_len==0){ + ret=_seek_helper(vf,offset); + if(ret)return(ret); + + ret=_get_next_page(vf,og,CHUNKSIZE); + if(ret<0) + /* this shouldn't be possible */ + return(OV_EFAULT); + } + + return(offset); +} + +static void _add_serialno(ogg_page *og,ogg_uint32_t **serialno_list, int *n){ + ogg_uint32_t s = ogg_page_serialno(og); + (*n)++; + + if(*serialno_list){ + *serialno_list = _ogg_realloc(*serialno_list, sizeof(**serialno_list)*(*n)); + }else{ + *serialno_list = _ogg_malloc(sizeof(**serialno_list)); + } + + (*serialno_list)[(*n)-1] = s; +} + +/* returns nonzero if found */ +static int _lookup_serialno(ogg_uint32_t s, ogg_uint32_t *serialno_list, int n){ + if(serialno_list){ + while(n--){ + if(*serialno_list == s) return 1; + serialno_list++; + } + } + return 0; +} + +static int _lookup_page_serialno(ogg_page *og, ogg_uint32_t *serialno_list, int n){ + ogg_uint32_t s = ogg_page_serialno(og); + return _lookup_serialno(s,serialno_list,n); +} + +/* performs the same search as _get_prev_page, but prefers pages of + the specified serial number. If a page of the specified serialno is + spotted during the seek-back-and-read-forward, it will return the + info of last page of the matching serial number instead of the very + last page. If no page of the specified serialno is seen, it will + return the info of last page and alter *serialno. */ +static ogg_int64_t _get_prev_page_serial(OggVorbis_File *vf, ogg_int64_t begin, + ogg_uint32_t *serial_list, int serial_n, + int *serialno, ogg_int64_t *granpos){ + ogg_page og; + ogg_int64_t end=begin; + ogg_int64_t ret; + + ogg_int64_t prefoffset=-1; + ogg_int64_t offset=-1; + ogg_int64_t ret_serialno=-1; + ogg_int64_t ret_gran=-1; + + while(offset==-1){ + begin-=CHUNKSIZE; + if(begin<0) + begin=0; + + ret=_seek_helper(vf,begin); + if(ret)return(ret); + + while(vf->offsetoffset); + if(ret==OV_EREAD)return(OV_EREAD); + if(ret<0){ + break; + }else{ + ret_serialno=ogg_page_serialno(&og); + ret_gran=ogg_page_granulepos(&og); + offset=ret; + + if((ogg_uint32_t)ret_serialno == *serialno){ + prefoffset=ret; + *granpos=ret_gran; + } + + if(!_lookup_serialno((ogg_uint32_t)ret_serialno,serial_list,serial_n)){ + /* we fell off the end of the link, which means we seeked + back too far and shouldn't have been looking in that link + to begin with. If we found the preferred serial number, + forget that we saw it. */ + prefoffset=-1; + } + } + } + } + + /* we're not interested in the page... just the serialno and granpos. */ + if(prefoffset>=0)return(prefoffset); + + *serialno = ret_serialno; + *granpos = ret_gran; + return(offset); + +} + +/* uses the local ogg_stream storage in vf; this is important for + non-streaming input sources */ +static int _fetch_headers(OggVorbis_File *vf,vorbis_info *vi,vorbis_comment *vc, + ogg_uint32_t **serialno_list, int *serialno_n, + ogg_page *og_ptr){ + ogg_page og; + ogg_packet op; + int i,ret; + int allbos=0; + + if(!og_ptr){ + ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE); + if(llret==OV_EREAD)return(OV_EREAD); + if(llret<0)return(OV_ENOTVORBIS); + og_ptr=&og; + } + + vorbis_info_init(vi); + vorbis_comment_init(vc); + vf->ready_state=OPENED; + + /* extract the serialnos of all BOS pages + the first set of vorbis + headers we see in the link */ + + while(ogg_page_bos(og_ptr)){ + if(serialno_list){ + if(_lookup_page_serialno(og_ptr,*serialno_list,*serialno_n)){ + /* a dupe serialnumber in an initial header packet set == invalid stream */ + if(*serialno_list)_ogg_free(*serialno_list); + *serialno_list=0; + *serialno_n=0; + ret=OV_EBADHEADER; + goto bail_header; + } + + _add_serialno(og_ptr,serialno_list,serialno_n); + } + + if(vf->ready_stateos,ogg_page_serialno(og_ptr)); + ogg_stream_pagein(&vf->os,og_ptr); + + if(ogg_stream_packetout(&vf->os,&op) > 0 && + vorbis_synthesis_idheader(&op)){ + /* vorbis header; continue setup */ + vf->ready_state=STREAMSET; + if((ret=vorbis_synthesis_headerin(vi,vc,&op))){ + ret=OV_EBADHEADER; + goto bail_header; + } + } + } + + /* get next page */ + { + ogg_int64_t llret=_get_next_page(vf,og_ptr,CHUNKSIZE); + if(llret==OV_EREAD){ + ret=OV_EREAD; + goto bail_header; + } + if(llret<0){ + ret=OV_ENOTVORBIS; + goto bail_header; + } + + /* if this page also belongs to our vorbis stream, submit it and break */ + if(vf->ready_state==STREAMSET && + vf->os.serialno == ogg_page_serialno(og_ptr)){ + ogg_stream_pagein(&vf->os,og_ptr); + break; + } + } + } + + if(vf->ready_state!=STREAMSET){ + ret = OV_ENOTVORBIS; + goto bail_header; + } + + while(1){ + + i=0; + while(i<2){ /* get a page loop */ + + while(i<2){ /* get a packet loop */ + + int result=ogg_stream_packetout(&vf->os,&op); + if(result==0)break; + if(result==-1){ + ret=OV_EBADHEADER; + goto bail_header; + } + + if((ret=vorbis_synthesis_headerin(vi,vc,&op))) + goto bail_header; + + i++; + } + + while(i<2){ + if(_get_next_page(vf,og_ptr,CHUNKSIZE)<0){ + ret=OV_EBADHEADER; + goto bail_header; + } + + /* if this page belongs to the correct stream, go parse it */ + if(vf->os.serialno == ogg_page_serialno(og_ptr)){ + ogg_stream_pagein(&vf->os,og_ptr); + break; + } + + /* if we never see the final vorbis headers before the link + ends, abort */ + if(ogg_page_bos(og_ptr)){ + if(allbos){ + ret = OV_EBADHEADER; + goto bail_header; + }else + allbos=1; + } + + /* otherwise, keep looking */ + } + } + + return 0; + } + + bail_header: + vorbis_info_clear(vi); + vorbis_comment_clear(vc); + vf->ready_state=OPENED; + + return ret; +} + +/* Starting from current cursor position, get initial PCM offset of + next page. Consumes the page in the process without decoding + audio, however this is only called during stream parsing upon + seekable open. */ +static ogg_int64_t _initial_pcmoffset(OggVorbis_File *vf, vorbis_info *vi){ + ogg_page og; + ogg_int64_t accumulated=0; + long lastblock=-1; + int result; + int serialno = vf->os.serialno; + + while(1){ + ogg_packet op; + if(_get_next_page(vf,&og,-1)<0) + break; /* should not be possible unless the file is truncated/mangled */ + + if(ogg_page_bos(&og)) break; + if(ogg_page_serialno(&og)!=serialno) continue; + + /* count blocksizes of all frames in the page */ + ogg_stream_pagein(&vf->os,&og); + while((result=ogg_stream_packetout(&vf->os,&op))){ + if(result>0){ /* ignore holes */ + long thisblock=vorbis_packet_blocksize(vi,&op); + if(lastblock!=-1) + accumulated+=(lastblock+thisblock)>>2; + lastblock=thisblock; + } + } + + if(ogg_page_granulepos(&og)!=-1){ + /* pcm offset of last packet on the first audio page */ + accumulated= ogg_page_granulepos(&og)-accumulated; + break; + } + } + + /* less than zero? This is a stream with samples trimmed off + the beginning, a normal occurrence; set the offset to zero */ + if(accumulated<0)accumulated=0; + + return accumulated; +} + +/* finds each bitstream link one at a time using a bisection search + (has to begin by knowing the offset of the lb's initial page). + Recurses for each link so it can alloc the link storage after + finding them all, then unroll and fill the cache at the same time */ +static int _bisect_forward_serialno(OggVorbis_File *vf, + ogg_int64_t begin, + ogg_int64_t searched, + ogg_int64_t end, + ogg_int64_t endgran, + int endserial, + ogg_uint32_t *currentno_list, + int currentnos, + long m){ + ogg_int64_t pcmoffset; + ogg_int64_t dataoffset=searched; + ogg_int64_t endsearched=end; + ogg_int64_t next=end; + ogg_int64_t searchgran=-1; + ogg_page og; + ogg_int64_t ret,last; + int serialno = vf->os.serialno; + + /* invariants: + we have the headers and serialnos for the link beginning at 'begin' + we have the offset and granpos of the last page in the file (potentially + not a page we care about) + */ + + /* Is the last page in our list of current serialnumbers? */ + if(_lookup_serialno(endserial,currentno_list,currentnos)){ + + /* last page is in the starting serialno list, so we've bisected + down to (or just started with) a single link. Now we need to + find the last vorbis page belonging to the first vorbis stream + for this link. */ + searched = end; + while(endserial != serialno){ + endserial = serialno; + searched=_get_prev_page_serial(vf,searched,currentno_list,currentnos,&endserial,&endgran); + } + + vf->links=m+1; + if(vf->offsets)_ogg_free(vf->offsets); + if(vf->serialnos)_ogg_free(vf->serialnos); + if(vf->dataoffsets)_ogg_free(vf->dataoffsets); + + vf->offsets=_ogg_malloc((vf->links+1)*sizeof(*vf->offsets)); + vf->vi=_ogg_realloc(vf->vi,vf->links*sizeof(*vf->vi)); + vf->vc=_ogg_realloc(vf->vc,vf->links*sizeof(*vf->vc)); + vf->serialnos=_ogg_malloc(vf->links*sizeof(*vf->serialnos)); + vf->dataoffsets=_ogg_malloc(vf->links*sizeof(*vf->dataoffsets)); + vf->pcmlengths=_ogg_malloc(vf->links*2*sizeof(*vf->pcmlengths)); + + vf->offsets[m+1]=end; + vf->offsets[m]=begin; + vf->pcmlengths[m*2+1]=(endgran<0?0:endgran); + + }else{ + + /* last page is not in the starting stream's serial number list, + so we have multiple links. Find where the stream that begins + our bisection ends. */ + + ogg_uint32_t *next_serialno_list=NULL; + int next_serialnos=0; + vorbis_info vi; + vorbis_comment vc; + int testserial = serialno+1; + + /* the below guards against garbage seperating the last and + first pages of two links. */ + while(searched=0)next=last; + }else{ + searched=vf->offset; + } + } + + /* Bisection point found */ + /* for the time being, fetch end PCM offset the simple way */ + searched = next; + while(testserial != serialno){ + testserial = serialno; + searched = _get_prev_page_serial(vf,searched,currentno_list,currentnos,&testserial,&searchgran); + } + + ret=_seek_helper(vf,next); + if(ret)return(ret); + + ret=_fetch_headers(vf,&vi,&vc,&next_serialno_list,&next_serialnos,NULL); + if(ret)return(ret); + serialno = vf->os.serialno; + dataoffset = vf->offset; + + /* this will consume a page, however the next bisection always + starts with a raw seek */ + pcmoffset = _initial_pcmoffset(vf,&vi); + + ret=_bisect_forward_serialno(vf,next,vf->offset,end,endgran,endserial, + next_serialno_list,next_serialnos,m+1); + if(ret)return(ret); + + if(next_serialno_list)_ogg_free(next_serialno_list); + + vf->offsets[m+1]=next; + vf->serialnos[m+1]=serialno; + vf->dataoffsets[m+1]=dataoffset; + + vf->vi[m+1]=vi; + vf->vc[m+1]=vc; + + vf->pcmlengths[m*2+1]=searchgran; + vf->pcmlengths[m*2+2]=pcmoffset; + vf->pcmlengths[m*2+3]-=pcmoffset; + if(vf->pcmlengths[m*2+3]<0)vf->pcmlengths[m*2+3]=0; + + } + return(0); +} + +static int _make_decode_ready(OggVorbis_File *vf){ + if(vf->ready_state>STREAMSET)return 0; + if(vf->ready_stateseekable){ + if(vorbis_synthesis_init(&vf->vd,vf->vi+vf->current_link)) + return OV_EBADLINK; + }else{ + if(vorbis_synthesis_init(&vf->vd,vf->vi)) + return OV_EBADLINK; + } + vorbis_block_init(&vf->vd,&vf->vb); + vf->ready_state=INITSET; + vf->bittrack=0; + vf->samptrack=0; + return 0; +} + +static int _open_seekable2(OggVorbis_File *vf){ + ogg_int64_t dataoffset=vf->dataoffsets[0],end,endgran=-1; + int endserial=vf->os.serialno; + int serialno=vf->os.serialno; + + /* we're partially open and have a first link header state in + storage in vf */ + + /* fetch initial PCM offset */ + ogg_int64_t pcmoffset = _initial_pcmoffset(vf,vf->vi); + + /* we can seek, so set out learning all about this file */ + if(vf->callbacks.seek_func && vf->callbacks.tell_func){ + (vf->callbacks.seek_func)(vf->datasource,0,SEEK_END); + vf->offset=vf->end=(vf->callbacks.tell_func)(vf->datasource); + }else{ + vf->offset=vf->end=-1; + } + + /* If seek_func is implemented, tell_func must also be implemented */ + if(vf->end==-1) return(OV_EINVAL); + + /* Get the offset of the last page of the physical bitstream, or, if + we're lucky the last vorbis page of this link as most OggVorbis + files will contain a single logical bitstream */ + end=_get_prev_page_serial(vf,vf->end,vf->serialnos+2,vf->serialnos[1],&endserial,&endgran); + if(end<0)return(end); + + /* now determine bitstream structure recursively */ + if(_bisect_forward_serialno(vf,0,dataoffset,end,endgran,endserial, + vf->serialnos+2,vf->serialnos[1],0)<0)return(OV_EREAD); + + vf->offsets[0]=0; + vf->serialnos[0]=serialno; + vf->dataoffsets[0]=dataoffset; + vf->pcmlengths[0]=pcmoffset; + vf->pcmlengths[1]-=pcmoffset; + if(vf->pcmlengths[1]<0)vf->pcmlengths[1]=0; + + return(ov_raw_seek(vf,dataoffset)); +} + +/* clear out the current logical bitstream decoder */ +static void _decode_clear(OggVorbis_File *vf){ + vorbis_dsp_clear(&vf->vd); + vorbis_block_clear(&vf->vb); + vf->ready_state=OPENED; +} + +/* fetch and process a packet. Handles the case where we're at a + bitstream boundary and dumps the decoding machine. If the decoding + machine is unloaded, it loads it. It also keeps pcm_offset up to + date (seek and read both use this. seek uses a special hack with + readp). + + return: <0) error, OV_HOLE (lost packet) or OV_EOF + 0) need more data (only if readp==0) + 1) got a packet +*/ + +static int _fetch_and_process_packet(OggVorbis_File *vf, + ogg_packet *op_in, + int readp, + int spanp){ + ogg_page og; + + /* handle one packet. Try to fetch it from current stream state */ + /* extract packets from page */ + while(1){ + + if(vf->ready_state==STREAMSET){ + int ret=_make_decode_ready(vf); + if(ret<0)return ret; + } + + /* process a packet if we can. If the machine isn't loaded, + neither is a page */ + if(vf->ready_state==INITSET){ + while(1) { + ogg_packet op; + ogg_packet *op_ptr=(op_in?op_in:&op); + int result=ogg_stream_packetout(&vf->os,op_ptr); + ogg_int64_t granulepos; + + op_in=NULL; + if(result==-1)return(OV_HOLE); /* hole in the data. */ + if(result>0){ + /* got a packet. process it */ + granulepos=op_ptr->granulepos; + if(!vorbis_synthesis(&vf->vb,op_ptr)){ /* lazy check for lazy + header handling. The + header packets aren't + audio, so if/when we + submit them, + vorbis_synthesis will + reject them */ + + /* suck in the synthesis data and track bitrate */ + { + int oldsamples=vorbis_synthesis_pcmout(&vf->vd,NULL); + /* for proper use of libvorbis within libvorbisfile, + oldsamples will always be zero. */ + if(oldsamples)return(OV_EFAULT); + + vorbis_synthesis_blockin(&vf->vd,&vf->vb); + vf->samptrack+=vorbis_synthesis_pcmout(&vf->vd,NULL); + vf->bittrack+=op_ptr->bytes*8; + } + + /* update the pcm offset. */ + if(granulepos!=-1 && !op_ptr->e_o_s){ + int link=(vf->seekable?vf->current_link:0); + int i,samples; + + /* this packet has a pcm_offset on it (the last packet + completed on a page carries the offset) After processing + (above), we know the pcm position of the *last* sample + ready to be returned. Find the offset of the *first* + + As an aside, this trick is inaccurate if we begin + reading anew right at the last page; the end-of-stream + granulepos declares the last frame in the stream, and the + last packet of the last page may be a partial frame. + So, we need a previous granulepos from an in-sequence page + to have a reference point. Thus the !op_ptr->e_o_s clause + above */ + + if(vf->seekable && link>0) + granulepos-=vf->pcmlengths[link*2]; + if(granulepos<0)granulepos=0; /* actually, this + shouldn't be possible + here unless the stream + is very broken */ + + samples=vorbis_synthesis_pcmout(&vf->vd,NULL); + + granulepos-=samples; + for(i=0;ipcmlengths[i*2+1]; + vf->pcm_offset=granulepos; + } + return(1); + } + } + else + break; + } + } + + if(vf->ready_state>=OPENED){ + ogg_int64_t ret; + + while(1){ + /* the loop is not strictly necessary, but there's no sense in + doing the extra checks of the larger loop for the common + case in a multiplexed bistream where the page is simply + part of a different logical bitstream; keep reading until + we get one with the correct serialno */ + + if(!readp)return(0); + if((ret=_get_next_page(vf,&og,-1))<0){ + return(OV_EOF); /* eof. leave unitialized */ + } + + /* bitrate tracking; add the header's bytes here, the body bytes + are done by packet above */ + vf->bittrack+=og.header_len*8; + + if(vf->ready_state==INITSET){ + if(vf->current_serialno!=ogg_page_serialno(&og)){ + + /* two possibilities: + 1) our decoding just traversed a bitstream boundary + 2) another stream is multiplexed into this logical section */ + + if(ogg_page_bos(&og)){ + /* boundary case */ + if(!spanp) + return(OV_EOF); + + _decode_clear(vf); + + if(!vf->seekable){ + vorbis_info_clear(vf->vi); + vorbis_comment_clear(vf->vc); + } + break; + + }else + continue; /* possibility #2 */ + } + } + + break; + } + } + + /* Do we need to load a new machine before submitting the page? */ + /* This is different in the seekable and non-seekable cases. + + In the seekable case, we already have all the header + information loaded and cached; we just initialize the machine + with it and continue on our merry way. + + In the non-seekable (streaming) case, we'll only be at a + boundary if we just left the previous logical bitstream and + we're now nominally at the header of the next bitstream + */ + + if(vf->ready_state!=INITSET){ + int link; + + if(vf->ready_stateseekable){ + ogg_uint32_t serialno = ogg_page_serialno(&og); + + /* match the serialno to bitstream section. We use this rather than + offset positions to avoid problems near logical bitstream + boundaries */ + + for(link=0;linklinks;link++) + if(vf->serialnos[link]==serialno)break; + + if(link==vf->links) continue; /* not the desired Vorbis + bitstream section; keep + trying */ + + vf->current_serialno=serialno; + vf->current_link=link; + + ogg_stream_reset_serialno(&vf->os,vf->current_serialno); + vf->ready_state=STREAMSET; + + }else{ + /* we're streaming */ + /* fetch the three header packets, build the info struct */ + + int ret=_fetch_headers(vf,vf->vi,vf->vc,NULL,NULL,&og); + if(ret)return(ret); + vf->current_serialno=vf->os.serialno; + vf->current_link++; + link=0; + } + } + } + + /* the buffered page is the data we want, and we're ready for it; + add it to the stream state */ + ogg_stream_pagein(&vf->os,&og); + + } +} + +/* if, eg, 64 bit stdio is configured by default, this will build with + fseek64 */ +static int _fseek64_wrap(FILE *f,ogg_int64_t off,int whence){ + if(f==NULL)return(-1); + return fseek(f,off,whence); +} + +static int _ov_open1(void *f,OggVorbis_File *vf,const char *initial, + long ibytes, ov_callbacks callbacks){ + int offsettest=((f && callbacks.seek_func)?callbacks.seek_func(f,0,SEEK_CUR):-1); + ogg_uint32_t *serialno_list=NULL; + int serialno_list_size=0; + int ret; + + memset(vf,0,sizeof(*vf)); + vf->datasource=f; + vf->callbacks = callbacks; + + /* init the framing state */ + ogg_sync_init(&vf->oy); + + /* perhaps some data was previously read into a buffer for testing + against other stream types. Allow initialization from this + previously read data (especially as we may be reading from a + non-seekable stream) */ + if(initial){ + char *buffer=ogg_sync_buffer(&vf->oy,ibytes); + memcpy(buffer,initial,ibytes); + ogg_sync_wrote(&vf->oy,ibytes); + } + + /* can we seek? Stevens suggests the seek test was portable */ + if(offsettest!=-1)vf->seekable=1; + + /* No seeking yet; Set up a 'single' (current) logical bitstream + entry for partial open */ + vf->links=1; + vf->vi=_ogg_calloc(vf->links,sizeof(*vf->vi)); + vf->vc=_ogg_calloc(vf->links,sizeof(*vf->vc)); + ogg_stream_init(&vf->os,-1); /* fill in the serialno later */ + + /* Fetch all BOS pages, store the vorbis header and all seen serial + numbers, load subsequent vorbis setup headers */ + if((ret=_fetch_headers(vf,vf->vi,vf->vc,&serialno_list,&serialno_list_size,NULL))<0){ + vf->datasource=NULL; + ov_clear(vf); + }else{ + /* serial number list for first link needs to be held somewhere + for second stage of seekable stream open; this saves having to + seek/reread first link's serialnumber data then. */ + vf->serialnos=_ogg_calloc(serialno_list_size+2,sizeof(*vf->serialnos)); + vf->serialnos[0]=vf->current_serialno=vf->os.serialno; + vf->serialnos[1]=serialno_list_size; + memcpy(vf->serialnos+2,serialno_list,serialno_list_size*sizeof(*vf->serialnos)); + + vf->offsets=_ogg_calloc(1,sizeof(*vf->offsets)); + vf->dataoffsets=_ogg_calloc(1,sizeof(*vf->dataoffsets)); + vf->offsets[0]=0; + vf->dataoffsets[0]=vf->offset; + + vf->ready_state=PARTOPEN; + } + if(serialno_list)_ogg_free(serialno_list); + return(ret); +} + +static int _ov_open2(OggVorbis_File *vf){ + if(vf->ready_state != PARTOPEN) return OV_EINVAL; + vf->ready_state=OPENED; + if(vf->seekable){ + int ret=_open_seekable2(vf); + if(ret){ + vf->datasource=NULL; + ov_clear(vf); + } + return(ret); + }else + vf->ready_state=STREAMSET; + + return 0; +} + + +/* clear out the OggVorbis_File struct */ +int ov_clear(OggVorbis_File *vf){ + if(vf){ + vorbis_block_clear(&vf->vb); + vorbis_dsp_clear(&vf->vd); + ogg_stream_clear(&vf->os); + + if(vf->vi && vf->links){ + int i; + for(i=0;ilinks;i++){ + vorbis_info_clear(vf->vi+i); + vorbis_comment_clear(vf->vc+i); + } + _ogg_free(vf->vi); + _ogg_free(vf->vc); + } + if(vf->dataoffsets)_ogg_free(vf->dataoffsets); + if(vf->pcmlengths)_ogg_free(vf->pcmlengths); + if(vf->serialnos)_ogg_free(vf->serialnos); + if(vf->offsets)_ogg_free(vf->offsets); + ogg_sync_clear(&vf->oy); + if(vf->datasource && vf->callbacks.close_func) + (vf->callbacks.close_func)(vf->datasource); + memset(vf,0,sizeof(*vf)); + } +#ifdef DEBUG_LEAKS + _VDBG_dump(); +#endif + return(0); +} + +/* inspects the OggVorbis file and finds/documents all the logical + bitstreams contained in it. Tries to be tolerant of logical + bitstream sections that are truncated/woogie. + + return: -1) error + 0) OK +*/ + +int ov_open_callbacks(void *f,OggVorbis_File *vf, + const char *initial,long ibytes,ov_callbacks callbacks){ + int ret=_ov_open1(f,vf,initial,ibytes,callbacks); + if(ret)return ret; + return _ov_open2(vf); +} + +int ov_open(FILE *f,OggVorbis_File *vf,const char *initial,long ibytes){ + ov_callbacks callbacks = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) _fseek64_wrap, + (int (*)(void *)) fclose, + (long (*)(void *)) ftell + }; + + return ov_open_callbacks((void *)f, vf, initial, ibytes, callbacks); +} + +int ov_fopen(const char *path,OggVorbis_File *vf){ + int ret; + FILE *f = fopen(path,"rb"); + if(!f) return -1; + + ret = ov_open(f,vf,NULL,0); + if(ret) fclose(f); + return ret; +} + + +/* Only partially open the vorbis file; test for Vorbisness, and load + the headers for the first chain. Do not seek (although test for + seekability). Use ov_test_open to finish opening the file, else + ov_clear to close/free it. Same return codes as open. */ + +int ov_test_callbacks(void *f,OggVorbis_File *vf, + const char *initial,long ibytes,ov_callbacks callbacks) +{ + return _ov_open1(f,vf,initial,ibytes,callbacks); +} + +int ov_test(FILE *f,OggVorbis_File *vf,const char *initial,long ibytes){ + ov_callbacks callbacks = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) _fseek64_wrap, + (int (*)(void *)) fclose, + (long (*)(void *)) ftell + }; + + return ov_test_callbacks((void *)f, vf, initial, ibytes, callbacks); +} + +int ov_test_open(OggVorbis_File *vf){ + if(vf->ready_state!=PARTOPEN)return(OV_EINVAL); + return _ov_open2(vf); +} + +/* How many logical bitstreams in this physical bitstream? */ +long ov_streams(OggVorbis_File *vf){ + return vf->links; +} + +/* Is the FILE * associated with vf seekable? */ +long ov_seekable(OggVorbis_File *vf){ + return vf->seekable; +} + +/* returns the bitrate for a given logical bitstream or the entire + physical bitstream. If the file is open for random access, it will + find the *actual* average bitrate. If the file is streaming, it + returns the nominal bitrate (if set) else the average of the + upper/lower bounds (if set) else -1 (unset). + + If you want the actual bitrate field settings, get them from the + vorbis_info structs */ + +long ov_bitrate(OggVorbis_File *vf,int i){ + if(vf->ready_state=vf->links)return(OV_EINVAL); + if(!vf->seekable && i!=0)return(ov_bitrate(vf,0)); + if(i<0){ + ogg_int64_t bits=0; + int i; + for(i=0;ilinks;i++) + bits+=(vf->offsets[i+1]-vf->dataoffsets[i])*8; + /* This once read: return(rint(bits/ov_time_total(vf,-1))); + * gcc 3.x on x86 miscompiled this at optimisation level 2 and above, + * so this is slightly transformed to make it work. + */ + return(bits*1000/ov_time_total(vf,-1)); + }else{ + if(vf->seekable){ + /* return the actual bitrate */ + return((vf->offsets[i+1]-vf->dataoffsets[i])*8000/ov_time_total(vf,i)); + }else{ + /* return nominal if set */ + if(vf->vi[i].bitrate_nominal>0){ + return vf->vi[i].bitrate_nominal; + }else{ + if(vf->vi[i].bitrate_upper>0){ + if(vf->vi[i].bitrate_lower>0){ + return (vf->vi[i].bitrate_upper+vf->vi[i].bitrate_lower)/2; + }else{ + return vf->vi[i].bitrate_upper; + } + } + return(OV_FALSE); + } + } + } +} + +/* returns the actual bitrate since last call. returns -1 if no + additional data to offer since last call (or at beginning of stream), + EINVAL if stream is only partially open +*/ +long ov_bitrate_instant(OggVorbis_File *vf){ + int link=(vf->seekable?vf->current_link:0); + long ret; + if(vf->ready_statesamptrack==0)return(OV_FALSE); + ret=vf->bittrack/vf->samptrack*vf->vi[link].rate; + vf->bittrack=0; + vf->samptrack=0; + return(ret); +} + +/* Guess */ +long ov_serialnumber(OggVorbis_File *vf,int i){ + if(i>=vf->links)return(ov_serialnumber(vf,vf->links-1)); + if(!vf->seekable && i>=0)return(ov_serialnumber(vf,-1)); + if(i<0){ + return(vf->current_serialno); + }else{ + return(vf->serialnos[i]); + } +} + +/* returns: total raw (compressed) length of content if i==-1 + raw (compressed) length of that logical bitstream for i==0 to n + OV_EINVAL if the stream is not seekable (we can't know the length) + or if stream is only partially open +*/ +ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i){ + if(vf->ready_stateseekable || i>=vf->links)return(OV_EINVAL); + if(i<0){ + ogg_int64_t acc=0; + int i; + for(i=0;ilinks;i++) + acc+=ov_raw_total(vf,i); + return(acc); + }else{ + return(vf->offsets[i+1]-vf->offsets[i]); + } +} + +/* returns: total PCM length (samples) of content if i==-1 PCM length + (samples) of that logical bitstream for i==0 to n + OV_EINVAL if the stream is not seekable (we can't know the + length) or only partially open +*/ +ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i){ + if(vf->ready_stateseekable || i>=vf->links)return(OV_EINVAL); + if(i<0){ + ogg_int64_t acc=0; + int i; + for(i=0;ilinks;i++) + acc+=ov_pcm_total(vf,i); + return(acc); + }else{ + return(vf->pcmlengths[i*2+1]); + } +} + +/* returns: total milliseconds of content if i==-1 + milliseconds in that logical bitstream for i==0 to n + OV_EINVAL if the stream is not seekable (we can't know the + length) or only partially open +*/ +ogg_int64_t ov_time_total(OggVorbis_File *vf,int i){ + if(vf->ready_stateseekable || i>=vf->links)return(OV_EINVAL); + if(i<0){ + ogg_int64_t acc=0; + int i; + for(i=0;ilinks;i++) + acc+=ov_time_total(vf,i); + return(acc); + }else{ + return(((ogg_int64_t)vf->pcmlengths[i*2+1])*1000/vf->vi[i].rate); + } +} + +/* seek to an offset relative to the *compressed* data. This also + scans packets to update the PCM cursor. It will cross a logical + bitstream boundary, but only if it can't get any packets out of the + tail of the bitstream we seek to (so no surprises). + + returns zero on success, nonzero on failure */ + +int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ + ogg_stream_state work_os; + int ret; + + if(vf->ready_stateseekable) + return(OV_ENOSEEK); /* don't dump machine if we can't seek */ + + if(pos<0 || pos>vf->end)return(OV_EINVAL); + + /* is the seek position outside our current link [if any]? */ + if(vf->ready_state>=STREAMSET){ + if(posoffsets[vf->current_link] || pos>=vf->offsets[vf->current_link+1]) + _decode_clear(vf); /* clear out stream state */ + } + + /* don't yet clear out decoding machine (if it's initialized), in + the case we're in the same link. Restart the decode lapping, and + let _fetch_and_process_packet deal with a potential bitstream + boundary */ + vf->pcm_offset=-1; + ogg_stream_reset_serialno(&vf->os, + vf->current_serialno); /* must set serialno */ + vorbis_synthesis_restart(&vf->vd); + + ret=_seek_helper(vf,pos); + if(ret)goto seek_error; + + /* we need to make sure the pcm_offset is set, but we don't want to + advance the raw cursor past good packets just to get to the first + with a granulepos. That's not equivalent behavior to beginning + decoding as immediately after the seek position as possible. + + So, a hack. We use two stream states; a local scratch state and + the shared vf->os stream state. We use the local state to + scan, and the shared state as a buffer for later decode. + + Unfortuantely, on the last page we still advance to last packet + because the granulepos on the last page is not necessarily on a + packet boundary, and we need to make sure the granpos is + correct. + */ + + { + ogg_page og; + ogg_packet op; + int lastblock=0; + int accblock=0; + int thisblock=0; + int lastflag=0; + int firstflag=0; + ogg_int64_t pagepos=-1; + + ogg_stream_init(&work_os,vf->current_serialno); /* get the memory ready */ + ogg_stream_reset(&work_os); /* eliminate the spurious OV_HOLE + return from not necessarily + starting from the beginning */ + + while(1){ + if(vf->ready_state>=STREAMSET){ + /* snarf/scan a packet if we can */ + int result=ogg_stream_packetout(&work_os,&op); + + if(result>0){ + + if(vf->vi[vf->current_link].codec_setup){ + thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op); + if(thisblock<0){ + ogg_stream_packetout(&vf->os,NULL); + thisblock=0; + }else{ + + /* We can't get a guaranteed correct pcm position out of the + last page in a stream because it might have a 'short' + granpos, which can only be detected in the presence of a + preceding page. However, if the last page is also the first + page, the granpos rules of a first page take precedence. Not + only that, but for first==last, the EOS page must be treated + as if its a normal first page for the stream to open/play. */ + if(lastflag && !firstflag) + ogg_stream_packetout(&vf->os,NULL); + else + if(lastblock)accblock+=(lastblock+thisblock)>>2; + } + + if(op.granulepos!=-1){ + int i,link=vf->current_link; + ogg_int64_t granulepos=op.granulepos-vf->pcmlengths[link*2]; + if(granulepos<0)granulepos=0; + + for(i=0;ipcmlengths[i*2+1]; + vf->pcm_offset=granulepos-accblock; + if(vf->pcm_offset<0)vf->pcm_offset=0; + break; + } + lastblock=thisblock; + continue; + }else + ogg_stream_packetout(&vf->os,NULL); + } + } + + if(!lastblock){ + pagepos=_get_next_page(vf,&og,-1); + if(pagepos<0){ + vf->pcm_offset=ov_pcm_total(vf,-1); + break; + } + }else{ + /* huh? Bogus stream with packets but no granulepos */ + vf->pcm_offset=-1; + break; + } + + /* has our decoding just traversed a bitstream boundary? */ + if(vf->ready_state>=STREAMSET){ + if(vf->current_serialno!=ogg_page_serialno(&og)){ + + /* two possibilities: + 1) our decoding just traversed a bitstream boundary + 2) another stream is multiplexed into this logical section? */ + + if(ogg_page_bos(&og)){ + /* we traversed */ + _decode_clear(vf); /* clear out stream state */ + ogg_stream_clear(&work_os); + } /* else, do nothing; next loop will scoop another page */ + } + } + + if(vf->ready_statelinks;link++) + if(vf->serialnos[link]==serialno)break; + + if(link==vf->links) continue; /* not the desired Vorbis + bitstream section; keep + trying */ + vf->current_link=link; + vf->current_serialno=serialno; + ogg_stream_reset_serialno(&vf->os,serialno); + ogg_stream_reset_serialno(&work_os,serialno); + vf->ready_state=STREAMSET; + firstflag=(pagepos<=vf->dataoffsets[link]); + } + + ogg_stream_pagein(&vf->os,&og); + ogg_stream_pagein(&work_os,&og); + lastflag=ogg_page_eos(&og); + + } + } + + ogg_stream_clear(&work_os); + vf->bittrack=0; + vf->samptrack=0; + return(0); + + seek_error: + /* dump the machine so we're in a known state */ + vf->pcm_offset=-1; + ogg_stream_clear(&work_os); + _decode_clear(vf); + return OV_EBADLINK; +} + +/* rescales the number x from the range of [0,from] to [0,to] + x is in the range [0,from] + from, to are in the range [1, 1<<62-1] */ +ogg_int64_t rescale64(ogg_int64_t x, ogg_int64_t from, ogg_int64_t to){ + ogg_int64_t frac=0; + ogg_int64_t ret=0; + int i; + if(x >= from) return to; + if(x <= 0) return 0; + + for(i=0;i<64;i++){ + if(x>=from){ + frac|=1; + x-=from; + } + x<<=1; + frac<<=1; + } + + for(i=0;i<64;i++){ + if(frac & 1){ + ret+=to; + } + frac>>=1; + ret>>=1; + } + + return ret; +} + +/* Page granularity seek (faster than sample granularity because we + don't do the last bit of decode to find a specific sample). + + Seek to the last [granule marked] page preceding the specified pos + location, such that decoding past the returned point will quickly + arrive at the requested position. */ +int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ + int link=-1; + ogg_int64_t result=0; + ogg_int64_t total=ov_pcm_total(vf,-1); + + if(vf->ready_stateseekable)return(OV_ENOSEEK); + + if(pos<0 || pos>total)return(OV_EINVAL); + + /* which bitstream section does this pcm offset occur in? */ + for(link=vf->links-1;link>=0;link--){ + total-=vf->pcmlengths[link*2+1]; + if(pos>=total)break; + } + + /* Search within the logical bitstream for the page with the highest + pcm_pos preceding pos. If we're looking for a position on the + first page, bisection will halt without finding our position as + it's before the first explicit granulepos fencepost. That case is + handled separately below. + + There is a danger here; missing pages or incorrect frame number + information in the bitstream could make our task impossible. + Account for that (it would be an error condition) */ + + /* new search algorithm originally by HB (Nicholas Vinen) */ + + { + ogg_int64_t end=vf->offsets[link+1]; + ogg_int64_t begin=vf->dataoffsets[link]; + ogg_int64_t begintime = vf->pcmlengths[link*2]; + ogg_int64_t endtime = vf->pcmlengths[link*2+1]+begintime; + ogg_int64_t target=pos-total+begintime; + ogg_int64_t best=-1; + int got_page=0; + + ogg_page og; + + /* if we have only one page, there will be no bisection. Grab the page here */ + if(begin==end){ + result=_seek_helper(vf,begin); + if(result) goto seek_error; + + result=_get_next_page(vf,&og,1); + if(result<0) goto seek_error; + + got_page=1; + } + + /* bisection loop */ + while(beginoffset); + if(result==OV_EREAD) goto seek_error; + if(result<0){ + /* there is no next page! */ + if(bisect<=begin+1) + /* No bisection left to perform. We've either found the + best candidate already or failed. Exit loop. */ + end=begin; + else{ + /* We tried to load a fraction of the last page; back up a + bit and try to get the whole last page */ + if(bisect==0) goto seek_error; + bisect-=CHUNKSIZE; + + /* don't repeat/loop on a read we've already performed */ + if(bisect<=begin)bisect=begin+1; + + /* seek and continue bisection */ + result=_seek_helper(vf,bisect); + if(result) goto seek_error; + } + }else{ + ogg_int64_t granulepos; + got_page=1; + + /* got a page. analyze it */ + /* only consider pages from primary vorbis stream */ + if(ogg_page_serialno(&og)!=vf->serialnos[link]) + continue; + + /* only consider pages with the granulepos set */ + granulepos=ogg_page_granulepos(&og); + if(granulepos==-1)continue; + + if(granuleposoffset; /* raw offset of next page */ + begintime=granulepos; + + /* if we're before our target but within a short distance, + don't bisect; read forward */ + if(target-begintime>44100)break; + + bisect=begin; /* *not* begin + 1 as above */ + }else{ + + /* This is one of our pages, but the granpos is + post-target; it is not a bisection return + candidate. (The only way we'd use it is if it's the + first page in the stream; we handle that case later + outside the bisection) */ + if(bisect<=begin+1){ + /* No bisection left to perform. We've either found the + best candidate already or failed. Exit loop. */ + end=begin; + }else{ + if(end==vf->offset){ + /* bisection read to the end; use the known page + boundary (result) to update bisection, back up a + little bit, and try again */ + end=result; + bisect-=CHUNKSIZE; + if(bisect<=begin)bisect=begin+1; + result=_seek_helper(vf,bisect); + if(result) goto seek_error; + }else{ + /* Normal bisection */ + end=bisect; + endtime=granulepos; + break; + } + } + } + } + } + } + + /* Out of bisection: did it 'fail?' */ + if(best == -1){ + + /* Check the 'looking for data in first page' special case; + bisection would 'fail' because our search target was before the + first PCM granule position fencepost. */ + + if(got_page && + begin == vf->dataoffsets[link] && + ogg_page_serialno(&og)==vf->serialnos[link]){ + + /* Yes, this is the beginning-of-stream case. We already have + our page, right at the beginning of PCM data. Set state + and return. */ + + vf->pcm_offset=total; + + if(link!=vf->current_link){ + /* Different link; dump entire decode machine */ + _decode_clear(vf); + + vf->current_link=link; + vf->current_serialno=vf->serialnos[link]; + vf->ready_state=STREAMSET; + + }else{ + vorbis_synthesis_restart(&vf->vd); + } + + ogg_stream_reset_serialno(&vf->os,vf->current_serialno); + ogg_stream_pagein(&vf->os,&og); + + }else + goto seek_error; + + }else{ + + /* Bisection found our page. seek to it, update pcm offset. Easier case than + raw_seek, don't keep packets preceding granulepos. */ + + ogg_page og; + ogg_packet op; + + /* seek */ + result=_seek_helper(vf,best); + vf->pcm_offset=-1; + if(result) goto seek_error; + result=_get_next_page(vf,&og,-1); + if(result<0) goto seek_error; + + if(link!=vf->current_link){ + /* Different link; dump entire decode machine */ + _decode_clear(vf); + + vf->current_link=link; + vf->current_serialno=vf->serialnos[link]; + vf->ready_state=STREAMSET; + + }else{ + vorbis_synthesis_restart(&vf->vd); + } + + ogg_stream_reset_serialno(&vf->os,vf->current_serialno); + ogg_stream_pagein(&vf->os,&og); + + /* pull out all but last packet; the one with granulepos */ + while(1){ + result=ogg_stream_packetpeek(&vf->os,&op); + if(result==0){ + /* No packet returned; we exited the bisection with 'best' + pointing to a page with a granule position, so the packet + finishing this page ('best') originated on a preceding + page. Keep fetching previous pages until we get one with + a granulepos or without the 'continued' flag set. Then + just use raw_seek for simplicity. */ + /* Do not rewind past the beginning of link data; if we do, + it's either a bug or a broken stream */ + result=best; + while(result>vf->dataoffsets[link]){ + result=_get_prev_page(vf,result,&og); + if(result<0) goto seek_error; + if(ogg_page_serialno(&og)==vf->current_serialno && + (ogg_page_granulepos(&og)>-1 || + !ogg_page_continued(&og))){ + return ov_raw_seek(vf,result); + } + } + } + if(result<0){ + result = OV_EBADPACKET; + goto seek_error; + } + if(op.granulepos!=-1){ + vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2]; + if(vf->pcm_offset<0)vf->pcm_offset=0; + vf->pcm_offset+=total; + break; + }else + result=ogg_stream_packetout(&vf->os,NULL); + } + } + } + + /* verify result */ + if(vf->pcm_offset>pos || pos>ov_pcm_total(vf,-1)){ + result=OV_EFAULT; + goto seek_error; + } + vf->bittrack=0; + vf->samptrack=0; + return(0); + + seek_error: + /* dump machine so we're in a known state */ + vf->pcm_offset=-1; + _decode_clear(vf); + return (int)result; +} + +/* seek to a sample offset relative to the decompressed pcm stream + returns zero on success, nonzero on failure */ + +int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){ + int thisblock,lastblock=0; + int ret=ov_pcm_seek_page(vf,pos); + if(ret<0)return(ret); + if((ret=_make_decode_ready(vf)))return ret; + + /* discard leading packets we don't need for the lapping of the + position we want; don't decode them */ + + while(1){ + ogg_packet op; + ogg_page og; + + int ret=ogg_stream_packetpeek(&vf->os,&op); + if(ret>0){ + thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op); + if(thisblock<0){ + ogg_stream_packetout(&vf->os,NULL); + continue; /* non audio packet */ + } + if(lastblock)vf->pcm_offset+=(lastblock+thisblock)>>2; + + if(vf->pcm_offset+((thisblock+ + vorbis_info_blocksize(vf->vi,1))>>2)>=pos)break; + + /* remove the packet from packet queue and track its granulepos */ + ogg_stream_packetout(&vf->os,NULL); + vorbis_synthesis_trackonly(&vf->vb,&op); /* set up a vb with + only tracking, no + pcm_decode */ + vorbis_synthesis_blockin(&vf->vd,&vf->vb); + + /* end of logical stream case is hard, especially with exact + length positioning. */ + + if(op.granulepos>-1){ + int i; + /* always believe the stream markers */ + vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2]; + if(vf->pcm_offset<0)vf->pcm_offset=0; + for(i=0;icurrent_link;i++) + vf->pcm_offset+=vf->pcmlengths[i*2+1]; + } + + lastblock=thisblock; + + }else{ + if(ret<0 && ret!=OV_HOLE)break; + + /* suck in a new page */ + if(_get_next_page(vf,&og,-1)<0)break; + if(ogg_page_bos(&og))_decode_clear(vf); + + if(vf->ready_statelinks;link++) + if(vf->serialnos[link]==serialno)break; + if(link==vf->links) continue; + vf->current_link=link; + + vf->ready_state=STREAMSET; + vf->current_serialno=ogg_page_serialno(&og); + ogg_stream_reset_serialno(&vf->os,serialno); + ret=_make_decode_ready(vf); + if(ret)return ret; + lastblock=0; + } + + ogg_stream_pagein(&vf->os,&og); + } + } + + vf->bittrack=0; + vf->samptrack=0; + /* discard samples until we reach the desired position. Crossing a + logical bitstream boundary with abandon is OK. */ + while(vf->pcm_offsetpcm_offset; + long samples=vorbis_synthesis_pcmout(&vf->vd,NULL); + + if(samples>target)samples=target; + vorbis_synthesis_read(&vf->vd,samples); + vf->pcm_offset+=samples; + + if(samplespcm_offset=ov_pcm_total(vf,-1); /* eof */ + } + return 0; +} + +/* seek to a playback time relative to the decompressed pcm stream + returns zero on success, nonzero on failure */ +int ov_time_seek(OggVorbis_File *vf,ogg_int64_t milliseconds){ + /* translate time to PCM position and call ov_pcm_seek */ + + int link=-1; + ogg_int64_t pcm_total=0; + ogg_int64_t time_total=0; + + if(vf->ready_stateseekable)return(OV_ENOSEEK); + if(milliseconds<0)return(OV_EINVAL); + + /* which bitstream section does this time offset occur in? */ + for(link=0;linklinks;link++){ + ogg_int64_t addsec = ov_time_total(vf,link); + if(millisecondspcmlengths[link*2+1]; + } + + if(link==vf->links)return(OV_EINVAL); + + /* enough information to convert time offset to pcm offset */ + { + ogg_int64_t target=pcm_total+(milliseconds-time_total)*vf->vi[link].rate/1000; + return(ov_pcm_seek(vf,target)); + } +} + +/* page-granularity version of ov_time_seek + returns zero on success, nonzero on failure */ +int ov_time_seek_page(OggVorbis_File *vf,ogg_int64_t milliseconds){ + /* translate time to PCM position and call ov_pcm_seek */ + + int link=-1; + ogg_int64_t pcm_total=0; + ogg_int64_t time_total=0; + + if(vf->ready_stateseekable)return(OV_ENOSEEK); + if(milliseconds<0)return(OV_EINVAL); + + /* which bitstream section does this time offset occur in? */ + for(link=0;linklinks;link++){ + ogg_int64_t addsec = ov_time_total(vf,link); + if(millisecondspcmlengths[link*2+1]; + } + + if(link==vf->links)return(OV_EINVAL); + + /* enough information to convert time offset to pcm offset */ + { + ogg_int64_t target=pcm_total+(milliseconds-time_total)*vf->vi[link].rate/1000; + return(ov_pcm_seek_page(vf,target)); + } +} + +/* tell the current stream offset cursor. Note that seek followed by + tell will likely not give the set offset due to caching */ +ogg_int64_t ov_raw_tell(OggVorbis_File *vf){ + if(vf->ready_stateoffset); +} + +/* return PCM offset (sample) of next PCM sample to be read */ +ogg_int64_t ov_pcm_tell(OggVorbis_File *vf){ + if(vf->ready_statepcm_offset); +} + +/* return time offset (milliseconds) of next PCM sample to be read */ +ogg_int64_t ov_time_tell(OggVorbis_File *vf){ + int link=0; + ogg_int64_t pcm_total=0; + ogg_int64_t time_total=0; + + if(vf->ready_stateseekable){ + pcm_total=ov_pcm_total(vf,-1); + time_total=ov_time_total(vf,-1); + + /* which bitstream section does this time offset occur in? */ + for(link=vf->links-1;link>=0;link--){ + pcm_total-=vf->pcmlengths[link*2+1]; + time_total-=ov_time_total(vf,link); + if(vf->pcm_offset>=pcm_total)break; + } + } + + return(time_total+(1000*vf->pcm_offset-pcm_total)/vf->vi[link].rate); +} + +/* link: -1) return the vorbis_info struct for the bitstream section + currently being decoded + 0-n) to request information for a specific bitstream section + + In the case of a non-seekable bitstream, any call returns the + current bitstream. NULL in the case that the machine is not + initialized */ + +vorbis_info *ov_info(OggVorbis_File *vf,int link){ + if(vf->seekable){ + if(link<0) + if(vf->ready_state>=STREAMSET) + return vf->vi+vf->current_link; + else + return vf->vi; + else + if(link>=vf->links) + return NULL; + else + return vf->vi+link; + }else{ + return vf->vi; + } +} + +/* grr, strong typing, grr, no templates/inheritence, grr */ +vorbis_comment *ov_comment(OggVorbis_File *vf,int link){ + if(vf->seekable){ + if(link<0) + if(vf->ready_state>=STREAMSET) + return vf->vc+vf->current_link; + else + return vf->vc; + else + if(link>=vf->links) + return NULL; + else + return vf->vc+link; + }else{ + return vf->vc; + } +} + +/* up to this point, everything could more or less hide the multiple + logical bitstream nature of chaining from the toplevel application + if the toplevel application didn't particularly care. However, at + the point that we actually read audio back, the multiple-section + nature must surface: Multiple bitstream sections do not necessarily + have to have the same number of channels or sampling rate. + + ov_read returns the sequential logical bitstream number currently + being decoded along with the PCM data in order that the toplevel + application can take action on channel/sample rate changes. This + number will be incremented even for streamed (non-seekable) streams + (for seekable streams, it represents the actual logical bitstream + index within the physical bitstream. Note that the accessor + functions above are aware of this dichotomy). + + input values: buffer) a buffer to hold packed PCM data for return + bytes_req) the byte length requested to be placed into buffer + + return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL) + 0) EOF + n) number of bytes of PCM actually returned. The + below works on a packet-by-packet basis, so the + return length is not related to the 'length' passed + in, just guaranteed to fit. + + *section) set to the logical bitstream number */ + +long ov_read(OggVorbis_File *vf,char *buffer,int bytes_req,int *bitstream){ + int i,j; + + ogg_int32_t **pcm; + long samples; + + if(vf->ready_stateready_state==INITSET){ + samples=vorbis_synthesis_pcmout(&vf->vd,&pcm); + if(samples)break; + } + + /* suck in another packet */ + { + int ret=_fetch_and_process_packet(vf,NULL,1,1); + if(ret==OV_EOF) + return(0); + if(ret<=0) + return(ret); + } + + } + + if(samples>0){ + + /* yay! proceed to pack data into the byte buffer */ + + long channels=ov_info(vf,-1)->channels; + + if(samples>(bytes_req/(2*channels))) + samples=bytes_req/(2*channels); + + for(i=0;i>9); + dest+=channels; + } + } + + vorbis_synthesis_read(&vf->vd,samples); + vf->pcm_offset+=samples; + if(bitstream)*bitstream=vf->current_link; + return(samples*2*channels); + }else{ + return(samples); + } +} diff --git a/libs/tremor/vorbisidec.pc.in b/libs/tremor/vorbisidec.pc.in new file mode 100644 index 0000000..56fa656 --- /dev/null +++ b/libs/tremor/vorbisidec.pc.in @@ -0,0 +1,14 @@ +# libvorbisidec pkg-config source file + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: vorbisidec +Description: vorbisidec is the integer Ogg Vorbis library +Version: @VERSION@ +Requires.private: ogg +Conflicts: +Libs: -L${libdir} -lvorbisidec +Cflags: -I${includedir} diff --git a/libs/tremor/win32/VS2005/libogg.vsprops b/libs/tremor/win32/VS2005/libogg.vsprops new file mode 100644 index 0000000..7fe0db7 --- /dev/null +++ b/libs/tremor/win32/VS2005/libogg.vsprops @@ -0,0 +1,19 @@ + + + + + + diff --git a/libs/tremor/win32/VS2005/libtremor/libtremor.vcproj b/libs/tremor/win32/VS2005/libtremor/libtremor.vcproj new file mode 100644 index 0000000..8f8ddcd --- /dev/null +++ b/libs/tremor/win32/VS2005/libtremor/libtremor.vcproj @@ -0,0 +1,865 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/tremor/win32/VS2008/libogg.vsprops b/libs/tremor/win32/VS2008/libogg.vsprops new file mode 100644 index 0000000..1355b50 --- /dev/null +++ b/libs/tremor/win32/VS2008/libogg.vsprops @@ -0,0 +1,19 @@ + + + + + + diff --git a/libs/tremor/win32/VS2008/libtremor/libtremor.vcproj b/libs/tremor/win32/VS2008/libtremor/libtremor.vcproj new file mode 100644 index 0000000..4fb9f48 --- /dev/null +++ b/libs/tremor/win32/VS2008/libtremor/libtremor.vcproj @@ -0,0 +1,865 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/tremor/window.c b/libs/tremor/window.c new file mode 100644 index 0000000..006a1ee --- /dev/null +++ b/libs/tremor/window.c @@ -0,0 +1,83 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * * + ******************************************************************** + + function: window functions + + ********************************************************************/ + +#include +#include +#include "misc.h" +#include "window.h" +#include "window_lookup.h" + +const void *_vorbis_window(int type, int left){ + + switch(type){ + case 0: + + switch(left){ + case 32: + return vwin64; + case 64: + return vwin128; + case 128: + return vwin256; + case 256: + return vwin512; + case 512: + return vwin1024; + case 1024: + return vwin2048; + case 2048: + return vwin4096; + case 4096: + return vwin8192; + default: + return(0); + } + break; + default: + return(0); + } +} + +void _vorbis_apply_window(ogg_int32_t *d,const void *window_p[2], + long *blocksizes, + int lW,int W,int nW){ + + LOOKUP_T *window[2]={window_p[0],window_p[1]}; + long n=blocksizes[W]; + long ln=blocksizes[lW]; + long rn=blocksizes[nW]; + + long leftbegin=n/4-ln/4; + long leftend=leftbegin+ln/2; + + long rightbegin=n/2+n/4-rn/4; + long rightend=rightbegin+rn/2; + + int i,p; + + for(i=0;i + +static const LOOKUP_T vwin64[32] = { + X(0x001f0003), X(0x01168c98), X(0x030333c8), X(0x05dfe3a4), + X(0x09a49562), X(0x0e45df18), X(0x13b47ef2), X(0x19dcf676), + X(0x20a74d83), X(0x27f7137c), X(0x2fabb05a), X(0x37a1105a), + X(0x3fb0ab28), X(0x47b2dcd1), X(0x4f807bc6), X(0x56f48e70), + X(0x5dedfc79), X(0x64511653), X(0x6a08cfff), X(0x6f079328), + X(0x734796f4), X(0x76cab7f2), X(0x7999d6e8), X(0x7bc3cf9f), + X(0x7d5c20c1), X(0x7e7961df), X(0x7f33a567), X(0x7fa2e1d0), + X(0x7fdd78a5), X(0x7ff6ec6d), X(0x7ffed0e9), X(0x7ffffc3f), +}; + +static const LOOKUP_T vwin128[64] = { + X(0x0007c04d), X(0x0045bb89), X(0x00c18b87), X(0x017ae294), + X(0x02714a4e), X(0x03a4217a), X(0x05129952), X(0x06bbb24f), + X(0x089e38a1), X(0x0ab8c073), X(0x0d09a228), X(0x0f8ef6bd), + X(0x12469488), X(0x152e0c7a), X(0x1842a81c), X(0x1b81686d), + X(0x1ee705d9), X(0x226ff15d), X(0x26185705), X(0x29dc21cc), + X(0x2db700fe), X(0x31a46f08), X(0x359fb9c1), X(0x39a40c0c), + X(0x3dac78b6), X(0x41b40674), X(0x45b5bcb0), X(0x49acb109), + X(0x4d94152b), X(0x516744bd), X(0x5521d320), X(0x58bf98a5), + X(0x5c3cbef4), X(0x5f95cc5d), X(0x62c7add7), X(0x65cfbf64), + X(0x68abd2ba), X(0x6b5a3405), X(0x6dd9acab), X(0x7029840d), + X(0x72497e38), X(0x7439d8ac), X(0x75fb4532), X(0x778ee30a), + X(0x78f6367e), X(0x7a331f1a), X(0x7b47cccd), X(0x7c36b416), + X(0x7d028192), X(0x7dae0d18), X(0x7e3c4caa), X(0x7eb04763), + X(0x7f0d08a7), X(0x7f5593b7), X(0x7f8cd7d5), X(0x7fb5a513), + X(0x7fd2a1fc), X(0x7fe64212), X(0x7ff2bd4c), X(0x7ffa0890), + X(0x7ffdcf39), X(0x7fff6dac), X(0x7fffed01), X(0x7fffffc4), +}; + +static const LOOKUP_T vwin256[128] = { + X(0x0001f018), X(0x00117066), X(0x00306e9e), X(0x005ee5f1), + X(0x009ccf26), X(0x00ea208b), X(0x0146cdea), X(0x01b2c87f), + X(0x022dfedf), X(0x02b85ced), X(0x0351cbbd), X(0x03fa317f), + X(0x04b17167), X(0x05776b90), X(0x064bfcdc), X(0x072efedd), + X(0x082047b4), X(0x091fa9f1), X(0x0a2cf477), X(0x0b47f25d), + X(0x0c706ad2), X(0x0da620ff), X(0x0ee8d3ef), X(0x10383e75), + X(0x11941716), X(0x12fc0ff6), X(0x146fd6c8), X(0x15ef14c2), + X(0x17796e8e), X(0x190e844f), X(0x1aadf196), X(0x1c574d6e), + X(0x1e0a2a62), X(0x1fc61688), X(0x218a9b9c), X(0x23573f12), + X(0x252b823d), X(0x2706e269), X(0x28e8d913), X(0x2ad0dc0e), + X(0x2cbe5dc1), X(0x2eb0cd60), X(0x30a79733), X(0x32a224d5), + X(0x349fdd8b), X(0x36a02690), X(0x38a2636f), X(0x3aa5f65e), + X(0x3caa409e), X(0x3eaea2df), X(0x40b27da6), X(0x42b531b8), + X(0x44b62086), X(0x46b4ac99), X(0x48b03a05), X(0x4aa82ed5), + X(0x4c9bf37d), X(0x4e8af349), X(0x50749ccb), X(0x52586246), + X(0x5435ba1c), X(0x560c1f31), X(0x57db1152), X(0x59a21591), + X(0x5b60b6a3), X(0x5d168535), X(0x5ec31839), X(0x60660d36), + X(0x61ff0886), X(0x638db595), X(0x6511c717), X(0x668af734), + X(0x67f907b0), X(0x695bc207), X(0x6ab2f787), X(0x6bfe815a), + X(0x6d3e4090), X(0x6e721e16), X(0x6f9a0ab5), X(0x70b5fef8), + X(0x71c5fb16), X(0x72ca06cd), X(0x73c2313d), X(0x74ae90b2), + X(0x758f4275), X(0x76646a85), X(0x772e335c), X(0x77eccda0), + X(0x78a06fd7), X(0x79495613), X(0x79e7c19c), X(0x7a7bf894), + X(0x7b064596), X(0x7b86f757), X(0x7bfe6044), X(0x7c6cd615), + X(0x7cd2b16e), X(0x7d304d71), X(0x7d860756), X(0x7dd43e06), + X(0x7e1b51ad), X(0x7e5ba355), X(0x7e95947e), X(0x7ec986bb), + X(0x7ef7db4a), X(0x7f20f2b9), X(0x7f452c7f), X(0x7f64e6a7), + X(0x7f807d71), X(0x7f984aff), X(0x7faca700), X(0x7fbde662), + X(0x7fcc5b04), X(0x7fd85372), X(0x7fe21a99), X(0x7fe9f791), + X(0x7ff02d58), X(0x7ff4fa9e), X(0x7ff89990), X(0x7ffb3faa), + X(0x7ffd1d8b), X(0x7ffe5ecc), X(0x7fff29e0), X(0x7fff9ff3), + X(0x7fffdcd2), X(0x7ffff6d6), X(0x7ffffed0), X(0x7ffffffc), +}; + +static const LOOKUP_T vwin512[256] = { + X(0x00007c06), X(0x00045c32), X(0x000c1c62), X(0x0017bc4c), + X(0x00273b7a), X(0x003a9955), X(0x0051d51c), X(0x006cede7), + X(0x008be2a9), X(0x00aeb22a), X(0x00d55b0d), X(0x00ffdbcc), + X(0x012e32b6), X(0x01605df5), X(0x01965b85), X(0x01d02939), + X(0x020dc4ba), X(0x024f2b83), X(0x02945ae6), X(0x02dd5004), + X(0x032a07d3), X(0x037a7f19), X(0x03ceb26e), X(0x04269e37), + X(0x04823eab), X(0x04e18fcc), X(0x05448d6d), X(0x05ab3329), + X(0x06157c68), X(0x0683645e), X(0x06f4e607), X(0x0769fc25), + X(0x07e2a146), X(0x085ecfbc), X(0x08de819f), X(0x0961b0cc), + X(0x09e856e3), X(0x0a726d46), X(0x0affed1d), X(0x0b90cf4c), + X(0x0c250c79), X(0x0cbc9d0b), X(0x0d577926), X(0x0df598aa), + X(0x0e96f337), X(0x0f3b8026), X(0x0fe3368f), X(0x108e0d42), + X(0x113bfaca), X(0x11ecf56b), X(0x12a0f324), X(0x1357e9ac), + X(0x1411ce70), X(0x14ce9698), X(0x158e3702), X(0x1650a444), + X(0x1715d2aa), X(0x17ddb638), X(0x18a842aa), X(0x19756b72), + X(0x1a4523b9), X(0x1b175e62), X(0x1bec0e04), X(0x1cc324f0), + X(0x1d9c9532), X(0x1e78508a), X(0x1f564876), X(0x20366e2e), + X(0x2118b2a2), X(0x21fd0681), X(0x22e35a37), X(0x23cb9dee), + X(0x24b5c18e), X(0x25a1b4c0), X(0x268f66f1), X(0x277ec74e), + X(0x286fc4cc), X(0x29624e23), X(0x2a5651d7), X(0x2b4bbe34), + X(0x2c428150), X(0x2d3a8913), X(0x2e33c332), X(0x2f2e1d35), + X(0x30298478), X(0x3125e62d), X(0x32232f61), X(0x33214cfc), + X(0x34202bc2), X(0x351fb85a), X(0x361fdf4f), X(0x37208d10), + X(0x3821adf7), X(0x39232e49), X(0x3a24fa3c), X(0x3b26fdf6), + X(0x3c292593), X(0x3d2b5d29), X(0x3e2d90c8), X(0x3f2fac7f), + X(0x40319c5f), X(0x41334c81), X(0x4234a905), X(0x43359e16), + X(0x443617f3), X(0x453602eb), X(0x46354b65), X(0x4733dde1), + X(0x4831a6ff), X(0x492e937f), X(0x4a2a9045), X(0x4b258a5f), + X(0x4c1f6f06), X(0x4d182ba2), X(0x4e0fadce), X(0x4f05e35b), + X(0x4ffaba53), X(0x50ee20fd), X(0x51e005e1), X(0x52d057ca), + X(0x53bf05ca), X(0x54abff3b), X(0x559733c7), X(0x56809365), + X(0x57680e62), X(0x584d955d), X(0x59311952), X(0x5a128b96), + X(0x5af1dddd), X(0x5bcf023a), X(0x5ca9eb27), X(0x5d828b81), + X(0x5e58d68d), X(0x5f2cbffc), X(0x5ffe3be9), X(0x60cd3edf), + X(0x6199bdda), X(0x6263ae45), X(0x632b0602), X(0x63efbb66), + X(0x64b1c53f), X(0x65711ad0), X(0x662db3d7), X(0x66e7888d), + X(0x679e91a5), X(0x6852c84e), X(0x69042635), X(0x69b2a582), + X(0x6a5e40dd), X(0x6b06f36c), X(0x6bacb8d2), X(0x6c4f8d30), + X(0x6cef6d26), X(0x6d8c55d4), X(0x6e2644d4), X(0x6ebd3840), + X(0x6f512ead), X(0x6fe2272e), X(0x7070214f), X(0x70fb1d17), + X(0x71831b06), X(0x72081c16), X(0x728a21b5), X(0x73092dc8), + X(0x738542a6), X(0x73fe631b), X(0x74749261), X(0x74e7d421), + X(0x75582c72), X(0x75c59fd5), X(0x76303333), X(0x7697ebdd), + X(0x76fccf85), X(0x775ee443), X(0x77be308a), X(0x781abb2e), + X(0x78748b59), X(0x78cba88e), X(0x79201aa7), X(0x7971e9cd), + X(0x79c11e79), X(0x7a0dc170), X(0x7a57dbc2), X(0x7a9f76c1), + X(0x7ae49c07), X(0x7b27556b), X(0x7b67ad02), X(0x7ba5ad1b), + X(0x7be1603a), X(0x7c1ad118), X(0x7c520a9e), X(0x7c8717e1), + X(0x7cba0421), X(0x7ceadac3), X(0x7d19a74f), X(0x7d46756e), + X(0x7d7150e5), X(0x7d9a4592), X(0x7dc15f69), X(0x7de6aa71), + X(0x7e0a32c0), X(0x7e2c0479), X(0x7e4c2bc7), X(0x7e6ab4db), + X(0x7e87abe9), X(0x7ea31d24), X(0x7ebd14be), X(0x7ed59edd), + X(0x7eecc7a3), X(0x7f029b21), X(0x7f17255a), X(0x7f2a723f), + X(0x7f3c8daa), X(0x7f4d835d), X(0x7f5d5f00), X(0x7f6c2c1b), + X(0x7f79f617), X(0x7f86c83a), X(0x7f92ada2), X(0x7f9db146), + X(0x7fa7ddf3), X(0x7fb13e46), X(0x7fb9dcb0), X(0x7fc1c36c), + X(0x7fc8fc83), X(0x7fcf91c7), X(0x7fd58cd2), X(0x7fdaf702), + X(0x7fdfd979), X(0x7fe43d1c), X(0x7fe82a8b), X(0x7febaa29), + X(0x7feec412), X(0x7ff1801c), X(0x7ff3e5d6), X(0x7ff5fc86), + X(0x7ff7cb29), X(0x7ff9586f), X(0x7ffaaaba), X(0x7ffbc81e), + X(0x7ffcb660), X(0x7ffd7af3), X(0x7ffe1afa), X(0x7ffe9b42), + X(0x7fff0047), X(0x7fff4e2f), X(0x7fff88c9), X(0x7fffb390), + X(0x7fffd1a6), X(0x7fffe5d7), X(0x7ffff296), X(0x7ffff9fd), + X(0x7ffffdcd), X(0x7fffff6d), X(0x7fffffed), X(0x7fffffff), +}; + +static const LOOKUP_T vwin1024[512] = { + X(0x00001f02), X(0x0001170e), X(0x00030724), X(0x0005ef40), + X(0x0009cf59), X(0x000ea767), X(0x0014775e), X(0x001b3f2e), + X(0x0022fec8), X(0x002bb618), X(0x00356508), X(0x00400b81), + X(0x004ba968), X(0x00583ea0), X(0x0065cb0a), X(0x00744e84), + X(0x0083c8ea), X(0x00943a14), X(0x00a5a1da), X(0x00b80010), + X(0x00cb5488), X(0x00df9f10), X(0x00f4df76), X(0x010b1584), + X(0x01224101), X(0x013a61b2), X(0x01537759), X(0x016d81b6), + X(0x01888087), X(0x01a47385), X(0x01c15a69), X(0x01df34e6), + X(0x01fe02b1), X(0x021dc377), X(0x023e76e7), X(0x02601ca9), + X(0x0282b466), X(0x02a63dc1), X(0x02cab85d), X(0x02f023d6), + X(0x03167fcb), X(0x033dcbd3), X(0x03660783), X(0x038f3270), + X(0x03b94c29), X(0x03e4543a), X(0x04104a2e), X(0x043d2d8b), + X(0x046afdd5), X(0x0499ba8c), X(0x04c9632d), X(0x04f9f734), + X(0x052b7615), X(0x055ddf46), X(0x05913237), X(0x05c56e53), + X(0x05fa9306), X(0x06309fb6), X(0x066793c5), X(0x069f6e93), + X(0x06d82f7c), X(0x0711d5d9), X(0x074c60fe), X(0x0787d03d), + X(0x07c422e4), X(0x0801583e), X(0x083f6f91), X(0x087e681f), + X(0x08be4129), X(0x08fef9ea), X(0x0940919a), X(0x0983076d), + X(0x09c65a92), X(0x0a0a8a38), X(0x0a4f9585), X(0x0a957b9f), + X(0x0adc3ba7), X(0x0b23d4b9), X(0x0b6c45ee), X(0x0bb58e5a), + X(0x0bffad0f), X(0x0c4aa11a), X(0x0c966982), X(0x0ce3054d), + X(0x0d30737b), X(0x0d7eb308), X(0x0dcdc2eb), X(0x0e1da21a), + X(0x0e6e4f83), X(0x0ebfca11), X(0x0f1210ad), X(0x0f652238), + X(0x0fb8fd91), X(0x100da192), X(0x10630d11), X(0x10b93ee0), + X(0x111035cb), X(0x1167f09a), X(0x11c06e13), X(0x1219acf5), + X(0x1273abfb), X(0x12ce69db), X(0x1329e54a), X(0x13861cf3), + X(0x13e30f80), X(0x1440bb97), X(0x149f1fd8), X(0x14fe3ade), + X(0x155e0b40), X(0x15be8f92), X(0x161fc662), X(0x1681ae38), + X(0x16e4459b), X(0x17478b0b), X(0x17ab7d03), X(0x181019fb), + X(0x18756067), X(0x18db4eb3), X(0x1941e34a), X(0x19a91c92), + X(0x1a10f8ea), X(0x1a7976af), X(0x1ae29439), X(0x1b4c4fda), + X(0x1bb6a7e2), X(0x1c219a9a), X(0x1c8d2649), X(0x1cf9492e), + X(0x1d660188), X(0x1dd34d8e), X(0x1e412b74), X(0x1eaf996a), + X(0x1f1e959b), X(0x1f8e1e2f), X(0x1ffe3146), X(0x206ecd01), + X(0x20dfef78), X(0x215196c2), X(0x21c3c0f0), X(0x22366c10), + X(0x22a9962a), X(0x231d3d45), X(0x23915f60), X(0x2405fa7a), + X(0x247b0c8c), X(0x24f09389), X(0x25668d65), X(0x25dcf80c), + X(0x2653d167), X(0x26cb175e), X(0x2742c7d0), X(0x27bae09e), + X(0x28335fa2), X(0x28ac42b3), X(0x292587a5), X(0x299f2c48), + X(0x2a192e69), X(0x2a938bd1), X(0x2b0e4247), X(0x2b894f8d), + X(0x2c04b164), X(0x2c806588), X(0x2cfc69b2), X(0x2d78bb9a), + X(0x2df558f4), X(0x2e723f6f), X(0x2eef6cbb), X(0x2f6cde83), + X(0x2fea9270), X(0x30688627), X(0x30e6b74e), X(0x31652385), + X(0x31e3c86b), X(0x3262a39e), X(0x32e1b2b8), X(0x3360f352), + X(0x33e06303), X(0x345fff5e), X(0x34dfc5f8), X(0x355fb462), + X(0x35dfc82a), X(0x365ffee0), X(0x36e0560f), X(0x3760cb43), + X(0x37e15c05), X(0x386205df), X(0x38e2c657), X(0x39639af5), + X(0x39e4813e), X(0x3a6576b6), X(0x3ae678e3), X(0x3b678547), + X(0x3be89965), X(0x3c69b2c1), X(0x3ceacedc), X(0x3d6beb37), + X(0x3ded0557), X(0x3e6e1abb), X(0x3eef28e6), X(0x3f702d5a), + X(0x3ff1259a), X(0x40720f29), X(0x40f2e789), X(0x4173ac3f), + X(0x41f45ad0), X(0x4274f0c2), X(0x42f56b9a), X(0x4375c8e0), + X(0x43f6061d), X(0x447620db), X(0x44f616a5), X(0x4575e509), + X(0x45f58994), X(0x467501d6), X(0x46f44b62), X(0x477363cb), + X(0x47f248a6), X(0x4870f78e), X(0x48ef6e1a), X(0x496da9e8), + X(0x49eba897), X(0x4a6967c8), X(0x4ae6e521), X(0x4b641e47), + X(0x4be110e5), X(0x4c5dbaa7), X(0x4cda193f), X(0x4d562a5f), + X(0x4dd1ebbd), X(0x4e4d5b15), X(0x4ec87623), X(0x4f433aa9), + X(0x4fbda66c), X(0x5037b734), X(0x50b16acf), X(0x512abf0e), + X(0x51a3b1c5), X(0x521c40ce), X(0x52946a06), X(0x530c2b50), + X(0x53838292), X(0x53fa6db8), X(0x5470eab3), X(0x54e6f776), + X(0x555c91fc), X(0x55d1b844), X(0x56466851), X(0x56baa02f), + X(0x572e5deb), X(0x57a19f98), X(0x58146352), X(0x5886a737), + X(0x58f8696d), X(0x5969a81c), X(0x59da6177), X(0x5a4a93b4), + X(0x5aba3d0f), X(0x5b295bcb), X(0x5b97ee30), X(0x5c05f28d), + X(0x5c736738), X(0x5ce04a8d), X(0x5d4c9aed), X(0x5db856c1), + X(0x5e237c78), X(0x5e8e0a89), X(0x5ef7ff6f), X(0x5f6159b0), + X(0x5fca17d4), X(0x6032386e), X(0x6099ba15), X(0x61009b69), + X(0x6166db11), X(0x61cc77b9), X(0x62317017), X(0x6295c2e7), + X(0x62f96eec), X(0x635c72f1), X(0x63becdc8), X(0x64207e4b), + X(0x6481835a), X(0x64e1dbde), X(0x654186c8), X(0x65a0830e), + X(0x65fecfb1), X(0x665c6bb7), X(0x66b95630), X(0x67158e30), + X(0x677112d7), X(0x67cbe34b), X(0x6825feb9), X(0x687f6456), + X(0x68d81361), X(0x69300b1e), X(0x69874ada), X(0x69ddd1ea), + X(0x6a339fab), X(0x6a88b382), X(0x6add0cdb), X(0x6b30ab2a), + X(0x6b838dec), X(0x6bd5b4a6), X(0x6c271ee2), X(0x6c77cc36), + X(0x6cc7bc3d), X(0x6d16ee9b), X(0x6d6562fb), X(0x6db31911), + X(0x6e001099), X(0x6e4c4955), X(0x6e97c311), X(0x6ee27d9f), + X(0x6f2c78d9), X(0x6f75b4a2), X(0x6fbe30e4), X(0x7005ed91), + X(0x704ceaa1), X(0x70932816), X(0x70d8a5f8), X(0x711d6457), + X(0x7161634b), X(0x71a4a2f3), X(0x71e72375), X(0x7228e500), + X(0x7269e7c8), X(0x72aa2c0a), X(0x72e9b209), X(0x73287a12), + X(0x73668476), X(0x73a3d18f), X(0x73e061bc), X(0x741c3566), + X(0x74574cfa), X(0x7491a8ee), X(0x74cb49be), X(0x75042fec), + X(0x753c5c03), X(0x7573ce92), X(0x75aa882f), X(0x75e08979), + X(0x7615d313), X(0x764a65a7), X(0x767e41e5), X(0x76b16884), + X(0x76e3da40), X(0x771597dc), X(0x7746a221), X(0x7776f9dd), + X(0x77a69fe6), X(0x77d59514), X(0x7803da49), X(0x7831706a), + X(0x785e5861), X(0x788a9320), X(0x78b6219c), X(0x78e104cf), + X(0x790b3dbb), X(0x7934cd64), X(0x795db4d5), X(0x7985f51d), + X(0x79ad8f50), X(0x79d48486), X(0x79fad5de), X(0x7a208478), + X(0x7a45917b), X(0x7a69fe12), X(0x7a8dcb6c), X(0x7ab0fabb), + X(0x7ad38d36), X(0x7af5841a), X(0x7b16e0a3), X(0x7b37a416), + X(0x7b57cfb8), X(0x7b7764d4), X(0x7b9664b6), X(0x7bb4d0b0), + X(0x7bd2aa14), X(0x7beff23b), X(0x7c0caa7f), X(0x7c28d43c), + X(0x7c4470d2), X(0x7c5f81a5), X(0x7c7a081a), X(0x7c940598), + X(0x7cad7b8b), X(0x7cc66b5e), X(0x7cded680), X(0x7cf6be64), + X(0x7d0e247b), X(0x7d250a3c), X(0x7d3b711c), X(0x7d515a95), + X(0x7d66c822), X(0x7d7bbb3c), X(0x7d903563), X(0x7da43814), + X(0x7db7c4d0), X(0x7dcadd16), X(0x7ddd826a), X(0x7defb64d), + X(0x7e017a44), X(0x7e12cfd3), X(0x7e23b87f), X(0x7e3435cc), + X(0x7e444943), X(0x7e53f467), X(0x7e6338c0), X(0x7e7217d5), + X(0x7e80932b), X(0x7e8eac49), X(0x7e9c64b7), X(0x7ea9bdf8), + X(0x7eb6b994), X(0x7ec35910), X(0x7ecf9def), X(0x7edb89b6), + X(0x7ee71de9), X(0x7ef25c09), X(0x7efd4598), X(0x7f07dc16), + X(0x7f122103), X(0x7f1c15dc), X(0x7f25bc1f), X(0x7f2f1547), + X(0x7f3822cd), X(0x7f40e62b), X(0x7f4960d6), X(0x7f519443), + X(0x7f5981e7), X(0x7f612b31), X(0x7f689191), X(0x7f6fb674), + X(0x7f769b45), X(0x7f7d416c), X(0x7f83aa51), X(0x7f89d757), + X(0x7f8fc9df), X(0x7f958348), X(0x7f9b04ef), X(0x7fa0502e), + X(0x7fa56659), X(0x7faa48c7), X(0x7faef8c7), X(0x7fb377a7), + X(0x7fb7c6b3), X(0x7fbbe732), X(0x7fbfda67), X(0x7fc3a196), + X(0x7fc73dfa), X(0x7fcab0ce), X(0x7fcdfb4a), X(0x7fd11ea0), + X(0x7fd41c00), X(0x7fd6f496), X(0x7fd9a989), X(0x7fdc3bff), + X(0x7fdead17), X(0x7fe0fdee), X(0x7fe32f9d), X(0x7fe54337), + X(0x7fe739ce), X(0x7fe9146c), X(0x7fead41b), X(0x7fec79dd), + X(0x7fee06b2), X(0x7fef7b94), X(0x7ff0d97b), X(0x7ff22158), + X(0x7ff35417), X(0x7ff472a3), X(0x7ff57de0), X(0x7ff676ac), + X(0x7ff75de3), X(0x7ff8345a), X(0x7ff8fae4), X(0x7ff9b24b), + X(0x7ffa5b58), X(0x7ffaf6cd), X(0x7ffb8568), X(0x7ffc07e2), + X(0x7ffc7eed), X(0x7ffceb38), X(0x7ffd4d6d), X(0x7ffda631), + X(0x7ffdf621), X(0x7ffe3dd8), X(0x7ffe7dea), X(0x7ffeb6e7), + X(0x7ffee959), X(0x7fff15c4), X(0x7fff3ca9), X(0x7fff5e80), + X(0x7fff7bc0), X(0x7fff94d6), X(0x7fffaa2d), X(0x7fffbc29), + X(0x7fffcb29), X(0x7fffd786), X(0x7fffe195), X(0x7fffe9a3), + X(0x7fffeffa), X(0x7ffff4dd), X(0x7ffff889), X(0x7ffffb37), + X(0x7ffffd1a), X(0x7ffffe5d), X(0x7fffff29), X(0x7fffffa0), + X(0x7fffffdd), X(0x7ffffff7), X(0x7fffffff), X(0x7fffffff), +}; + +static const LOOKUP_T vwin2048[1024] = { + X(0x000007c0), X(0x000045c4), X(0x0000c1ca), X(0x00017bd3), + X(0x000273de), X(0x0003a9eb), X(0x00051df9), X(0x0006d007), + X(0x0008c014), X(0x000aee1e), X(0x000d5a25), X(0x00100428), + X(0x0012ec23), X(0x00161216), X(0x001975fe), X(0x001d17da), + X(0x0020f7a8), X(0x00251564), X(0x0029710c), X(0x002e0a9e), + X(0x0032e217), X(0x0037f773), X(0x003d4ab0), X(0x0042dbca), + X(0x0048aabe), X(0x004eb788), X(0x00550224), X(0x005b8a8f), + X(0x006250c5), X(0x006954c1), X(0x0070967e), X(0x007815f9), + X(0x007fd32c), X(0x0087ce13), X(0x009006a9), X(0x00987ce9), + X(0x00a130cc), X(0x00aa224f), X(0x00b3516b), X(0x00bcbe1a), + X(0x00c66856), X(0x00d0501a), X(0x00da755f), X(0x00e4d81f), + X(0x00ef7853), X(0x00fa55f4), X(0x010570fc), X(0x0110c963), + X(0x011c5f22), X(0x01283232), X(0x0134428c), X(0x01409027), + X(0x014d1afb), X(0x0159e302), X(0x0166e831), X(0x01742a82), + X(0x0181a9ec), X(0x018f6665), X(0x019d5fe5), X(0x01ab9663), + X(0x01ba09d6), X(0x01c8ba34), X(0x01d7a775), X(0x01e6d18d), + X(0x01f63873), X(0x0205dc1e), X(0x0215bc82), X(0x0225d997), + X(0x02363350), X(0x0246c9a3), X(0x02579c86), X(0x0268abed), + X(0x0279f7cc), X(0x028b801a), X(0x029d44c9), X(0x02af45ce), + X(0x02c1831d), X(0x02d3fcaa), X(0x02e6b269), X(0x02f9a44c), + X(0x030cd248), X(0x03203c4f), X(0x0333e255), X(0x0347c44b), + X(0x035be225), X(0x03703bd5), X(0x0384d14d), X(0x0399a280), + X(0x03aeaf5e), X(0x03c3f7d9), X(0x03d97be4), X(0x03ef3b6e), + X(0x0405366a), X(0x041b6cc8), X(0x0431de78), X(0x04488b6c), + X(0x045f7393), X(0x047696dd), X(0x048df53b), X(0x04a58e9b), + X(0x04bd62ee), X(0x04d57223), X(0x04edbc28), X(0x050640ed), + X(0x051f0060), X(0x0537fa70), X(0x05512f0a), X(0x056a9e1e), + X(0x05844798), X(0x059e2b67), X(0x05b84978), X(0x05d2a1b8), + X(0x05ed3414), X(0x06080079), X(0x062306d3), X(0x063e470f), + X(0x0659c119), X(0x067574dd), X(0x06916247), X(0x06ad8941), + X(0x06c9e9b8), X(0x06e68397), X(0x070356c8), X(0x07206336), + X(0x073da8cb), X(0x075b2772), X(0x0778df15), X(0x0796cf9c), + X(0x07b4f8f3), X(0x07d35b01), X(0x07f1f5b1), X(0x0810c8eb), + X(0x082fd497), X(0x084f189e), X(0x086e94e9), X(0x088e495e), + X(0x08ae35e6), X(0x08ce5a68), X(0x08eeb6cc), X(0x090f4af8), + X(0x093016d3), X(0x09511a44), X(0x09725530), X(0x0993c77f), + X(0x09b57115), X(0x09d751d8), X(0x09f969ae), X(0x0a1bb87c), + X(0x0a3e3e26), X(0x0a60fa91), X(0x0a83eda2), X(0x0aa7173c), + X(0x0aca7743), X(0x0aee0d9b), X(0x0b11da28), X(0x0b35dccc), + X(0x0b5a156a), X(0x0b7e83e5), X(0x0ba3281f), X(0x0bc801fa), + X(0x0bed1159), X(0x0c12561c), X(0x0c37d025), X(0x0c5d7f55), + X(0x0c83638d), X(0x0ca97cae), X(0x0ccfca97), X(0x0cf64d2a), + X(0x0d1d0444), X(0x0d43efc7), X(0x0d6b0f92), X(0x0d926383), + X(0x0db9eb79), X(0x0de1a752), X(0x0e0996ee), X(0x0e31ba29), + X(0x0e5a10e2), X(0x0e829af6), X(0x0eab5841), X(0x0ed448a2), + X(0x0efd6bf4), X(0x0f26c214), X(0x0f504ade), X(0x0f7a062e), + X(0x0fa3f3df), X(0x0fce13cd), X(0x0ff865d2), X(0x1022e9ca), + X(0x104d9f8e), X(0x107886f9), X(0x10a39fe5), X(0x10ceea2c), + X(0x10fa65a6), X(0x1126122d), X(0x1151ef9a), X(0x117dfdc5), + X(0x11aa3c87), X(0x11d6abb6), X(0x12034b2c), X(0x12301ac0), + X(0x125d1a48), X(0x128a499b), X(0x12b7a891), X(0x12e536ff), + X(0x1312f4bb), X(0x1340e19c), X(0x136efd75), X(0x139d481e), + X(0x13cbc16a), X(0x13fa692f), X(0x14293f40), X(0x14584371), + X(0x14877597), X(0x14b6d585), X(0x14e6630d), X(0x15161e04), + X(0x1546063b), X(0x15761b85), X(0x15a65db3), X(0x15d6cc99), + X(0x16076806), X(0x16382fcd), X(0x166923bf), X(0x169a43ab), + X(0x16cb8f62), X(0x16fd06b5), X(0x172ea973), X(0x1760776b), + X(0x1792706e), X(0x17c49449), X(0x17f6e2cb), X(0x18295bc3), + X(0x185bfeff), X(0x188ecc4c), X(0x18c1c379), X(0x18f4e452), + X(0x19282ea4), X(0x195ba23c), X(0x198f3ee6), X(0x19c3046e), + X(0x19f6f2a1), X(0x1a2b094a), X(0x1a5f4833), X(0x1a93af28), + X(0x1ac83df3), X(0x1afcf460), X(0x1b31d237), X(0x1b66d744), + X(0x1b9c034e), X(0x1bd15621), X(0x1c06cf84), X(0x1c3c6f40), + X(0x1c72351e), X(0x1ca820e6), X(0x1cde3260), X(0x1d146953), + X(0x1d4ac587), X(0x1d8146c3), X(0x1db7eccd), X(0x1deeb76c), + X(0x1e25a667), X(0x1e5cb982), X(0x1e93f085), X(0x1ecb4b33), + X(0x1f02c953), X(0x1f3a6aaa), X(0x1f722efb), X(0x1faa160b), + X(0x1fe21f9e), X(0x201a4b79), X(0x2052995d), X(0x208b0910), + X(0x20c39a53), X(0x20fc4cea), X(0x21352097), X(0x216e151c), + X(0x21a72a3a), X(0x21e05fb5), X(0x2219b54d), X(0x22532ac3), + X(0x228cbfd8), X(0x22c6744d), X(0x230047e2), X(0x233a3a58), + X(0x23744b6d), X(0x23ae7ae3), X(0x23e8c878), X(0x242333ec), + X(0x245dbcfd), X(0x24986369), X(0x24d326f1), X(0x250e0750), + X(0x25490446), X(0x25841d90), X(0x25bf52ec), X(0x25faa417), + X(0x263610cd), X(0x267198cc), X(0x26ad3bcf), X(0x26e8f994), + X(0x2724d1d6), X(0x2760c451), X(0x279cd0c0), X(0x27d8f6e0), + X(0x2815366a), X(0x28518f1b), X(0x288e00ac), X(0x28ca8ad8), + X(0x29072d5a), X(0x2943e7eb), X(0x2980ba45), X(0x29bda422), + X(0x29faa53c), X(0x2a37bd4a), X(0x2a74ec07), X(0x2ab2312b), + X(0x2aef8c6f), X(0x2b2cfd8b), X(0x2b6a8437), X(0x2ba8202c), + X(0x2be5d120), X(0x2c2396cc), X(0x2c6170e7), X(0x2c9f5f29), + X(0x2cdd6147), X(0x2d1b76fa), X(0x2d599ff7), X(0x2d97dbf5), + X(0x2dd62aab), X(0x2e148bcf), X(0x2e52ff16), X(0x2e918436), + X(0x2ed01ae5), X(0x2f0ec2d9), X(0x2f4d7bc6), X(0x2f8c4562), + X(0x2fcb1f62), X(0x300a097a), X(0x3049035f), X(0x30880cc6), + X(0x30c72563), X(0x31064cea), X(0x3145830f), X(0x3184c786), + X(0x31c41a03), X(0x32037a39), X(0x3242e7dc), X(0x3282629f), + X(0x32c1ea36), X(0x33017e53), X(0x33411ea9), X(0x3380caec), + X(0x33c082ce), X(0x34004602), X(0x34401439), X(0x347fed27), + X(0x34bfd07e), X(0x34ffbdf0), X(0x353fb52e), X(0x357fb5ec), + X(0x35bfbfda), X(0x35ffd2aa), X(0x363fee0f), X(0x368011b9), + X(0x36c03d5a), X(0x370070a4), X(0x3740ab48), X(0x3780ecf7), + X(0x37c13562), X(0x3801843a), X(0x3841d931), X(0x388233f7), + X(0x38c2943d), X(0x3902f9b4), X(0x3943640d), X(0x3983d2f8), + X(0x39c44626), X(0x3a04bd48), X(0x3a45380e), X(0x3a85b62a), + X(0x3ac6374a), X(0x3b06bb20), X(0x3b47415c), X(0x3b87c9ae), + X(0x3bc853c7), X(0x3c08df57), X(0x3c496c0f), X(0x3c89f99f), + X(0x3cca87b6), X(0x3d0b1605), X(0x3d4ba43d), X(0x3d8c320e), + X(0x3dccbf27), X(0x3e0d4b3a), X(0x3e4dd5f6), X(0x3e8e5f0c), + X(0x3ecee62b), X(0x3f0f6b05), X(0x3f4fed49), X(0x3f906ca8), + X(0x3fd0e8d2), X(0x40116177), X(0x4051d648), X(0x409246f6), + X(0x40d2b330), X(0x41131aa7), X(0x41537d0c), X(0x4193da10), + X(0x41d43162), X(0x421482b4), X(0x4254cdb7), X(0x4295121b), + X(0x42d54f91), X(0x431585ca), X(0x4355b477), X(0x4395db49), + X(0x43d5f9f1), X(0x44161021), X(0x44561d8a), X(0x449621dd), + X(0x44d61ccc), X(0x45160e08), X(0x4555f544), X(0x4595d230), + X(0x45d5a47f), X(0x46156be3), X(0x4655280e), X(0x4694d8b2), + X(0x46d47d82), X(0x4714162f), X(0x4753a26d), X(0x479321ef), + X(0x47d29466), X(0x4811f987), X(0x48515104), X(0x48909a91), + X(0x48cfd5e1), X(0x490f02a7), X(0x494e2098), X(0x498d2f66), + X(0x49cc2ec7), X(0x4a0b1e6f), X(0x4a49fe11), X(0x4a88cd62), + X(0x4ac78c18), X(0x4b0639e6), X(0x4b44d683), X(0x4b8361a2), + X(0x4bc1dafa), X(0x4c004241), X(0x4c3e972c), X(0x4c7cd970), + X(0x4cbb08c5), X(0x4cf924e1), X(0x4d372d7a), X(0x4d752247), + X(0x4db30300), X(0x4df0cf5a), X(0x4e2e870f), X(0x4e6c29d6), + X(0x4ea9b766), X(0x4ee72f78), X(0x4f2491c4), X(0x4f61de02), + X(0x4f9f13ec), X(0x4fdc333b), X(0x50193ba8), X(0x50562ced), + X(0x509306c3), X(0x50cfc8e5), X(0x510c730d), X(0x514904f6), + X(0x51857e5a), X(0x51c1def5), X(0x51fe2682), X(0x523a54bc), + X(0x52766961), X(0x52b2642c), X(0x52ee44d9), X(0x532a0b26), + X(0x5365b6d0), X(0x53a14793), X(0x53dcbd2f), X(0x54181760), + X(0x545355e5), X(0x548e787d), X(0x54c97ee6), X(0x550468e1), + X(0x553f362c), X(0x5579e687), X(0x55b479b3), X(0x55eeef70), + X(0x5629477f), X(0x566381a1), X(0x569d9d97), X(0x56d79b24), + X(0x57117a0a), X(0x574b3a0a), X(0x5784dae9), X(0x57be5c69), + X(0x57f7be4d), X(0x5831005a), X(0x586a2254), X(0x58a32400), + X(0x58dc0522), X(0x5914c57f), X(0x594d64de), X(0x5985e305), + X(0x59be3fba), X(0x59f67ac3), X(0x5a2e93e9), X(0x5a668af2), + X(0x5a9e5fa6), X(0x5ad611ce), X(0x5b0da133), X(0x5b450d9d), + X(0x5b7c56d7), X(0x5bb37ca9), X(0x5bea7ede), X(0x5c215d41), + X(0x5c58179d), X(0x5c8eadbe), X(0x5cc51f6f), X(0x5cfb6c7c), + X(0x5d3194b2), X(0x5d6797de), X(0x5d9d75cf), X(0x5dd32e51), + X(0x5e08c132), X(0x5e3e2e43), X(0x5e737551), X(0x5ea8962d), + X(0x5edd90a7), X(0x5f12648e), X(0x5f4711b4), X(0x5f7b97ea), + X(0x5faff702), X(0x5fe42ece), X(0x60183f20), X(0x604c27cc), + X(0x607fe8a6), X(0x60b38180), X(0x60e6f22f), X(0x611a3a89), + X(0x614d5a62), X(0x61805190), X(0x61b31fe9), X(0x61e5c545), + X(0x62184179), X(0x624a945d), X(0x627cbdca), X(0x62aebd98), + X(0x62e0939f), X(0x63123fba), X(0x6343c1c1), X(0x6375198f), + X(0x63a646ff), X(0x63d749ec), X(0x64082232), X(0x6438cfad), + X(0x64695238), X(0x6499a9b3), X(0x64c9d5f9), X(0x64f9d6ea), + X(0x6529ac63), X(0x65595643), X(0x6588d46a), X(0x65b826b8), + X(0x65e74d0e), X(0x6616474b), X(0x66451552), X(0x6673b704), + X(0x66a22c44), X(0x66d074f4), X(0x66fe90f8), X(0x672c8033), + X(0x675a428a), X(0x6787d7e1), X(0x67b5401f), X(0x67e27b27), + X(0x680f88e1), X(0x683c6934), X(0x68691c05), X(0x6895a13e), + X(0x68c1f8c7), X(0x68ee2287), X(0x691a1e68), X(0x6945ec54), + X(0x69718c35), X(0x699cfdf5), X(0x69c8417f), X(0x69f356c0), + X(0x6a1e3da3), X(0x6a48f615), X(0x6a738002), X(0x6a9ddb5a), + X(0x6ac80808), X(0x6af205fd), X(0x6b1bd526), X(0x6b457575), + X(0x6b6ee6d8), X(0x6b982940), X(0x6bc13c9f), X(0x6bea20e5), + X(0x6c12d605), X(0x6c3b5bf1), X(0x6c63b29c), X(0x6c8bd9fb), + X(0x6cb3d200), X(0x6cdb9aa0), X(0x6d0333d0), X(0x6d2a9d86), + X(0x6d51d7b7), X(0x6d78e25a), X(0x6d9fbd67), X(0x6dc668d3), + X(0x6dece498), X(0x6e1330ad), X(0x6e394d0c), X(0x6e5f39ae), + X(0x6e84f68d), X(0x6eaa83a2), X(0x6ecfe0ea), X(0x6ef50e5e), + X(0x6f1a0bfc), X(0x6f3ed9bf), X(0x6f6377a4), X(0x6f87e5a8), + X(0x6fac23c9), X(0x6fd03206), X(0x6ff4105c), X(0x7017becc), + X(0x703b3d54), X(0x705e8bf5), X(0x7081aaaf), X(0x70a49984), + X(0x70c75874), X(0x70e9e783), X(0x710c46b2), X(0x712e7605), + X(0x7150757f), X(0x71724523), X(0x7193e4f6), X(0x71b554fd), + X(0x71d6953e), X(0x71f7a5bd), X(0x72188681), X(0x72393792), + X(0x7259b8f5), X(0x727a0ab2), X(0x729a2cd2), X(0x72ba1f5d), + X(0x72d9e25c), X(0x72f975d8), X(0x7318d9db), X(0x73380e6f), + X(0x735713a0), X(0x7375e978), X(0x73949003), X(0x73b3074c), + X(0x73d14f61), X(0x73ef684f), X(0x740d5222), X(0x742b0ce9), + X(0x744898b1), X(0x7465f589), X(0x74832381), X(0x74a022a8), + X(0x74bcf30e), X(0x74d994c3), X(0x74f607d8), X(0x75124c5f), + X(0x752e6268), X(0x754a4a05), X(0x7566034b), X(0x75818e4a), + X(0x759ceb16), X(0x75b819c4), X(0x75d31a66), X(0x75eded12), + X(0x760891dc), X(0x762308da), X(0x763d5221), X(0x76576dc8), + X(0x76715be4), X(0x768b1c8c), X(0x76a4afd9), X(0x76be15e0), + X(0x76d74ebb), X(0x76f05a82), X(0x7709394d), X(0x7721eb35), + X(0x773a7054), X(0x7752c8c4), X(0x776af49f), X(0x7782f400), + X(0x779ac701), X(0x77b26dbd), X(0x77c9e851), X(0x77e136d8), + X(0x77f8596f), X(0x780f5032), X(0x78261b3f), X(0x783cbab2), + X(0x78532eaa), X(0x78697745), X(0x787f94a0), X(0x789586db), + X(0x78ab4e15), X(0x78c0ea6d), X(0x78d65c03), X(0x78eba2f7), + X(0x7900bf68), X(0x7915b179), X(0x792a7949), X(0x793f16fb), + X(0x79538aaf), X(0x7967d488), X(0x797bf4a8), X(0x798feb31), + X(0x79a3b846), X(0x79b75c0a), X(0x79cad6a1), X(0x79de282e), + X(0x79f150d5), X(0x7a0450bb), X(0x7a172803), X(0x7a29d6d3), + X(0x7a3c5d50), X(0x7a4ebb9f), X(0x7a60f1e6), X(0x7a73004a), + X(0x7a84e6f2), X(0x7a96a604), X(0x7aa83da7), X(0x7ab9ae01), + X(0x7acaf73a), X(0x7adc1979), X(0x7aed14e6), X(0x7afde9a8), + X(0x7b0e97e8), X(0x7b1f1fcd), X(0x7b2f8182), X(0x7b3fbd2d), + X(0x7b4fd2f9), X(0x7b5fc30f), X(0x7b6f8d98), X(0x7b7f32bd), + X(0x7b8eb2a9), X(0x7b9e0d85), X(0x7bad437d), X(0x7bbc54b9), + X(0x7bcb4166), X(0x7bda09ae), X(0x7be8adbc), X(0x7bf72dbc), + X(0x7c0589d8), X(0x7c13c23d), X(0x7c21d716), X(0x7c2fc88f), + X(0x7c3d96d5), X(0x7c4b4214), X(0x7c58ca78), X(0x7c66302d), + X(0x7c737362), X(0x7c809443), X(0x7c8d92fc), X(0x7c9a6fbc), + X(0x7ca72aaf), X(0x7cb3c404), X(0x7cc03be8), X(0x7ccc9288), + X(0x7cd8c814), X(0x7ce4dcb9), X(0x7cf0d0a5), X(0x7cfca406), + X(0x7d08570c), X(0x7d13e9e5), X(0x7d1f5cbf), X(0x7d2aafca), + X(0x7d35e335), X(0x7d40f72e), X(0x7d4bebe4), X(0x7d56c188), + X(0x7d617848), X(0x7d6c1054), X(0x7d7689db), X(0x7d80e50e), + X(0x7d8b221b), X(0x7d954133), X(0x7d9f4286), X(0x7da92643), + X(0x7db2ec9b), X(0x7dbc95bd), X(0x7dc621da), X(0x7dcf9123), + X(0x7dd8e3c6), X(0x7de219f6), X(0x7deb33e2), X(0x7df431ba), + X(0x7dfd13af), X(0x7e05d9f2), X(0x7e0e84b4), X(0x7e171424), + X(0x7e1f8874), X(0x7e27e1d4), X(0x7e302074), X(0x7e384487), + X(0x7e404e3c), X(0x7e483dc4), X(0x7e501350), X(0x7e57cf11), + X(0x7e5f7138), X(0x7e66f9f4), X(0x7e6e6979), X(0x7e75bff5), + X(0x7e7cfd9a), X(0x7e842298), X(0x7e8b2f22), X(0x7e922366), + X(0x7e98ff97), X(0x7e9fc3e4), X(0x7ea6707f), X(0x7ead0598), + X(0x7eb38360), X(0x7eb9ea07), X(0x7ec039bf), X(0x7ec672b7), + X(0x7ecc9521), X(0x7ed2a12c), X(0x7ed8970a), X(0x7ede76ea), + X(0x7ee440fd), X(0x7ee9f573), X(0x7eef947d), X(0x7ef51e4b), + X(0x7efa930d), X(0x7efff2f2), X(0x7f053e2b), X(0x7f0a74e8), + X(0x7f0f9758), X(0x7f14a5ac), X(0x7f19a013), X(0x7f1e86bc), + X(0x7f2359d8), X(0x7f281995), X(0x7f2cc623), X(0x7f315fb1), + X(0x7f35e66e), X(0x7f3a5a8a), X(0x7f3ebc33), X(0x7f430b98), + X(0x7f4748e7), X(0x7f4b7450), X(0x7f4f8e01), X(0x7f539629), + X(0x7f578cf5), X(0x7f5b7293), X(0x7f5f4732), X(0x7f630b00), + X(0x7f66be2b), X(0x7f6a60df), X(0x7f6df34b), X(0x7f71759b), + X(0x7f74e7fe), X(0x7f784aa0), X(0x7f7b9daf), X(0x7f7ee156), + X(0x7f8215c3), X(0x7f853b22), X(0x7f88519f), X(0x7f8b5967), + X(0x7f8e52a6), X(0x7f913d87), X(0x7f941a36), X(0x7f96e8df), + X(0x7f99a9ad), X(0x7f9c5ccb), X(0x7f9f0265), X(0x7fa19aa5), + X(0x7fa425b5), X(0x7fa6a3c1), X(0x7fa914f3), X(0x7fab7974), + X(0x7fadd16f), X(0x7fb01d0d), X(0x7fb25c78), X(0x7fb48fd9), + X(0x7fb6b75a), X(0x7fb8d323), X(0x7fbae35d), X(0x7fbce831), + X(0x7fbee1c7), X(0x7fc0d047), X(0x7fc2b3d9), X(0x7fc48ca5), + X(0x7fc65ad3), X(0x7fc81e88), X(0x7fc9d7ee), X(0x7fcb872a), + X(0x7fcd2c63), X(0x7fcec7bf), X(0x7fd05966), X(0x7fd1e17c), + X(0x7fd36027), X(0x7fd4d58d), X(0x7fd641d3), X(0x7fd7a51e), + X(0x7fd8ff94), X(0x7fda5157), X(0x7fdb9a8e), X(0x7fdcdb5b), + X(0x7fde13e2), X(0x7fdf4448), X(0x7fe06caf), X(0x7fe18d3b), + X(0x7fe2a60e), X(0x7fe3b74b), X(0x7fe4c114), X(0x7fe5c38b), + X(0x7fe6bed2), X(0x7fe7b30a), X(0x7fe8a055), X(0x7fe986d4), + X(0x7fea66a7), X(0x7feb3ff0), X(0x7fec12cd), X(0x7fecdf5f), + X(0x7feda5c5), X(0x7fee6620), X(0x7fef208d), X(0x7fefd52c), + X(0x7ff0841c), X(0x7ff12d7a), X(0x7ff1d164), X(0x7ff26ff9), + X(0x7ff30955), X(0x7ff39d96), X(0x7ff42cd9), X(0x7ff4b739), + X(0x7ff53cd4), X(0x7ff5bdc5), X(0x7ff63a28), X(0x7ff6b217), + X(0x7ff725af), X(0x7ff7950a), X(0x7ff80043), X(0x7ff86773), + X(0x7ff8cab4), X(0x7ff92a21), X(0x7ff985d1), X(0x7ff9dddf), + X(0x7ffa3262), X(0x7ffa8374), X(0x7ffad12c), X(0x7ffb1ba1), + X(0x7ffb62ec), X(0x7ffba723), X(0x7ffbe85c), X(0x7ffc26b0), + X(0x7ffc6233), X(0x7ffc9afb), X(0x7ffcd11e), X(0x7ffd04b1), + X(0x7ffd35c9), X(0x7ffd647b), X(0x7ffd90da), X(0x7ffdbafa), + X(0x7ffde2f0), X(0x7ffe08ce), X(0x7ffe2ca7), X(0x7ffe4e8e), + X(0x7ffe6e95), X(0x7ffe8cce), X(0x7ffea94a), X(0x7ffec41b), + X(0x7ffedd52), X(0x7ffef4ff), X(0x7fff0b33), X(0x7fff1ffd), + X(0x7fff336e), X(0x7fff4593), X(0x7fff567d), X(0x7fff663a), + X(0x7fff74d8), X(0x7fff8265), X(0x7fff8eee), X(0x7fff9a81), + X(0x7fffa52b), X(0x7fffaef8), X(0x7fffb7f5), X(0x7fffc02d), + X(0x7fffc7ab), X(0x7fffce7c), X(0x7fffd4a9), X(0x7fffda3e), + X(0x7fffdf44), X(0x7fffe3c6), X(0x7fffe7cc), X(0x7fffeb60), + X(0x7fffee8a), X(0x7ffff153), X(0x7ffff3c4), X(0x7ffff5e3), + X(0x7ffff7b8), X(0x7ffff94b), X(0x7ffffaa1), X(0x7ffffbc1), + X(0x7ffffcb2), X(0x7ffffd78), X(0x7ffffe19), X(0x7ffffe9a), + X(0x7ffffeff), X(0x7fffff4e), X(0x7fffff89), X(0x7fffffb3), + X(0x7fffffd2), X(0x7fffffe6), X(0x7ffffff3), X(0x7ffffffa), + X(0x7ffffffe), X(0x7fffffff), X(0x7fffffff), X(0x7fffffff), +}; + +static const LOOKUP_T vwin4096[2048] = { + X(0x000001f0), X(0x00001171), X(0x00003072), X(0x00005ef5), + X(0x00009cf8), X(0x0000ea7c), X(0x00014780), X(0x0001b405), + X(0x0002300b), X(0x0002bb91), X(0x00035698), X(0x0004011e), + X(0x0004bb25), X(0x000584ac), X(0x00065db3), X(0x0007463a), + X(0x00083e41), X(0x000945c7), X(0x000a5ccc), X(0x000b8350), + X(0x000cb954), X(0x000dfed7), X(0x000f53d8), X(0x0010b857), + X(0x00122c55), X(0x0013afd1), X(0x001542ca), X(0x0016e541), + X(0x00189735), X(0x001a58a7), X(0x001c2995), X(0x001e09ff), + X(0x001ff9e6), X(0x0021f948), X(0x00240826), X(0x00262680), + X(0x00285454), X(0x002a91a3), X(0x002cde6c), X(0x002f3aaf), + X(0x0031a66b), X(0x003421a0), X(0x0036ac4f), X(0x00394675), + X(0x003bf014), X(0x003ea92a), X(0x004171b7), X(0x004449bb), + X(0x00473135), X(0x004a2824), X(0x004d2e8a), X(0x00504463), + X(0x005369b2), X(0x00569e74), X(0x0059e2aa), X(0x005d3652), + X(0x0060996d), X(0x00640bf9), X(0x00678df7), X(0x006b1f66), + X(0x006ec045), X(0x00727093), X(0x00763051), X(0x0079ff7d), + X(0x007dde16), X(0x0081cc1d), X(0x0085c991), X(0x0089d671), + X(0x008df2bc), X(0x00921e71), X(0x00965991), X(0x009aa41a), + X(0x009efe0c), X(0x00a36766), X(0x00a7e028), X(0x00ac6850), + X(0x00b0ffde), X(0x00b5a6d1), X(0x00ba5d28), X(0x00bf22e4), + X(0x00c3f802), X(0x00c8dc83), X(0x00cdd065), X(0x00d2d3a8), + X(0x00d7e64a), X(0x00dd084c), X(0x00e239ac), X(0x00e77a69), + X(0x00ecca83), X(0x00f229f9), X(0x00f798ca), X(0x00fd16f5), + X(0x0102a479), X(0x01084155), X(0x010ded89), X(0x0113a913), + X(0x011973f3), X(0x011f4e27), X(0x012537af), X(0x012b308a), + X(0x013138b7), X(0x01375035), X(0x013d7702), X(0x0143ad1f), + X(0x0149f289), X(0x01504741), X(0x0156ab44), X(0x015d1e92), + X(0x0163a12a), X(0x016a330b), X(0x0170d433), X(0x017784a3), + X(0x017e4458), X(0x01851351), X(0x018bf18e), X(0x0192df0d), + X(0x0199dbcd), X(0x01a0e7cd), X(0x01a8030c), X(0x01af2d89), + X(0x01b66743), X(0x01bdb038), X(0x01c50867), X(0x01cc6fd0), + X(0x01d3e670), X(0x01db6c47), X(0x01e30153), X(0x01eaa593), + X(0x01f25907), X(0x01fa1bac), X(0x0201ed81), X(0x0209ce86), + X(0x0211beb8), X(0x0219be17), X(0x0221cca2), X(0x0229ea56), + X(0x02321733), X(0x023a5337), X(0x02429e60), X(0x024af8af), + X(0x02536220), X(0x025bdab3), X(0x02646267), X(0x026cf93a), + X(0x02759f2a), X(0x027e5436), X(0x0287185d), X(0x028feb9d), + X(0x0298cdf4), X(0x02a1bf62), X(0x02aabfe5), X(0x02b3cf7b), + X(0x02bcee23), X(0x02c61bdb), X(0x02cf58a2), X(0x02d8a475), + X(0x02e1ff55), X(0x02eb693e), X(0x02f4e230), X(0x02fe6a29), + X(0x03080127), X(0x0311a729), X(0x031b5c2d), X(0x03252031), + X(0x032ef334), X(0x0338d534), X(0x0342c630), X(0x034cc625), + X(0x0356d512), X(0x0360f2f6), X(0x036b1fce), X(0x03755b99), + X(0x037fa655), X(0x038a0001), X(0x0394689a), X(0x039ee020), + X(0x03a9668f), X(0x03b3fbe6), X(0x03bea024), X(0x03c95347), + X(0x03d4154d), X(0x03dee633), X(0x03e9c5f9), X(0x03f4b49b), + X(0x03ffb219), X(0x040abe71), X(0x0415d9a0), X(0x042103a5), + X(0x042c3c7d), X(0x04378428), X(0x0442daa2), X(0x044e3fea), + X(0x0459b3fd), X(0x046536db), X(0x0470c880), X(0x047c68eb), + X(0x0488181a), X(0x0493d60b), X(0x049fa2bc), X(0x04ab7e2a), + X(0x04b76854), X(0x04c36137), X(0x04cf68d1), X(0x04db7f21), + X(0x04e7a424), X(0x04f3d7d8), X(0x05001a3b), X(0x050c6b4a), + X(0x0518cb04), X(0x05253966), X(0x0531b66e), X(0x053e421a), + X(0x054adc68), X(0x05578555), X(0x05643cdf), X(0x05710304), + X(0x057dd7c1), X(0x058abb15), X(0x0597acfd), X(0x05a4ad76), + X(0x05b1bc7f), X(0x05beda14), X(0x05cc0635), X(0x05d940dd), + X(0x05e68a0b), X(0x05f3e1bd), X(0x060147f0), X(0x060ebca1), + X(0x061c3fcf), X(0x0629d176), X(0x06377194), X(0x06452027), + X(0x0652dd2c), X(0x0660a8a2), X(0x066e8284), X(0x067c6ad1), + X(0x068a6186), X(0x069866a1), X(0x06a67a1e), X(0x06b49bfc), + X(0x06c2cc38), X(0x06d10acf), X(0x06df57bf), X(0x06edb304), + X(0x06fc1c9d), X(0x070a9487), X(0x07191abe), X(0x0727af40), + X(0x0736520b), X(0x0745031c), X(0x0753c270), X(0x07629004), + X(0x07716bd6), X(0x078055e2), X(0x078f4e26), X(0x079e549f), + X(0x07ad694b), X(0x07bc8c26), X(0x07cbbd2e), X(0x07dafc5f), + X(0x07ea49b7), X(0x07f9a533), X(0x08090ed1), X(0x0818868c), + X(0x08280c62), X(0x0837a051), X(0x08474255), X(0x0856f26b), + X(0x0866b091), X(0x08767cc3), X(0x088656fe), X(0x08963f3f), + X(0x08a63584), X(0x08b639c8), X(0x08c64c0a), X(0x08d66c45), + X(0x08e69a77), X(0x08f6d69d), X(0x090720b3), X(0x091778b7), + X(0x0927dea5), X(0x0938527a), X(0x0948d433), X(0x095963cc), + X(0x096a0143), X(0x097aac94), X(0x098b65bb), X(0x099c2cb6), + X(0x09ad0182), X(0x09bde41a), X(0x09ced47d), X(0x09dfd2a5), + X(0x09f0de90), X(0x0a01f83b), X(0x0a131fa3), X(0x0a2454c3), + X(0x0a359798), X(0x0a46e820), X(0x0a584656), X(0x0a69b237), + X(0x0a7b2bc0), X(0x0a8cb2ec), X(0x0a9e47ba), X(0x0aafea24), + X(0x0ac19a29), X(0x0ad357c3), X(0x0ae522ef), X(0x0af6fbab), + X(0x0b08e1f1), X(0x0b1ad5c0), X(0x0b2cd712), X(0x0b3ee5e5), + X(0x0b510234), X(0x0b632bfd), X(0x0b75633b), X(0x0b87a7eb), + X(0x0b99fa08), X(0x0bac5990), X(0x0bbec67e), X(0x0bd140cf), + X(0x0be3c87e), X(0x0bf65d89), X(0x0c08ffeb), X(0x0c1bafa1), + X(0x0c2e6ca6), X(0x0c4136f6), X(0x0c540e8f), X(0x0c66f36c), + X(0x0c79e588), X(0x0c8ce4e1), X(0x0c9ff172), X(0x0cb30b37), + X(0x0cc6322c), X(0x0cd9664d), X(0x0ceca797), X(0x0cfff605), + X(0x0d135193), X(0x0d26ba3d), X(0x0d3a2fff), X(0x0d4db2d5), + X(0x0d6142ba), X(0x0d74dfac), X(0x0d8889a5), X(0x0d9c40a1), + X(0x0db0049d), X(0x0dc3d593), X(0x0dd7b380), X(0x0deb9e60), + X(0x0dff962f), X(0x0e139ae7), X(0x0e27ac85), X(0x0e3bcb05), + X(0x0e4ff662), X(0x0e642e98), X(0x0e7873a2), X(0x0e8cc57d), + X(0x0ea12423), X(0x0eb58f91), X(0x0eca07c2), X(0x0ede8cb1), + X(0x0ef31e5b), X(0x0f07bcba), X(0x0f1c67cb), X(0x0f311f88), + X(0x0f45e3ee), X(0x0f5ab4f7), X(0x0f6f92a0), X(0x0f847ce3), + X(0x0f9973bc), X(0x0fae7726), X(0x0fc3871e), X(0x0fd8a39d), + X(0x0fedcca1), X(0x10030223), X(0x1018441f), X(0x102d9291), + X(0x1042ed74), X(0x105854c3), X(0x106dc879), X(0x10834892), + X(0x1098d508), X(0x10ae6dd8), X(0x10c412fc), X(0x10d9c46f), + X(0x10ef822d), X(0x11054c30), X(0x111b2274), X(0x113104f5), + X(0x1146f3ac), X(0x115cee95), X(0x1172f5ab), X(0x118908e9), + X(0x119f284a), X(0x11b553ca), X(0x11cb8b62), X(0x11e1cf0f), + X(0x11f81ecb), X(0x120e7a90), X(0x1224e25a), X(0x123b5624), + X(0x1251d5e9), X(0x126861a3), X(0x127ef94e), X(0x12959ce3), + X(0x12ac4c5f), X(0x12c307bb), X(0x12d9cef2), X(0x12f0a200), + X(0x130780df), X(0x131e6b8a), X(0x133561fa), X(0x134c642c), + X(0x1363721a), X(0x137a8bbe), X(0x1391b113), X(0x13a8e214), + X(0x13c01eba), X(0x13d76702), X(0x13eebae5), X(0x14061a5e), + X(0x141d8567), X(0x1434fbfb), X(0x144c7e14), X(0x14640bae), + X(0x147ba4c1), X(0x14934949), X(0x14aaf941), X(0x14c2b4a2), + X(0x14da7b67), X(0x14f24d8a), X(0x150a2b06), X(0x152213d5), + X(0x153a07f1), X(0x15520755), X(0x156a11fb), X(0x158227dd), + X(0x159a48f5), X(0x15b2753d), X(0x15caacb1), X(0x15e2ef49), + X(0x15fb3d01), X(0x161395d2), X(0x162bf9b6), X(0x164468a8), + X(0x165ce2a1), X(0x1675679c), X(0x168df793), X(0x16a69280), + X(0x16bf385c), X(0x16d7e922), X(0x16f0a4cc), X(0x17096b54), + X(0x17223cb4), X(0x173b18e5), X(0x1753ffe2), X(0x176cf1a5), + X(0x1785ee27), X(0x179ef562), X(0x17b80750), X(0x17d123eb), + X(0x17ea4b2d), X(0x18037d10), X(0x181cb98d), X(0x1836009e), + X(0x184f523c), X(0x1868ae63), X(0x1882150a), X(0x189b862c), + X(0x18b501c4), X(0x18ce87c9), X(0x18e81836), X(0x1901b305), + X(0x191b582f), X(0x193507ad), X(0x194ec17a), X(0x1968858f), + X(0x198253e5), X(0x199c2c75), X(0x19b60f3a), X(0x19cffc2d), + X(0x19e9f347), X(0x1a03f482), X(0x1a1dffd7), X(0x1a381540), + X(0x1a5234b5), X(0x1a6c5e31), X(0x1a8691ac), X(0x1aa0cf21), + X(0x1abb1687), X(0x1ad567da), X(0x1aefc311), X(0x1b0a2826), + X(0x1b249712), X(0x1b3f0fd0), X(0x1b599257), X(0x1b741ea1), + X(0x1b8eb4a7), X(0x1ba95462), X(0x1bc3fdcd), X(0x1bdeb0de), + X(0x1bf96d91), X(0x1c1433dd), X(0x1c2f03bc), X(0x1c49dd27), + X(0x1c64c017), X(0x1c7fac85), X(0x1c9aa269), X(0x1cb5a1be), + X(0x1cd0aa7c), X(0x1cebbc9c), X(0x1d06d816), X(0x1d21fce4), + X(0x1d3d2aff), X(0x1d586260), X(0x1d73a2fe), X(0x1d8eecd4), + X(0x1daa3fda), X(0x1dc59c09), X(0x1de1015a), X(0x1dfc6fc5), + X(0x1e17e743), X(0x1e3367cd), X(0x1e4ef15b), X(0x1e6a83e7), + X(0x1e861f6a), X(0x1ea1c3da), X(0x1ebd7133), X(0x1ed9276b), + X(0x1ef4e67c), X(0x1f10ae5e), X(0x1f2c7f0a), X(0x1f485879), + X(0x1f643aa2), X(0x1f80257f), X(0x1f9c1908), X(0x1fb81536), + X(0x1fd41a00), X(0x1ff02761), X(0x200c3d4f), X(0x20285bc3), + X(0x204482b7), X(0x2060b221), X(0x207ce9fb), X(0x20992a3e), + X(0x20b572e0), X(0x20d1c3dc), X(0x20ee1d28), X(0x210a7ebe), + X(0x2126e895), X(0x21435aa6), X(0x215fd4ea), X(0x217c5757), + X(0x2198e1e8), X(0x21b57493), X(0x21d20f51), X(0x21eeb21b), + X(0x220b5ce7), X(0x22280fb0), X(0x2244ca6c), X(0x22618d13), + X(0x227e579f), X(0x229b2a06), X(0x22b80442), X(0x22d4e649), + X(0x22f1d015), X(0x230ec19d), X(0x232bbad9), X(0x2348bbc1), + X(0x2365c44c), X(0x2382d474), X(0x239fec30), X(0x23bd0b78), + X(0x23da3244), X(0x23f7608b), X(0x24149646), X(0x2431d36c), + X(0x244f17f5), X(0x246c63da), X(0x2489b711), X(0x24a71193), + X(0x24c47358), X(0x24e1dc57), X(0x24ff4c88), X(0x251cc3e2), + X(0x253a425e), X(0x2557c7f4), X(0x2575549a), X(0x2592e848), + X(0x25b082f7), X(0x25ce249e), X(0x25ebcd34), X(0x26097cb2), + X(0x2627330e), X(0x2644f040), X(0x2662b441), X(0x26807f07), + X(0x269e5089), X(0x26bc28c1), X(0x26da07a4), X(0x26f7ed2b), + X(0x2715d94d), X(0x2733cc02), X(0x2751c540), X(0x276fc500), + X(0x278dcb39), X(0x27abd7e2), X(0x27c9eaf3), X(0x27e80463), + X(0x28062429), X(0x28244a3e), X(0x28427697), X(0x2860a92d), + X(0x287ee1f7), X(0x289d20eb), X(0x28bb6603), X(0x28d9b134), + X(0x28f80275), X(0x291659c0), X(0x2934b709), X(0x29531a49), + X(0x29718378), X(0x298ff28b), X(0x29ae677b), X(0x29cce23e), + X(0x29eb62cb), X(0x2a09e91b), X(0x2a287523), X(0x2a4706dc), + X(0x2a659e3c), X(0x2a843b39), X(0x2aa2ddcd), X(0x2ac185ec), + X(0x2ae0338f), X(0x2afee6ad), X(0x2b1d9f3c), X(0x2b3c5d33), + X(0x2b5b208b), X(0x2b79e939), X(0x2b98b734), X(0x2bb78a74), + X(0x2bd662ef), X(0x2bf5409d), X(0x2c142374), X(0x2c330b6b), + X(0x2c51f87a), X(0x2c70ea97), X(0x2c8fe1b9), X(0x2caeddd6), + X(0x2ccddee7), X(0x2cece4e1), X(0x2d0befbb), X(0x2d2aff6d), + X(0x2d4a13ec), X(0x2d692d31), X(0x2d884b32), X(0x2da76de4), + X(0x2dc69540), X(0x2de5c13d), X(0x2e04f1d0), X(0x2e2426f0), + X(0x2e436095), X(0x2e629eb4), X(0x2e81e146), X(0x2ea1283f), + X(0x2ec07398), X(0x2edfc347), X(0x2eff1742), X(0x2f1e6f80), + X(0x2f3dcbf8), X(0x2f5d2ca0), X(0x2f7c916f), X(0x2f9bfa5c), + X(0x2fbb675d), X(0x2fdad869), X(0x2ffa4d76), X(0x3019c67b), + X(0x3039436f), X(0x3058c448), X(0x307848fc), X(0x3097d183), + X(0x30b75dd3), X(0x30d6ede2), X(0x30f681a6), X(0x31161917), + X(0x3135b42b), X(0x315552d8), X(0x3174f514), X(0x31949ad7), + X(0x31b44417), X(0x31d3f0ca), X(0x31f3a0e6), X(0x32135462), + X(0x32330b35), X(0x3252c555), X(0x327282b7), X(0x32924354), + X(0x32b20720), X(0x32d1ce13), X(0x32f19823), X(0x33116546), + X(0x33313573), X(0x3351089f), X(0x3370dec2), X(0x3390b7d1), + X(0x33b093c3), X(0x33d0728f), X(0x33f05429), X(0x3410388a), + X(0x34301fa7), X(0x34500977), X(0x346ff5ef), X(0x348fe506), + X(0x34afd6b3), X(0x34cfcaeb), X(0x34efc1a5), X(0x350fbad7), + X(0x352fb678), X(0x354fb47d), X(0x356fb4dd), X(0x358fb78e), + X(0x35afbc86), X(0x35cfc3bc), X(0x35efcd25), X(0x360fd8b8), + X(0x362fe66c), X(0x364ff636), X(0x3670080c), X(0x36901be5), + X(0x36b031b7), X(0x36d04978), X(0x36f0631e), X(0x37107ea0), + X(0x37309bf3), X(0x3750bb0e), X(0x3770dbe6), X(0x3790fe73), + X(0x37b122aa), X(0x37d14881), X(0x37f16fee), X(0x381198e8), + X(0x3831c365), X(0x3851ef5a), X(0x38721cbe), X(0x38924b87), + X(0x38b27bac), X(0x38d2ad21), X(0x38f2dfde), X(0x391313d8), + X(0x39334906), X(0x39537f5d), X(0x3973b6d4), X(0x3993ef60), + X(0x39b428f9), X(0x39d46393), X(0x39f49f25), X(0x3a14dba6), + X(0x3a35190a), X(0x3a555748), X(0x3a759657), X(0x3a95d62c), + X(0x3ab616be), X(0x3ad65801), X(0x3af699ed), X(0x3b16dc78), + X(0x3b371f97), X(0x3b576341), X(0x3b77a76c), X(0x3b97ec0d), + X(0x3bb8311b), X(0x3bd8768b), X(0x3bf8bc55), X(0x3c19026d), + X(0x3c3948ca), X(0x3c598f62), X(0x3c79d62b), X(0x3c9a1d1b), + X(0x3cba6428), X(0x3cdaab48), X(0x3cfaf271), X(0x3d1b3999), + X(0x3d3b80b6), X(0x3d5bc7be), X(0x3d7c0ea8), X(0x3d9c5569), + X(0x3dbc9bf7), X(0x3ddce248), X(0x3dfd2852), X(0x3e1d6e0c), + X(0x3e3db36c), X(0x3e5df866), X(0x3e7e3cf2), X(0x3e9e8106), + X(0x3ebec497), X(0x3edf079b), X(0x3eff4a09), X(0x3f1f8bd7), + X(0x3f3fccfa), X(0x3f600d69), X(0x3f804d1a), X(0x3fa08c02), + X(0x3fc0ca19), X(0x3fe10753), X(0x400143a7), X(0x40217f0a), + X(0x4041b974), X(0x4061f2da), X(0x40822b32), X(0x40a26272), + X(0x40c29891), X(0x40e2cd83), X(0x41030140), X(0x412333bd), + X(0x414364f1), X(0x416394d2), X(0x4183c355), X(0x41a3f070), + X(0x41c41c1b), X(0x41e4464a), X(0x42046ef4), X(0x42249610), + X(0x4244bb92), X(0x4264df72), X(0x428501a5), X(0x42a52222), + X(0x42c540de), X(0x42e55dd0), X(0x430578ed), X(0x4325922d), + X(0x4345a985), X(0x4365beeb), X(0x4385d255), X(0x43a5e3ba), + X(0x43c5f30f), X(0x43e6004b), X(0x44060b65), X(0x44261451), + X(0x44461b07), X(0x44661f7c), X(0x448621a7), X(0x44a6217d), + X(0x44c61ef6), X(0x44e61a07), X(0x450612a6), X(0x452608ca), + X(0x4545fc69), X(0x4565ed79), X(0x4585dbf1), X(0x45a5c7c6), + X(0x45c5b0ef), X(0x45e59761), X(0x46057b15), X(0x46255bfe), + X(0x46453a15), X(0x4665154f), X(0x4684eda2), X(0x46a4c305), + X(0x46c4956e), X(0x46e464d3), X(0x4704312b), X(0x4723fa6c), + X(0x4743c08d), X(0x47638382), X(0x47834344), X(0x47a2ffc9), + X(0x47c2b906), X(0x47e26ef2), X(0x48022183), X(0x4821d0b1), + X(0x48417c71), X(0x486124b9), X(0x4880c981), X(0x48a06abe), + X(0x48c00867), X(0x48dfa272), X(0x48ff38d6), X(0x491ecb8a), + X(0x493e5a84), X(0x495de5b9), X(0x497d6d22), X(0x499cf0b4), + X(0x49bc7066), X(0x49dbec2e), X(0x49fb6402), X(0x4a1ad7db), + X(0x4a3a47ad), X(0x4a59b370), X(0x4a791b1a), X(0x4a987ea1), + X(0x4ab7ddfd), X(0x4ad73924), X(0x4af6900c), X(0x4b15e2ad), + X(0x4b3530fc), X(0x4b547af1), X(0x4b73c082), X(0x4b9301a6), + X(0x4bb23e53), X(0x4bd17681), X(0x4bf0aa25), X(0x4c0fd937), + X(0x4c2f03ae), X(0x4c4e297f), X(0x4c6d4aa3), X(0x4c8c670f), + X(0x4cab7eba), X(0x4cca919c), X(0x4ce99fab), X(0x4d08a8de), + X(0x4d27ad2c), X(0x4d46ac8b), X(0x4d65a6f3), X(0x4d849c5a), + X(0x4da38cb7), X(0x4dc27802), X(0x4de15e31), X(0x4e003f3a), + X(0x4e1f1b16), X(0x4e3df1ba), X(0x4e5cc31e), X(0x4e7b8f3a), + X(0x4e9a5603), X(0x4eb91771), X(0x4ed7d37b), X(0x4ef68a18), + X(0x4f153b3f), X(0x4f33e6e7), X(0x4f528d08), X(0x4f712d97), + X(0x4f8fc88e), X(0x4fae5de1), X(0x4fcced8a), X(0x4feb777f), + X(0x5009fbb6), X(0x50287a28), X(0x5046f2cc), X(0x50656598), + X(0x5083d284), X(0x50a23988), X(0x50c09a9a), X(0x50def5b1), + X(0x50fd4ac7), X(0x511b99d0), X(0x5139e2c5), X(0x5158259e), + X(0x51766251), X(0x519498d6), X(0x51b2c925), X(0x51d0f334), + X(0x51ef16fb), X(0x520d3473), X(0x522b4b91), X(0x52495c4e), + X(0x526766a2), X(0x52856a83), X(0x52a367e9), X(0x52c15ecd), + X(0x52df4f24), X(0x52fd38e8), X(0x531b1c10), X(0x5338f892), + X(0x5356ce68), X(0x53749d89), X(0x539265eb), X(0x53b02788), + X(0x53cde257), X(0x53eb964f), X(0x54094369), X(0x5426e99c), + X(0x544488df), X(0x5462212c), X(0x547fb279), X(0x549d3cbe), + X(0x54babff4), X(0x54d83c12), X(0x54f5b110), X(0x55131ee7), + X(0x5530858d), X(0x554de4fc), X(0x556b3d2a), X(0x55888e11), + X(0x55a5d7a8), X(0x55c319e7), X(0x55e054c7), X(0x55fd883f), + X(0x561ab447), X(0x5637d8d8), X(0x5654f5ea), X(0x56720b75), + X(0x568f1971), X(0x56ac1fd7), X(0x56c91e9e), X(0x56e615c0), + X(0x57030534), X(0x571fecf2), X(0x573cccf3), X(0x5759a530), + X(0x577675a0), X(0x57933e3c), X(0x57affefd), X(0x57ccb7db), + X(0x57e968ce), X(0x580611cf), X(0x5822b2d6), X(0x583f4bdd), + X(0x585bdcdb), X(0x587865c9), X(0x5894e69f), X(0x58b15f57), + X(0x58cdcfe9), X(0x58ea384e), X(0x5906987d), X(0x5922f071), + X(0x593f4022), X(0x595b8788), X(0x5977c69c), X(0x5993fd57), + X(0x59b02bb2), X(0x59cc51a6), X(0x59e86f2c), X(0x5a04843c), + X(0x5a2090d0), X(0x5a3c94e0), X(0x5a589065), X(0x5a748359), + X(0x5a906db4), X(0x5aac4f70), X(0x5ac82884), X(0x5ae3f8ec), + X(0x5affc09f), X(0x5b1b7f97), X(0x5b3735cd), X(0x5b52e33a), + X(0x5b6e87d8), X(0x5b8a239f), X(0x5ba5b689), X(0x5bc1408f), + X(0x5bdcc1aa), X(0x5bf839d5), X(0x5c13a907), X(0x5c2f0f3b), + X(0x5c4a6c6a), X(0x5c65c08d), X(0x5c810b9e), X(0x5c9c4d97), + X(0x5cb78670), X(0x5cd2b623), X(0x5ceddcaa), X(0x5d08f9ff), + X(0x5d240e1b), X(0x5d3f18f8), X(0x5d5a1a8f), X(0x5d7512da), + X(0x5d9001d3), X(0x5daae773), X(0x5dc5c3b5), X(0x5de09692), + X(0x5dfb6004), X(0x5e162004), X(0x5e30d68d), X(0x5e4b8399), + X(0x5e662721), X(0x5e80c11f), X(0x5e9b518e), X(0x5eb5d867), + X(0x5ed055a4), X(0x5eeac940), X(0x5f053334), X(0x5f1f937b), + X(0x5f39ea0f), X(0x5f5436ea), X(0x5f6e7a06), X(0x5f88b35d), + X(0x5fa2e2e9), X(0x5fbd08a6), X(0x5fd7248d), X(0x5ff13698), + X(0x600b3ec2), X(0x60253d05), X(0x603f315b), X(0x60591bc0), + X(0x6072fc2d), X(0x608cd29e), X(0x60a69f0b), X(0x60c06171), + X(0x60da19ca), X(0x60f3c80f), X(0x610d6c3d), X(0x6127064d), + X(0x6140963a), X(0x615a1bff), X(0x61739797), X(0x618d08fc), + X(0x61a67029), X(0x61bfcd1a), X(0x61d91fc8), X(0x61f2682f), + X(0x620ba64a), X(0x6224da13), X(0x623e0386), X(0x6257229d), + X(0x62703754), X(0x628941a6), X(0x62a2418e), X(0x62bb3706), + X(0x62d4220a), X(0x62ed0296), X(0x6305d8a3), X(0x631ea42f), + X(0x63376533), X(0x63501bab), X(0x6368c793), X(0x638168e5), + X(0x6399ff9e), X(0x63b28bb8), X(0x63cb0d2f), X(0x63e383ff), + X(0x63fbf022), X(0x64145195), X(0x642ca853), X(0x6444f457), + X(0x645d359e), X(0x64756c22), X(0x648d97e0), X(0x64a5b8d3), + X(0x64bdcef6), X(0x64d5da47), X(0x64eddabf), X(0x6505d05c), + X(0x651dbb19), X(0x65359af2), X(0x654d6fe3), X(0x656539e7), + X(0x657cf8fb), X(0x6594ad1b), X(0x65ac5643), X(0x65c3f46e), + X(0x65db8799), X(0x65f30fc0), X(0x660a8ce0), X(0x6621fef3), + X(0x663965f7), X(0x6650c1e7), X(0x666812c1), X(0x667f5880), + X(0x66969320), X(0x66adc29e), X(0x66c4e6f7), X(0x66dc0026), + X(0x66f30e28), X(0x670a10fa), X(0x67210898), X(0x6737f4ff), + X(0x674ed62b), X(0x6765ac19), X(0x677c76c5), X(0x6793362c), + X(0x67a9ea4b), X(0x67c0931f), X(0x67d730a3), X(0x67edc2d6), + X(0x680449b3), X(0x681ac538), X(0x68313562), X(0x68479a2d), + X(0x685df396), X(0x6874419b), X(0x688a8438), X(0x68a0bb6a), + X(0x68b6e72e), X(0x68cd0782), X(0x68e31c63), X(0x68f925cd), + X(0x690f23be), X(0x69251633), X(0x693afd29), X(0x6950d89e), + X(0x6966a88f), X(0x697c6cf8), X(0x699225d9), X(0x69a7d32d), + X(0x69bd74f3), X(0x69d30b27), X(0x69e895c8), X(0x69fe14d2), + X(0x6a138844), X(0x6a28f01b), X(0x6a3e4c54), X(0x6a539ced), + X(0x6a68e1e4), X(0x6a7e1b37), X(0x6a9348e3), X(0x6aa86ae6), + X(0x6abd813d), X(0x6ad28be7), X(0x6ae78ae2), X(0x6afc7e2b), + X(0x6b1165c0), X(0x6b26419f), X(0x6b3b11c7), X(0x6b4fd634), + X(0x6b648ee6), X(0x6b793bda), X(0x6b8ddd0e), X(0x6ba27281), + X(0x6bb6fc31), X(0x6bcb7a1b), X(0x6bdfec3e), X(0x6bf45299), + X(0x6c08ad29), X(0x6c1cfbed), X(0x6c313ee4), X(0x6c45760a), + X(0x6c59a160), X(0x6c6dc0e4), X(0x6c81d493), X(0x6c95dc6d), + X(0x6ca9d86f), X(0x6cbdc899), X(0x6cd1acea), X(0x6ce5855f), + X(0x6cf951f7), X(0x6d0d12b1), X(0x6d20c78c), X(0x6d347087), + X(0x6d480da0), X(0x6d5b9ed6), X(0x6d6f2427), X(0x6d829d94), + X(0x6d960b1a), X(0x6da96cb9), X(0x6dbcc270), X(0x6dd00c3c), + X(0x6de34a1f), X(0x6df67c16), X(0x6e09a221), X(0x6e1cbc3f), + X(0x6e2fca6e), X(0x6e42ccaf), X(0x6e55c300), X(0x6e68ad60), + X(0x6e7b8bd0), X(0x6e8e5e4d), X(0x6ea124d8), X(0x6eb3df70), + X(0x6ec68e13), X(0x6ed930c3), X(0x6eebc77d), X(0x6efe5242), + X(0x6f10d111), X(0x6f2343e9), X(0x6f35aacb), X(0x6f4805b5), + X(0x6f5a54a8), X(0x6f6c97a2), X(0x6f7ecea4), X(0x6f90f9ae), + X(0x6fa318be), X(0x6fb52bd6), X(0x6fc732f4), X(0x6fd92e19), + X(0x6feb1d44), X(0x6ffd0076), X(0x700ed7ad), X(0x7020a2eb), + X(0x7032622f), X(0x7044157a), X(0x7055bcca), X(0x70675821), + X(0x7078e77e), X(0x708a6ae2), X(0x709be24c), X(0x70ad4dbd), + X(0x70bead36), X(0x70d000b5), X(0x70e1483d), X(0x70f283cc), + X(0x7103b363), X(0x7114d704), X(0x7125eead), X(0x7136fa60), + X(0x7147fa1c), X(0x7158ede4), X(0x7169d5b6), X(0x717ab193), + X(0x718b817d), X(0x719c4573), X(0x71acfd76), X(0x71bda988), + X(0x71ce49a8), X(0x71deddd7), X(0x71ef6617), X(0x71ffe267), + X(0x721052ca), X(0x7220b73e), X(0x72310fc6), X(0x72415c62), + X(0x72519d14), X(0x7261d1db), X(0x7271faba), X(0x728217b1), + X(0x729228c0), X(0x72a22dea), X(0x72b22730), X(0x72c21491), + X(0x72d1f611), X(0x72e1cbaf), X(0x72f1956c), X(0x7301534c), + X(0x7311054d), X(0x7320ab72), X(0x733045bc), X(0x733fd42d), + X(0x734f56c5), X(0x735ecd86), X(0x736e3872), X(0x737d9789), + X(0x738ceacf), X(0x739c3243), X(0x73ab6de7), X(0x73ba9dbe), + X(0x73c9c1c8), X(0x73d8da08), X(0x73e7e67f), X(0x73f6e72e), + X(0x7405dc17), X(0x7414c53c), X(0x7423a29f), X(0x74327442), + X(0x74413a26), X(0x744ff44d), X(0x745ea2b9), X(0x746d456c), + X(0x747bdc68), X(0x748a67ae), X(0x7498e741), X(0x74a75b23), + X(0x74b5c356), X(0x74c41fdb), X(0x74d270b6), X(0x74e0b5e7), + X(0x74eeef71), X(0x74fd1d57), X(0x750b3f9a), X(0x7519563c), + X(0x75276140), X(0x753560a8), X(0x75435477), X(0x75513cae), + X(0x755f1951), X(0x756cea60), X(0x757aafdf), X(0x758869d1), + X(0x75961837), X(0x75a3bb14), X(0x75b1526a), X(0x75bede3c), + X(0x75cc5e8d), X(0x75d9d35f), X(0x75e73cb5), X(0x75f49a91), + X(0x7601ecf6), X(0x760f33e6), X(0x761c6f65), X(0x76299f74), + X(0x7636c417), X(0x7643dd51), X(0x7650eb24), X(0x765ded93), + X(0x766ae4a0), X(0x7677d050), X(0x7684b0a4), X(0x7691859f), + X(0x769e4f45), X(0x76ab0d98), X(0x76b7c09c), X(0x76c46852), + X(0x76d104bf), X(0x76dd95e6), X(0x76ea1bc9), X(0x76f6966b), + X(0x770305d0), X(0x770f69fb), X(0x771bc2ef), X(0x772810af), + X(0x7734533e), X(0x77408aa0), X(0x774cb6d7), X(0x7758d7e8), + X(0x7764edd5), X(0x7770f8a2), X(0x777cf852), X(0x7788ece8), + X(0x7794d668), X(0x77a0b4d5), X(0x77ac8833), X(0x77b85085), + X(0x77c40dce), X(0x77cfc013), X(0x77db6756), X(0x77e7039b), + X(0x77f294e6), X(0x77fe1b3b), X(0x7809969c), X(0x7815070e), + X(0x78206c93), X(0x782bc731), X(0x783716ea), X(0x78425bc3), + X(0x784d95be), X(0x7858c4e1), X(0x7863e92d), X(0x786f02a8), + X(0x787a1156), X(0x78851539), X(0x78900e56), X(0x789afcb1), + X(0x78a5e04d), X(0x78b0b92f), X(0x78bb875b), X(0x78c64ad4), + X(0x78d1039e), X(0x78dbb1be), X(0x78e65537), X(0x78f0ee0e), + X(0x78fb7c46), X(0x7905ffe4), X(0x791078ec), X(0x791ae762), + X(0x79254b4a), X(0x792fa4a7), X(0x7939f380), X(0x794437d7), + X(0x794e71b0), X(0x7958a111), X(0x7962c5fd), X(0x796ce078), + X(0x7976f087), X(0x7980f62f), X(0x798af173), X(0x7994e258), + X(0x799ec8e2), X(0x79a8a515), X(0x79b276f7), X(0x79bc3e8b), + X(0x79c5fbd6), X(0x79cfaedc), X(0x79d957a2), X(0x79e2f62c), + X(0x79ec8a7f), X(0x79f6149f), X(0x79ff9492), X(0x7a090a5a), + X(0x7a1275fe), X(0x7a1bd781), X(0x7a252ee9), X(0x7a2e7c39), + X(0x7a37bf77), X(0x7a40f8a7), X(0x7a4a27ce), X(0x7a534cf0), + X(0x7a5c6813), X(0x7a65793b), X(0x7a6e806d), X(0x7a777dad), + X(0x7a807100), X(0x7a895a6b), X(0x7a9239f4), X(0x7a9b0f9e), + X(0x7aa3db6f), X(0x7aac9d6b), X(0x7ab55597), X(0x7abe03f9), + X(0x7ac6a895), X(0x7acf4370), X(0x7ad7d48f), X(0x7ae05bf6), + X(0x7ae8d9ac), X(0x7af14db5), X(0x7af9b815), X(0x7b0218d2), + X(0x7b0a6ff2), X(0x7b12bd78), X(0x7b1b016a), X(0x7b233bce), + X(0x7b2b6ca7), X(0x7b3393fc), X(0x7b3bb1d1), X(0x7b43c62c), + X(0x7b4bd111), X(0x7b53d286), X(0x7b5bca90), X(0x7b63b935), + X(0x7b6b9e78), X(0x7b737a61), X(0x7b7b4cf3), X(0x7b831634), + X(0x7b8ad629), X(0x7b928cd8), X(0x7b9a3a45), X(0x7ba1de77), + X(0x7ba97972), X(0x7bb10b3c), X(0x7bb893d9), X(0x7bc01350), + X(0x7bc789a6), X(0x7bcef6e0), X(0x7bd65b03), X(0x7bddb616), + X(0x7be5081c), X(0x7bec511c), X(0x7bf3911b), X(0x7bfac81f), + X(0x7c01f62c), X(0x7c091b49), X(0x7c10377b), X(0x7c174ac7), + X(0x7c1e5532), X(0x7c2556c4), X(0x7c2c4f80), X(0x7c333f6c), + X(0x7c3a268e), X(0x7c4104ec), X(0x7c47da8a), X(0x7c4ea76f), + X(0x7c556ba1), X(0x7c5c2724), X(0x7c62d9fe), X(0x7c698435), + X(0x7c7025cf), X(0x7c76bed0), X(0x7c7d4f40), X(0x7c83d723), + X(0x7c8a567f), X(0x7c90cd5a), X(0x7c973bb9), X(0x7c9da1a2), + X(0x7ca3ff1b), X(0x7caa542a), X(0x7cb0a0d3), X(0x7cb6e51e), + X(0x7cbd210f), X(0x7cc354ac), X(0x7cc97ffc), X(0x7ccfa304), + X(0x7cd5bdc9), X(0x7cdbd051), X(0x7ce1daa3), X(0x7ce7dcc3), + X(0x7cedd6b8), X(0x7cf3c888), X(0x7cf9b238), X(0x7cff93cf), + X(0x7d056d51), X(0x7d0b3ec5), X(0x7d110830), X(0x7d16c99a), + X(0x7d1c8306), X(0x7d22347c), X(0x7d27de00), X(0x7d2d7f9a), + X(0x7d33194f), X(0x7d38ab24), X(0x7d3e351f), X(0x7d43b748), + X(0x7d4931a2), X(0x7d4ea435), X(0x7d540f06), X(0x7d59721b), + X(0x7d5ecd7b), X(0x7d64212a), X(0x7d696d2f), X(0x7d6eb190), + X(0x7d73ee53), X(0x7d79237e), X(0x7d7e5117), X(0x7d837723), + X(0x7d8895a9), X(0x7d8dacae), X(0x7d92bc3a), X(0x7d97c451), + X(0x7d9cc4f9), X(0x7da1be39), X(0x7da6b017), X(0x7dab9a99), + X(0x7db07dc4), X(0x7db5599e), X(0x7dba2e2f), X(0x7dbefb7b), + X(0x7dc3c189), X(0x7dc8805e), X(0x7dcd3802), X(0x7dd1e879), + X(0x7dd691ca), X(0x7ddb33fb), X(0x7ddfcf12), X(0x7de46315), + X(0x7de8f00a), X(0x7ded75f8), X(0x7df1f4e3), X(0x7df66cd3), + X(0x7dfaddcd), X(0x7dff47d7), X(0x7e03aaf8), X(0x7e080735), + X(0x7e0c5c95), X(0x7e10ab1e), X(0x7e14f2d5), X(0x7e1933c1), + X(0x7e1d6de8), X(0x7e21a150), X(0x7e25cdff), X(0x7e29f3fc), + X(0x7e2e134c), X(0x7e322bf5), X(0x7e363dfd), X(0x7e3a496b), + X(0x7e3e4e45), X(0x7e424c90), X(0x7e464454), X(0x7e4a3595), + X(0x7e4e205a), X(0x7e5204aa), X(0x7e55e289), X(0x7e59b9ff), + X(0x7e5d8b12), X(0x7e6155c7), X(0x7e651a24), X(0x7e68d831), + X(0x7e6c8ff2), X(0x7e70416e), X(0x7e73ecac), X(0x7e7791b0), + X(0x7e7b3082), X(0x7e7ec927), X(0x7e825ba6), X(0x7e85e804), + X(0x7e896e48), X(0x7e8cee77), X(0x7e906899), X(0x7e93dcb2), + X(0x7e974aca), X(0x7e9ab2e5), X(0x7e9e150b), X(0x7ea17141), + X(0x7ea4c78e), X(0x7ea817f7), X(0x7eab6283), X(0x7eaea737), + X(0x7eb1e61a), X(0x7eb51f33), X(0x7eb85285), X(0x7ebb8019), + X(0x7ebea7f4), X(0x7ec1ca1d), X(0x7ec4e698), X(0x7ec7fd6d), + X(0x7ecb0ea1), X(0x7ece1a3a), X(0x7ed1203f), X(0x7ed420b6), + X(0x7ed71ba4), X(0x7eda110f), X(0x7edd00ff), X(0x7edfeb78), + X(0x7ee2d081), X(0x7ee5b01f), X(0x7ee88a5a), X(0x7eeb5f36), + X(0x7eee2eba), X(0x7ef0f8ed), X(0x7ef3bdd3), X(0x7ef67d73), + X(0x7ef937d3), X(0x7efbecf9), X(0x7efe9ceb), X(0x7f0147ae), + X(0x7f03ed4a), X(0x7f068dc4), X(0x7f092922), X(0x7f0bbf69), + X(0x7f0e50a1), X(0x7f10dcce), X(0x7f1363f7), X(0x7f15e622), + X(0x7f186355), X(0x7f1adb95), X(0x7f1d4ee9), X(0x7f1fbd57), + X(0x7f2226e4), X(0x7f248b96), X(0x7f26eb74), X(0x7f294683), + X(0x7f2b9cc9), X(0x7f2dee4d), X(0x7f303b13), X(0x7f328322), + X(0x7f34c680), X(0x7f370533), X(0x7f393f40), X(0x7f3b74ad), + X(0x7f3da581), X(0x7f3fd1c1), X(0x7f41f972), X(0x7f441c9c), + X(0x7f463b43), X(0x7f48556d), X(0x7f4a6b21), X(0x7f4c7c64), + X(0x7f4e893c), X(0x7f5091ae), X(0x7f5295c1), X(0x7f54957a), + X(0x7f5690e0), X(0x7f5887f7), X(0x7f5a7ac5), X(0x7f5c6951), + X(0x7f5e53a0), X(0x7f6039b8), X(0x7f621b9e), X(0x7f63f958), + X(0x7f65d2ed), X(0x7f67a861), X(0x7f6979ba), X(0x7f6b46ff), + X(0x7f6d1034), X(0x7f6ed560), X(0x7f709687), X(0x7f7253b1), + X(0x7f740ce1), X(0x7f75c21f), X(0x7f777370), X(0x7f7920d8), + X(0x7f7aca5f), X(0x7f7c7008), X(0x7f7e11db), X(0x7f7fafdd), + X(0x7f814a13), X(0x7f82e082), X(0x7f847331), X(0x7f860224), + X(0x7f878d62), X(0x7f8914f0), X(0x7f8a98d4), X(0x7f8c1912), + X(0x7f8d95b0), X(0x7f8f0eb5), X(0x7f908425), X(0x7f91f605), + X(0x7f93645c), X(0x7f94cf2f), X(0x7f963683), X(0x7f979a5d), + X(0x7f98fac4), X(0x7f9a57bb), X(0x7f9bb14a), X(0x7f9d0775), + X(0x7f9e5a41), X(0x7f9fa9b4), X(0x7fa0f5d3), X(0x7fa23ea4), + X(0x7fa3842b), X(0x7fa4c66f), X(0x7fa60575), X(0x7fa74141), + X(0x7fa879d9), X(0x7fa9af42), X(0x7faae182), X(0x7fac109e), + X(0x7fad3c9a), X(0x7fae657d), X(0x7faf8b4c), X(0x7fb0ae0b), + X(0x7fb1cdc0), X(0x7fb2ea70), X(0x7fb40420), X(0x7fb51ad5), + X(0x7fb62e95), X(0x7fb73f64), X(0x7fb84d48), X(0x7fb95846), + X(0x7fba6062), X(0x7fbb65a2), X(0x7fbc680c), X(0x7fbd67a3), + X(0x7fbe646d), X(0x7fbf5e70), X(0x7fc055af), X(0x7fc14a31), + X(0x7fc23bf9), X(0x7fc32b0d), X(0x7fc41773), X(0x7fc5012e), + X(0x7fc5e844), X(0x7fc6ccba), X(0x7fc7ae94), X(0x7fc88dd8), + X(0x7fc96a8a), X(0x7fca44af), X(0x7fcb1c4c), X(0x7fcbf167), + X(0x7fccc403), X(0x7fcd9425), X(0x7fce61d3), X(0x7fcf2d11), + X(0x7fcff5e3), X(0x7fd0bc4f), X(0x7fd1805a), X(0x7fd24207), + X(0x7fd3015c), X(0x7fd3be5d), X(0x7fd47910), X(0x7fd53178), + X(0x7fd5e79b), X(0x7fd69b7c), X(0x7fd74d21), X(0x7fd7fc8e), + X(0x7fd8a9c8), X(0x7fd954d4), X(0x7fd9fdb5), X(0x7fdaa471), + X(0x7fdb490b), X(0x7fdbeb89), X(0x7fdc8bef), X(0x7fdd2a42), + X(0x7fddc685), X(0x7fde60be), X(0x7fdef8f0), X(0x7fdf8f20), + X(0x7fe02353), X(0x7fe0b58d), X(0x7fe145d3), X(0x7fe1d428), + X(0x7fe26091), X(0x7fe2eb12), X(0x7fe373b0), X(0x7fe3fa6f), + X(0x7fe47f53), X(0x7fe50260), X(0x7fe5839b), X(0x7fe60308), + X(0x7fe680ab), X(0x7fe6fc88), X(0x7fe776a4), X(0x7fe7ef02), + X(0x7fe865a7), X(0x7fe8da97), X(0x7fe94dd6), X(0x7fe9bf68), + X(0x7fea2f51), X(0x7fea9d95), X(0x7feb0a39), X(0x7feb7540), + X(0x7febdeae), X(0x7fec4687), X(0x7fecaccf), X(0x7fed118b), + X(0x7fed74be), X(0x7fedd66c), X(0x7fee3698), X(0x7fee9548), + X(0x7feef27e), X(0x7fef4e3f), X(0x7fefa88e), X(0x7ff0016f), + X(0x7ff058e7), X(0x7ff0aef8), X(0x7ff103a6), X(0x7ff156f6), + X(0x7ff1a8eb), X(0x7ff1f988), X(0x7ff248d2), X(0x7ff296cc), + X(0x7ff2e37a), X(0x7ff32edf), X(0x7ff378ff), X(0x7ff3c1de), + X(0x7ff4097e), X(0x7ff44fe5), X(0x7ff49515), X(0x7ff4d911), + X(0x7ff51bde), X(0x7ff55d7f), X(0x7ff59df7), X(0x7ff5dd4a), + X(0x7ff61b7b), X(0x7ff6588d), X(0x7ff69485), X(0x7ff6cf65), + X(0x7ff70930), X(0x7ff741eb), X(0x7ff77998), X(0x7ff7b03b), + X(0x7ff7e5d7), X(0x7ff81a6f), X(0x7ff84e06), X(0x7ff880a1), + X(0x7ff8b241), X(0x7ff8e2ea), X(0x7ff912a0), X(0x7ff94165), + X(0x7ff96f3d), X(0x7ff99c2b), X(0x7ff9c831), X(0x7ff9f354), + X(0x7ffa1d95), X(0x7ffa46f9), X(0x7ffa6f81), X(0x7ffa9731), + X(0x7ffabe0d), X(0x7ffae416), X(0x7ffb0951), X(0x7ffb2dbf), + X(0x7ffb5164), X(0x7ffb7442), X(0x7ffb965d), X(0x7ffbb7b8), + X(0x7ffbd854), X(0x7ffbf836), X(0x7ffc175f), X(0x7ffc35d3), + X(0x7ffc5394), X(0x7ffc70a5), X(0x7ffc8d09), X(0x7ffca8c2), + X(0x7ffcc3d4), X(0x7ffcde3f), X(0x7ffcf809), X(0x7ffd1132), + X(0x7ffd29be), X(0x7ffd41ae), X(0x7ffd5907), X(0x7ffd6fc9), + X(0x7ffd85f9), X(0x7ffd9b97), X(0x7ffdb0a7), X(0x7ffdc52b), + X(0x7ffdd926), X(0x7ffdec99), X(0x7ffdff88), X(0x7ffe11f4), + X(0x7ffe23e0), X(0x7ffe354f), X(0x7ffe4642), X(0x7ffe56bc), + X(0x7ffe66bf), X(0x7ffe764e), X(0x7ffe856a), X(0x7ffe9416), + X(0x7ffea254), X(0x7ffeb026), X(0x7ffebd8e), X(0x7ffeca8f), + X(0x7ffed72a), X(0x7ffee362), X(0x7ffeef38), X(0x7ffefaaf), + X(0x7fff05c9), X(0x7fff1087), X(0x7fff1aec), X(0x7fff24f9), + X(0x7fff2eb1), X(0x7fff3816), X(0x7fff4128), X(0x7fff49eb), + X(0x7fff5260), X(0x7fff5a88), X(0x7fff6266), X(0x7fff69fc), + X(0x7fff714b), X(0x7fff7854), X(0x7fff7f1a), X(0x7fff859f), + X(0x7fff8be3), X(0x7fff91ea), X(0x7fff97b3), X(0x7fff9d41), + X(0x7fffa296), X(0x7fffa7b3), X(0x7fffac99), X(0x7fffb14b), + X(0x7fffb5c9), X(0x7fffba15), X(0x7fffbe31), X(0x7fffc21d), + X(0x7fffc5dc), X(0x7fffc96f), X(0x7fffccd8), X(0x7fffd016), + X(0x7fffd32d), X(0x7fffd61c), X(0x7fffd8e7), X(0x7fffdb8d), + X(0x7fffde0f), X(0x7fffe071), X(0x7fffe2b1), X(0x7fffe4d2), + X(0x7fffe6d5), X(0x7fffe8bb), X(0x7fffea85), X(0x7fffec34), + X(0x7fffedc9), X(0x7fffef45), X(0x7ffff0aa), X(0x7ffff1f7), + X(0x7ffff330), X(0x7ffff453), X(0x7ffff562), X(0x7ffff65f), + X(0x7ffff749), X(0x7ffff823), X(0x7ffff8ec), X(0x7ffff9a6), + X(0x7ffffa51), X(0x7ffffaee), X(0x7ffffb7e), X(0x7ffffc02), + X(0x7ffffc7a), X(0x7ffffce7), X(0x7ffffd4a), X(0x7ffffda3), + X(0x7ffffdf4), X(0x7ffffe3c), X(0x7ffffe7c), X(0x7ffffeb6), + X(0x7ffffee8), X(0x7fffff15), X(0x7fffff3c), X(0x7fffff5e), + X(0x7fffff7b), X(0x7fffff95), X(0x7fffffaa), X(0x7fffffbc), + X(0x7fffffcb), X(0x7fffffd7), X(0x7fffffe2), X(0x7fffffea), + X(0x7ffffff0), X(0x7ffffff5), X(0x7ffffff9), X(0x7ffffffb), + X(0x7ffffffd), X(0x7ffffffe), X(0x7fffffff), X(0x7fffffff), + X(0x7fffffff), X(0x7fffffff), X(0x7fffffff), X(0x7fffffff), +}; + +static const LOOKUP_T vwin8192[4096] = { + X(0x0000007c), X(0x0000045c), X(0x00000c1d), X(0x000017bd), + X(0x0000273e), X(0x00003a9f), X(0x000051e0), X(0x00006d02), + X(0x00008c03), X(0x0000aee5), X(0x0000d5a7), X(0x00010049), + X(0x00012ecb), X(0x0001612d), X(0x00019770), X(0x0001d193), + X(0x00020f96), X(0x00025178), X(0x0002973c), X(0x0002e0df), + X(0x00032e62), X(0x00037fc5), X(0x0003d509), X(0x00042e2c), + X(0x00048b30), X(0x0004ec13), X(0x000550d7), X(0x0005b97a), + X(0x000625fe), X(0x00069661), X(0x00070aa4), X(0x000782c8), + X(0x0007fecb), X(0x00087eae), X(0x00090271), X(0x00098a14), + X(0x000a1597), X(0x000aa4f9), X(0x000b383b), X(0x000bcf5d), + X(0x000c6a5f), X(0x000d0941), X(0x000dac02), X(0x000e52a3), + X(0x000efd23), X(0x000fab84), X(0x00105dc3), X(0x001113e3), + X(0x0011cde2), X(0x00128bc0), X(0x00134d7e), X(0x0014131b), + X(0x0014dc98), X(0x0015a9f4), X(0x00167b30), X(0x0017504a), + X(0x00182945), X(0x0019061e), X(0x0019e6d7), X(0x001acb6f), + X(0x001bb3e6), X(0x001ca03c), X(0x001d9071), X(0x001e8485), + X(0x001f7c79), X(0x0020784b), X(0x002177fc), X(0x00227b8c), + X(0x002382fb), X(0x00248e49), X(0x00259d76), X(0x0026b081), + X(0x0027c76b), X(0x0028e234), X(0x002a00dc), X(0x002b2361), + X(0x002c49c6), X(0x002d7409), X(0x002ea22a), X(0x002fd42a), + X(0x00310a08), X(0x003243c5), X(0x00338160), X(0x0034c2d9), + X(0x00360830), X(0x00375165), X(0x00389e78), X(0x0039ef6a), + X(0x003b4439), X(0x003c9ce6), X(0x003df971), X(0x003f59da), + X(0x0040be20), X(0x00422645), X(0x00439247), X(0x00450226), + X(0x004675e3), X(0x0047ed7e), X(0x004968f5), X(0x004ae84b), + X(0x004c6b7d), X(0x004df28d), X(0x004f7d7a), X(0x00510c44), + X(0x00529eeb), X(0x00543570), X(0x0055cfd1), X(0x00576e0f), + X(0x00591029), X(0x005ab621), X(0x005c5ff5), X(0x005e0da6), + X(0x005fbf33), X(0x0061749d), X(0x00632de4), X(0x0064eb06), + X(0x0066ac05), X(0x006870e0), X(0x006a3998), X(0x006c062b), + X(0x006dd69b), X(0x006faae6), X(0x0071830d), X(0x00735f10), + X(0x00753eef), X(0x007722a9), X(0x00790a3f), X(0x007af5b1), + X(0x007ce4fe), X(0x007ed826), X(0x0080cf29), X(0x0082ca08), + X(0x0084c8c2), X(0x0086cb57), X(0x0088d1c7), X(0x008adc11), + X(0x008cea37), X(0x008efc37), X(0x00911212), X(0x00932bc7), + X(0x00954957), X(0x00976ac2), X(0x00999006), X(0x009bb925), + X(0x009de61e), X(0x00a016f1), X(0x00a24b9e), X(0x00a48425), + X(0x00a6c086), X(0x00a900c0), X(0x00ab44d4), X(0x00ad8cc2), + X(0x00afd889), X(0x00b22829), X(0x00b47ba2), X(0x00b6d2f5), + X(0x00b92e21), X(0x00bb8d26), X(0x00bdf004), X(0x00c056ba), + X(0x00c2c149), X(0x00c52fb1), X(0x00c7a1f1), X(0x00ca180a), + X(0x00cc91fb), X(0x00cf0fc5), X(0x00d19166), X(0x00d416df), + X(0x00d6a031), X(0x00d92d5a), X(0x00dbbe5b), X(0x00de5333), + X(0x00e0ebe3), X(0x00e3886b), X(0x00e628c9), X(0x00e8ccff), + X(0x00eb750c), X(0x00ee20f0), X(0x00f0d0ab), X(0x00f3843d), + X(0x00f63ba5), X(0x00f8f6e4), X(0x00fbb5fa), X(0x00fe78e5), + X(0x01013fa7), X(0x01040a3f), X(0x0106d8ae), X(0x0109aaf2), + X(0x010c810c), X(0x010f5afb), X(0x011238c0), X(0x01151a5b), + X(0x0117ffcb), X(0x011ae910), X(0x011dd62a), X(0x0120c719), + X(0x0123bbdd), X(0x0126b476), X(0x0129b0e4), X(0x012cb126), + X(0x012fb53c), X(0x0132bd27), X(0x0135c8e6), X(0x0138d879), + X(0x013bebdf), X(0x013f031a), X(0x01421e28), X(0x01453d0a), + X(0x01485fbf), X(0x014b8648), X(0x014eb0a4), X(0x0151ded2), + X(0x015510d4), X(0x015846a8), X(0x015b8050), X(0x015ebdc9), + X(0x0161ff15), X(0x01654434), X(0x01688d24), X(0x016bd9e6), + X(0x016f2a7b), X(0x01727ee1), X(0x0175d718), X(0x01793321), + X(0x017c92fc), X(0x017ff6a7), X(0x01835e24), X(0x0186c972), + X(0x018a3890), X(0x018dab7f), X(0x0191223f), X(0x01949ccf), + X(0x01981b2f), X(0x019b9d5f), X(0x019f235f), X(0x01a2ad2f), + X(0x01a63acf), X(0x01a9cc3e), X(0x01ad617c), X(0x01b0fa8a), + X(0x01b49767), X(0x01b83813), X(0x01bbdc8d), X(0x01bf84d6), + X(0x01c330ee), X(0x01c6e0d4), X(0x01ca9488), X(0x01ce4c0b), + X(0x01d2075b), X(0x01d5c679), X(0x01d98964), X(0x01dd501d), + X(0x01e11aa3), X(0x01e4e8f6), X(0x01e8bb17), X(0x01ec9104), + X(0x01f06abd), X(0x01f44844), X(0x01f82996), X(0x01fc0eb5), + X(0x01fff7a0), X(0x0203e456), X(0x0207d4d9), X(0x020bc926), + X(0x020fc140), X(0x0213bd24), X(0x0217bcd4), X(0x021bc04e), + X(0x021fc793), X(0x0223d2a3), X(0x0227e17d), X(0x022bf421), + X(0x02300a90), X(0x023424c8), X(0x023842ca), X(0x023c6495), + X(0x02408a2a), X(0x0244b389), X(0x0248e0b0), X(0x024d11a0), + X(0x02514659), X(0x02557eda), X(0x0259bb24), X(0x025dfb35), + X(0x02623f0f), X(0x026686b1), X(0x026ad21a), X(0x026f214b), + X(0x02737443), X(0x0277cb02), X(0x027c2588), X(0x028083d5), + X(0x0284e5e9), X(0x02894bc2), X(0x028db562), X(0x029222c8), + X(0x029693f4), X(0x029b08e6), X(0x029f819d), X(0x02a3fe19), + X(0x02a87e5b), X(0x02ad0261), X(0x02b18a2c), X(0x02b615bb), + X(0x02baa50f), X(0x02bf3827), X(0x02c3cf03), X(0x02c869a3), + X(0x02cd0807), X(0x02d1aa2d), X(0x02d65017), X(0x02daf9c4), + X(0x02dfa734), X(0x02e45866), X(0x02e90d5b), X(0x02edc612), + X(0x02f2828b), X(0x02f742c6), X(0x02fc06c3), X(0x0300ce80), + X(0x030599ff), X(0x030a6940), X(0x030f3c40), X(0x03141302), + X(0x0318ed84), X(0x031dcbc6), X(0x0322adc8), X(0x0327938a), + X(0x032c7d0c), X(0x03316a4c), X(0x03365b4d), X(0x033b500c), + X(0x03404889), X(0x034544c6), X(0x034a44c0), X(0x034f4879), + X(0x03544ff0), X(0x03595b24), X(0x035e6a16), X(0x03637cc5), + X(0x03689331), X(0x036dad5a), X(0x0372cb40), X(0x0377ece2), + X(0x037d1240), X(0x03823b5a), X(0x03876830), X(0x038c98c1), + X(0x0391cd0e), X(0x03970516), X(0x039c40d8), X(0x03a18055), + X(0x03a6c38d), X(0x03ac0a7f), X(0x03b1552b), X(0x03b6a390), + X(0x03bbf5af), X(0x03c14b88), X(0x03c6a519), X(0x03cc0263), + X(0x03d16366), X(0x03d6c821), X(0x03dc3094), X(0x03e19cc0), + X(0x03e70ca2), X(0x03ec803d), X(0x03f1f78e), X(0x03f77296), + X(0x03fcf155), X(0x040273cb), X(0x0407f9f7), X(0x040d83d9), + X(0x04131170), X(0x0418a2bd), X(0x041e37c0), X(0x0423d077), + X(0x04296ce4), X(0x042f0d04), X(0x0434b0da), X(0x043a5863), + X(0x044003a0), X(0x0445b290), X(0x044b6534), X(0x04511b8b), + X(0x0456d595), X(0x045c9352), X(0x046254c1), X(0x046819e1), + X(0x046de2b4), X(0x0473af39), X(0x04797f6e), X(0x047f5355), + X(0x04852aec), X(0x048b0635), X(0x0490e52d), X(0x0496c7d6), + X(0x049cae2e), X(0x04a29836), X(0x04a885ed), X(0x04ae7753), + X(0x04b46c68), X(0x04ba652b), X(0x04c0619d), X(0x04c661bc), + X(0x04cc658a), X(0x04d26d04), X(0x04d8782c), X(0x04de8701), + X(0x04e49983), X(0x04eaafb0), X(0x04f0c98a), X(0x04f6e710), + X(0x04fd0842), X(0x05032d1e), X(0x050955a6), X(0x050f81d8), + X(0x0515b1b5), X(0x051be53d), X(0x05221c6e), X(0x05285748), + X(0x052e95cd), X(0x0534d7fa), X(0x053b1dd0), X(0x0541674e), + X(0x0547b475), X(0x054e0544), X(0x055459bb), X(0x055ab1d9), + X(0x05610d9e), X(0x05676d0a), X(0x056dd01c), X(0x057436d5), + X(0x057aa134), X(0x05810f38), X(0x058780e2), X(0x058df631), + X(0x05946f25), X(0x059aebbe), X(0x05a16bfa), X(0x05a7efdb), + X(0x05ae775f), X(0x05b50287), X(0x05bb9152), X(0x05c223c0), + X(0x05c8b9d0), X(0x05cf5382), X(0x05d5f0d6), X(0x05dc91cc), + X(0x05e33663), X(0x05e9de9c), X(0x05f08a75), X(0x05f739ee), + X(0x05fded07), X(0x0604a3c0), X(0x060b5e19), X(0x06121c11), + X(0x0618dda8), X(0x061fa2dd), X(0x06266bb1), X(0x062d3822), + X(0x06340831), X(0x063adbde), X(0x0641b328), X(0x06488e0e), + X(0x064f6c91), X(0x06564eaf), X(0x065d346a), X(0x06641dc0), + X(0x066b0ab1), X(0x0671fb3d), X(0x0678ef64), X(0x067fe724), + X(0x0686e27f), X(0x068de173), X(0x0694e400), X(0x069bea27), + X(0x06a2f3e6), X(0x06aa013d), X(0x06b1122c), X(0x06b826b3), + X(0x06bf3ed1), X(0x06c65a86), X(0x06cd79d1), X(0x06d49cb3), + X(0x06dbc32b), X(0x06e2ed38), X(0x06ea1adb), X(0x06f14c13), + X(0x06f880df), X(0x06ffb940), X(0x0706f535), X(0x070e34bd), + X(0x071577d9), X(0x071cbe88), X(0x072408c9), X(0x072b569d), + X(0x0732a802), X(0x0739fcf9), X(0x07415582), X(0x0748b19b), + X(0x07501145), X(0x0757747f), X(0x075edb49), X(0x076645a3), + X(0x076db38c), X(0x07752503), X(0x077c9a09), X(0x0784129e), + X(0x078b8ec0), X(0x07930e70), X(0x079a91ac), X(0x07a21876), + X(0x07a9a2cc), X(0x07b130ad), X(0x07b8c21b), X(0x07c05714), + X(0x07c7ef98), X(0x07cf8ba6), X(0x07d72b3f), X(0x07dece62), + X(0x07e6750e), X(0x07ee1f43), X(0x07f5cd01), X(0x07fd7e48), + X(0x08053316), X(0x080ceb6d), X(0x0814a74a), X(0x081c66af), + X(0x0824299a), X(0x082bf00c), X(0x0833ba03), X(0x083b8780), + X(0x08435882), X(0x084b2d09), X(0x08530514), X(0x085ae0a3), + X(0x0862bfb6), X(0x086aa24c), X(0x08728865), X(0x087a7201), + X(0x08825f1e), X(0x088a4fbe), X(0x089243de), X(0x089a3b80), + X(0x08a236a2), X(0x08aa3545), X(0x08b23767), X(0x08ba3d09), + X(0x08c2462a), X(0x08ca52c9), X(0x08d262e7), X(0x08da7682), + X(0x08e28d9c), X(0x08eaa832), X(0x08f2c645), X(0x08fae7d4), + X(0x09030cdf), X(0x090b3566), X(0x09136168), X(0x091b90e5), + X(0x0923c3dc), X(0x092bfa4d), X(0x09343437), X(0x093c719b), + X(0x0944b277), X(0x094cf6cc), X(0x09553e99), X(0x095d89dd), + X(0x0965d899), X(0x096e2acb), X(0x09768073), X(0x097ed991), + X(0x09873625), X(0x098f962e), X(0x0997f9ac), X(0x09a0609e), + X(0x09a8cb04), X(0x09b138dd), X(0x09b9aa29), X(0x09c21ee8), + X(0x09ca9719), X(0x09d312bc), X(0x09db91d0), X(0x09e41456), + X(0x09ec9a4b), X(0x09f523b1), X(0x09fdb087), X(0x0a0640cc), + X(0x0a0ed47f), X(0x0a176ba2), X(0x0a200632), X(0x0a28a42f), + X(0x0a31459a), X(0x0a39ea72), X(0x0a4292b5), X(0x0a4b3e65), + X(0x0a53ed80), X(0x0a5ca006), X(0x0a6555f7), X(0x0a6e0f51), + X(0x0a76cc16), X(0x0a7f8c44), X(0x0a884fda), X(0x0a9116d9), + X(0x0a99e140), X(0x0aa2af0e), X(0x0aab8043), X(0x0ab454df), + X(0x0abd2ce1), X(0x0ac60849), X(0x0acee716), X(0x0ad7c948), + X(0x0ae0aedf), X(0x0ae997d9), X(0x0af28437), X(0x0afb73f7), + X(0x0b04671b), X(0x0b0d5da0), X(0x0b165788), X(0x0b1f54d0), + X(0x0b285579), X(0x0b315983), X(0x0b3a60ec), X(0x0b436bb5), + X(0x0b4c79dd), X(0x0b558b63), X(0x0b5ea048), X(0x0b67b88a), + X(0x0b70d429), X(0x0b79f324), X(0x0b83157c), X(0x0b8c3b30), + X(0x0b95643f), X(0x0b9e90a8), X(0x0ba7c06c), X(0x0bb0f38a), + X(0x0bba2a01), X(0x0bc363d1), X(0x0bcca0f9), X(0x0bd5e17a), + X(0x0bdf2552), X(0x0be86c81), X(0x0bf1b706), X(0x0bfb04e2), + X(0x0c045613), X(0x0c0daa99), X(0x0c170274), X(0x0c205da3), + X(0x0c29bc25), X(0x0c331dfb), X(0x0c3c8323), X(0x0c45eb9e), + X(0x0c4f576a), X(0x0c58c688), X(0x0c6238f6), X(0x0c6baeb5), + X(0x0c7527c3), X(0x0c7ea421), X(0x0c8823cd), X(0x0c91a6c8), + X(0x0c9b2d10), X(0x0ca4b6a6), X(0x0cae4389), X(0x0cb7d3b8), + X(0x0cc16732), X(0x0ccafdf8), X(0x0cd49809), X(0x0cde3564), + X(0x0ce7d609), X(0x0cf179f7), X(0x0cfb212e), X(0x0d04cbad), + X(0x0d0e7974), X(0x0d182a83), X(0x0d21ded8), X(0x0d2b9673), + X(0x0d355154), X(0x0d3f0f7b), X(0x0d48d0e6), X(0x0d529595), + X(0x0d5c5d88), X(0x0d6628be), X(0x0d6ff737), X(0x0d79c8f2), + X(0x0d839dee), X(0x0d8d762c), X(0x0d9751aa), X(0x0da13068), + X(0x0dab1266), X(0x0db4f7a3), X(0x0dbee01e), X(0x0dc8cbd8), + X(0x0dd2bace), X(0x0ddcad02), X(0x0de6a272), X(0x0df09b1e), + X(0x0dfa9705), X(0x0e049627), X(0x0e0e9883), X(0x0e189e19), + X(0x0e22a6e8), X(0x0e2cb2f0), X(0x0e36c230), X(0x0e40d4a8), + X(0x0e4aea56), X(0x0e55033b), X(0x0e5f1f56), X(0x0e693ea7), + X(0x0e73612c), X(0x0e7d86e5), X(0x0e87afd3), X(0x0e91dbf3), + X(0x0e9c0b47), X(0x0ea63dcc), X(0x0eb07383), X(0x0ebaac6b), + X(0x0ec4e883), X(0x0ecf27cc), X(0x0ed96a44), X(0x0ee3afea), + X(0x0eedf8bf), X(0x0ef844c2), X(0x0f0293f2), X(0x0f0ce64e), + X(0x0f173bd6), X(0x0f21948a), X(0x0f2bf069), X(0x0f364f72), + X(0x0f40b1a5), X(0x0f4b1701), X(0x0f557f86), X(0x0f5feb32), + X(0x0f6a5a07), X(0x0f74cc02), X(0x0f7f4124), X(0x0f89b96b), + X(0x0f9434d8), X(0x0f9eb369), X(0x0fa9351e), X(0x0fb3b9f7), + X(0x0fbe41f3), X(0x0fc8cd11), X(0x0fd35b51), X(0x0fddecb2), + X(0x0fe88134), X(0x0ff318d6), X(0x0ffdb397), X(0x10085177), + X(0x1012f275), X(0x101d9691), X(0x10283dca), X(0x1032e81f), + X(0x103d9591), X(0x1048461e), X(0x1052f9c5), X(0x105db087), + X(0x10686a62), X(0x10732756), X(0x107de763), X(0x1088aa87), + X(0x109370c2), X(0x109e3a14), X(0x10a9067c), X(0x10b3d5f9), + X(0x10bea88b), X(0x10c97e31), X(0x10d456eb), X(0x10df32b8), + X(0x10ea1197), X(0x10f4f387), X(0x10ffd889), X(0x110ac09b), + X(0x1115abbe), X(0x112099ef), X(0x112b8b2f), X(0x11367f7d), + X(0x114176d9), X(0x114c7141), X(0x11576eb6), X(0x11626f36), + X(0x116d72c1), X(0x11787957), X(0x118382f6), X(0x118e8f9e), + X(0x11999f4f), X(0x11a4b208), X(0x11afc7c7), X(0x11bae08e), + X(0x11c5fc5a), X(0x11d11b2c), X(0x11dc3d02), X(0x11e761dd), + X(0x11f289ba), X(0x11fdb49b), X(0x1208e27e), X(0x12141362), + X(0x121f4748), X(0x122a7e2d), X(0x1235b812), X(0x1240f4f6), + X(0x124c34d9), X(0x125777b9), X(0x1262bd96), X(0x126e0670), + X(0x12795245), X(0x1284a115), X(0x128ff2e0), X(0x129b47a5), + X(0x12a69f63), X(0x12b1fa19), X(0x12bd57c7), X(0x12c8b86c), + X(0x12d41c08), X(0x12df829a), X(0x12eaec21), X(0x12f6589d), + X(0x1301c80c), X(0x130d3a6f), X(0x1318afc4), X(0x1324280b), + X(0x132fa344), X(0x133b216d), X(0x1346a286), X(0x1352268e), + X(0x135dad85), X(0x1369376a), X(0x1374c43c), X(0x138053fb), + X(0x138be6a5), X(0x13977c3b), X(0x13a314bc), X(0x13aeb026), + X(0x13ba4e79), X(0x13c5efb5), X(0x13d193d9), X(0x13dd3ae4), + X(0x13e8e4d6), X(0x13f491ad), X(0x1400416a), X(0x140bf40b), + X(0x1417a98f), X(0x142361f7), X(0x142f1d41), X(0x143adb6d), + X(0x14469c7a), X(0x14526067), X(0x145e2734), X(0x1469f0df), + X(0x1475bd69), X(0x14818cd0), X(0x148d5f15), X(0x14993435), + X(0x14a50c31), X(0x14b0e708), X(0x14bcc4b8), X(0x14c8a542), + X(0x14d488a5), X(0x14e06edf), X(0x14ec57f1), X(0x14f843d9), + X(0x15043297), X(0x1510242b), X(0x151c1892), X(0x15280fcd), + X(0x153409dc), X(0x154006bc), X(0x154c066e), X(0x155808f1), + X(0x15640e44), X(0x15701666), X(0x157c2157), X(0x15882f16), + X(0x15943fa2), X(0x15a052fb), X(0x15ac691f), X(0x15b8820f), + X(0x15c49dc8), X(0x15d0bc4c), X(0x15dcdd98), X(0x15e901ad), + X(0x15f52888), X(0x1601522b), X(0x160d7e93), X(0x1619adc1), + X(0x1625dfb3), X(0x16321469), X(0x163e4be2), X(0x164a861d), + X(0x1656c31a), X(0x166302d8), X(0x166f4555), X(0x167b8a92), + X(0x1687d28e), X(0x16941d47), X(0x16a06abe), X(0x16acbaf0), + X(0x16b90ddf), X(0x16c56388), X(0x16d1bbeb), X(0x16de1708), + X(0x16ea74dd), X(0x16f6d56a), X(0x170338ae), X(0x170f9ea8), + X(0x171c0758), X(0x172872bd), X(0x1734e0d6), X(0x174151a2), + X(0x174dc520), X(0x175a3b51), X(0x1766b432), X(0x17732fc4), + X(0x177fae05), X(0x178c2ef4), X(0x1798b292), X(0x17a538dd), + X(0x17b1c1d4), X(0x17be4d77), X(0x17cadbc5), X(0x17d76cbc), + X(0x17e4005e), X(0x17f096a7), X(0x17fd2f98), X(0x1809cb31), + X(0x1816696f), X(0x18230a53), X(0x182faddc), X(0x183c5408), + X(0x1848fcd8), X(0x1855a849), X(0x1862565d), X(0x186f0711), + X(0x187bba64), X(0x18887057), X(0x189528e9), X(0x18a1e418), + X(0x18aea1e3), X(0x18bb624b), X(0x18c8254e), X(0x18d4eaeb), + X(0x18e1b321), X(0x18ee7df1), X(0x18fb4b58), X(0x19081b57), + X(0x1914edec), X(0x1921c317), X(0x192e9ad6), X(0x193b7529), + X(0x19485210), X(0x19553189), X(0x19621393), X(0x196ef82e), + X(0x197bdf59), X(0x1988c913), X(0x1995b55c), X(0x19a2a432), + X(0x19af9595), X(0x19bc8983), X(0x19c97ffd), X(0x19d67900), + X(0x19e3748e), X(0x19f072a3), X(0x19fd7341), X(0x1a0a7665), + X(0x1a177c10), X(0x1a248440), X(0x1a318ef4), X(0x1a3e9c2c), + X(0x1a4babe7), X(0x1a58be24), X(0x1a65d2e2), X(0x1a72ea20), + X(0x1a8003de), X(0x1a8d201a), X(0x1a9a3ed5), X(0x1aa7600c), + X(0x1ab483bf), X(0x1ac1a9ee), X(0x1aced297), X(0x1adbfdba), + X(0x1ae92b56), X(0x1af65b69), X(0x1b038df4), X(0x1b10c2f5), + X(0x1b1dfa6b), X(0x1b2b3456), X(0x1b3870b5), X(0x1b45af87), + X(0x1b52f0ca), X(0x1b60347f), X(0x1b6d7aa4), X(0x1b7ac339), + X(0x1b880e3c), X(0x1b955bad), X(0x1ba2ab8b), X(0x1baffdd5), + X(0x1bbd528a), X(0x1bcaa9a9), X(0x1bd80332), X(0x1be55f24), + X(0x1bf2bd7d), X(0x1c001e3d), X(0x1c0d8164), X(0x1c1ae6ef), + X(0x1c284edf), X(0x1c35b932), X(0x1c4325e7), X(0x1c5094fe), + X(0x1c5e0677), X(0x1c6b7a4f), X(0x1c78f086), X(0x1c86691b), + X(0x1c93e40d), X(0x1ca1615c), X(0x1caee107), X(0x1cbc630c), + X(0x1cc9e76b), X(0x1cd76e23), X(0x1ce4f733), X(0x1cf2829a), + X(0x1d001057), X(0x1d0da06a), X(0x1d1b32d1), X(0x1d28c78c), + X(0x1d365e9a), X(0x1d43f7f9), X(0x1d5193a9), X(0x1d5f31aa), + X(0x1d6cd1f9), X(0x1d7a7497), X(0x1d881982), X(0x1d95c0ba), + X(0x1da36a3d), X(0x1db1160a), X(0x1dbec422), X(0x1dcc7482), + X(0x1dda272b), X(0x1de7dc1a), X(0x1df59350), X(0x1e034ccb), + X(0x1e11088a), X(0x1e1ec68c), X(0x1e2c86d1), X(0x1e3a4958), + X(0x1e480e20), X(0x1e55d527), X(0x1e639e6d), X(0x1e7169f1), + X(0x1e7f37b2), X(0x1e8d07b0), X(0x1e9ad9e8), X(0x1ea8ae5b), + X(0x1eb68507), X(0x1ec45dec), X(0x1ed23908), X(0x1ee0165b), + X(0x1eedf5e4), X(0x1efbd7a1), X(0x1f09bb92), X(0x1f17a1b6), + X(0x1f258a0d), X(0x1f337494), X(0x1f41614b), X(0x1f4f5032), + X(0x1f5d4147), X(0x1f6b3489), X(0x1f7929f7), X(0x1f872192), + X(0x1f951b56), X(0x1fa31744), X(0x1fb1155b), X(0x1fbf159a), + X(0x1fcd17ff), X(0x1fdb1c8b), X(0x1fe9233b), X(0x1ff72c0f), + X(0x20053706), X(0x20134420), X(0x2021535a), X(0x202f64b4), + X(0x203d782e), X(0x204b8dc6), X(0x2059a57c), X(0x2067bf4e), + X(0x2075db3b), X(0x2083f943), X(0x20921964), X(0x20a03b9e), + X(0x20ae5fef), X(0x20bc8657), X(0x20caaed5), X(0x20d8d967), + X(0x20e7060e), X(0x20f534c7), X(0x21036592), X(0x2111986e), + X(0x211fcd59), X(0x212e0454), X(0x213c3d5d), X(0x214a7873), + X(0x2158b594), X(0x2166f4c1), X(0x217535f8), X(0x21837938), + X(0x2191be81), X(0x21a005d0), X(0x21ae4f26), X(0x21bc9a81), + X(0x21cae7e0), X(0x21d93743), X(0x21e788a8), X(0x21f5dc0e), + X(0x22043174), X(0x221288da), X(0x2220e23e), X(0x222f3da0), + X(0x223d9afe), X(0x224bfa58), X(0x225a5bac), X(0x2268bef9), + X(0x2277243f), X(0x22858b7d), X(0x2293f4b0), X(0x22a25fda), + X(0x22b0ccf8), X(0x22bf3c09), X(0x22cdad0d), X(0x22dc2002), + X(0x22ea94e8), X(0x22f90bbe), X(0x23078482), X(0x2315ff33), + X(0x23247bd1), X(0x2332fa5b), X(0x23417acf), X(0x234ffd2c), + X(0x235e8173), X(0x236d07a0), X(0x237b8fb4), X(0x238a19ae), + X(0x2398a58c), X(0x23a7334d), X(0x23b5c2f1), X(0x23c45477), + X(0x23d2e7dd), X(0x23e17d22), X(0x23f01446), X(0x23fead47), + X(0x240d4825), X(0x241be4dd), X(0x242a8371), X(0x243923dd), + X(0x2447c622), X(0x24566a3e), X(0x24651031), X(0x2473b7f8), + X(0x24826194), X(0x24910d03), X(0x249fba44), X(0x24ae6957), + X(0x24bd1a39), X(0x24cbccea), X(0x24da816a), X(0x24e937b7), + X(0x24f7efcf), X(0x2506a9b3), X(0x25156560), X(0x252422d6), + X(0x2532e215), X(0x2541a31a), X(0x255065e4), X(0x255f2a74), + X(0x256df0c7), X(0x257cb8dd), X(0x258b82b4), X(0x259a4e4c), + X(0x25a91ba4), X(0x25b7eaba), X(0x25c6bb8e), X(0x25d58e1e), + X(0x25e46269), X(0x25f3386e), X(0x2602102d), X(0x2610e9a4), + X(0x261fc4d3), X(0x262ea1b7), X(0x263d8050), X(0x264c609e), + X(0x265b429e), X(0x266a2650), X(0x26790bb3), X(0x2687f2c6), + X(0x2696db88), X(0x26a5c5f7), X(0x26b4b213), X(0x26c39fda), + X(0x26d28f4c), X(0x26e18067), X(0x26f0732b), X(0x26ff6796), + X(0x270e5da7), X(0x271d555d), X(0x272c4eb7), X(0x273b49b5), + X(0x274a4654), X(0x27594495), X(0x27684475), X(0x277745f4), + X(0x27864910), X(0x27954dc9), X(0x27a4541e), X(0x27b35c0d), + X(0x27c26596), X(0x27d170b7), X(0x27e07d6f), X(0x27ef8bbd), + X(0x27fe9ba0), X(0x280dad18), X(0x281cc022), X(0x282bd4be), + X(0x283aeaeb), X(0x284a02a7), X(0x28591bf2), X(0x286836cb), + X(0x28775330), X(0x28867120), X(0x2895909b), X(0x28a4b19e), + X(0x28b3d42a), X(0x28c2f83d), X(0x28d21dd5), X(0x28e144f3), + X(0x28f06d94), X(0x28ff97b8), X(0x290ec35d), X(0x291df082), + X(0x292d1f27), X(0x293c4f4a), X(0x294b80eb), X(0x295ab407), + X(0x2969e89e), X(0x29791eaf), X(0x29885639), X(0x29978f3b), + X(0x29a6c9b3), X(0x29b605a0), X(0x29c54302), X(0x29d481d7), + X(0x29e3c21e), X(0x29f303d6), X(0x2a0246fd), X(0x2a118b94), + X(0x2a20d198), X(0x2a301909), X(0x2a3f61e6), X(0x2a4eac2c), + X(0x2a5df7dc), X(0x2a6d44f4), X(0x2a7c9374), X(0x2a8be359), + X(0x2a9b34a2), X(0x2aaa8750), X(0x2ab9db60), X(0x2ac930d1), + X(0x2ad887a3), X(0x2ae7dfd3), X(0x2af73962), X(0x2b06944e), + X(0x2b15f096), X(0x2b254e38), X(0x2b34ad34), X(0x2b440d89), + X(0x2b536f34), X(0x2b62d236), X(0x2b72368d), X(0x2b819c38), + X(0x2b910336), X(0x2ba06b86), X(0x2bafd526), X(0x2bbf4015), + X(0x2bceac53), X(0x2bde19de), X(0x2bed88b5), X(0x2bfcf8d7), + X(0x2c0c6a43), X(0x2c1bdcf7), X(0x2c2b50f3), X(0x2c3ac635), + X(0x2c4a3cbd), X(0x2c59b488), X(0x2c692d97), X(0x2c78a7e7), + X(0x2c882378), X(0x2c97a049), X(0x2ca71e58), X(0x2cb69da4), + X(0x2cc61e2c), X(0x2cd59ff0), X(0x2ce522ed), X(0x2cf4a723), + X(0x2d042c90), X(0x2d13b334), X(0x2d233b0d), X(0x2d32c41a), + X(0x2d424e5a), X(0x2d51d9cc), X(0x2d61666e), X(0x2d70f440), + X(0x2d808340), X(0x2d90136e), X(0x2d9fa4c7), X(0x2daf374c), + X(0x2dbecafa), X(0x2dce5fd1), X(0x2dddf5cf), X(0x2ded8cf4), + X(0x2dfd253d), X(0x2e0cbeab), X(0x2e1c593b), X(0x2e2bf4ed), + X(0x2e3b91c0), X(0x2e4b2fb1), X(0x2e5acec1), X(0x2e6a6eee), + X(0x2e7a1037), X(0x2e89b29b), X(0x2e995618), X(0x2ea8faad), + X(0x2eb8a05a), X(0x2ec8471c), X(0x2ed7eef4), X(0x2ee797df), + X(0x2ef741dc), X(0x2f06eceb), X(0x2f16990a), X(0x2f264639), + X(0x2f35f475), X(0x2f45a3bd), X(0x2f555412), X(0x2f650570), + X(0x2f74b7d8), X(0x2f846b48), X(0x2f941fbe), X(0x2fa3d53a), + X(0x2fb38bbb), X(0x2fc3433f), X(0x2fd2fbc5), X(0x2fe2b54c), + X(0x2ff26fd3), X(0x30022b58), X(0x3011e7db), X(0x3021a55a), + X(0x303163d4), X(0x30412348), X(0x3050e3b5), X(0x3060a519), + X(0x30706773), X(0x30802ac3), X(0x308fef06), X(0x309fb43d), + X(0x30af7a65), X(0x30bf417d), X(0x30cf0985), X(0x30ded27a), + X(0x30ee9c5d), X(0x30fe672b), X(0x310e32e3), X(0x311dff85), + X(0x312dcd0f), X(0x313d9b80), X(0x314d6ad7), X(0x315d3b12), + X(0x316d0c30), X(0x317cde31), X(0x318cb113), X(0x319c84d4), + X(0x31ac5974), X(0x31bc2ef1), X(0x31cc054b), X(0x31dbdc7f), + X(0x31ebb48e), X(0x31fb8d74), X(0x320b6733), X(0x321b41c7), + X(0x322b1d31), X(0x323af96e), X(0x324ad67e), X(0x325ab45f), + X(0x326a9311), X(0x327a7291), X(0x328a52e0), X(0x329a33fb), + X(0x32aa15e1), X(0x32b9f892), X(0x32c9dc0c), X(0x32d9c04d), + X(0x32e9a555), X(0x32f98b22), X(0x330971b4), X(0x33195909), + X(0x3329411f), X(0x333929f6), X(0x3349138c), X(0x3358fde1), + X(0x3368e8f2), X(0x3378d4c0), X(0x3388c147), X(0x3398ae89), + X(0x33a89c82), X(0x33b88b32), X(0x33c87a98), X(0x33d86ab2), + X(0x33e85b80), X(0x33f84d00), X(0x34083f30), X(0x34183210), + X(0x3428259f), X(0x343819db), X(0x34480ec3), X(0x34580455), + X(0x3467fa92), X(0x3477f176), X(0x3487e902), X(0x3497e134), + X(0x34a7da0a), X(0x34b7d384), X(0x34c7cda0), X(0x34d7c85e), + X(0x34e7c3bb), X(0x34f7bfb7), X(0x3507bc50), X(0x3517b985), + X(0x3527b756), X(0x3537b5c0), X(0x3547b4c3), X(0x3557b45d), + X(0x3567b48d), X(0x3577b552), X(0x3587b6aa), X(0x3597b895), + X(0x35a7bb12), X(0x35b7be1e), X(0x35c7c1b9), X(0x35d7c5e1), + X(0x35e7ca96), X(0x35f7cfd6), X(0x3607d5a0), X(0x3617dbf3), + X(0x3627e2cd), X(0x3637ea2d), X(0x3647f212), X(0x3657fa7b), + X(0x36680366), X(0x36780cd2), X(0x368816bf), X(0x3698212b), + X(0x36a82c14), X(0x36b83779), X(0x36c8435a), X(0x36d84fb4), + X(0x36e85c88), X(0x36f869d2), X(0x37087793), X(0x371885c9), + X(0x37289473), X(0x3738a38f), X(0x3748b31d), X(0x3758c31a), + X(0x3768d387), X(0x3778e461), X(0x3788f5a7), X(0x37990759), + X(0x37a91975), X(0x37b92bf9), X(0x37c93ee4), X(0x37d95236), + X(0x37e965ed), X(0x37f97a08), X(0x38098e85), X(0x3819a363), + X(0x3829b8a2), X(0x3839ce3f), X(0x3849e43a), X(0x3859fa91), + X(0x386a1143), X(0x387a284f), X(0x388a3fb4), X(0x389a5770), + X(0x38aa6f83), X(0x38ba87ea), X(0x38caa0a5), X(0x38dab9b2), + X(0x38ead311), X(0x38faecbf), X(0x390b06bc), X(0x391b2107), + X(0x392b3b9e), X(0x393b5680), X(0x394b71ac), X(0x395b8d20), + X(0x396ba8dc), X(0x397bc4dd), X(0x398be124), X(0x399bfdae), + X(0x39ac1a7a), X(0x39bc3788), X(0x39cc54d5), X(0x39dc7261), + X(0x39ec902a), X(0x39fcae2f), X(0x3a0ccc70), X(0x3a1ceaea), + X(0x3a2d099c), X(0x3a3d2885), X(0x3a4d47a5), X(0x3a5d66f9), + X(0x3a6d8680), X(0x3a7da63a), X(0x3a8dc625), X(0x3a9de63f), + X(0x3aae0688), X(0x3abe26fe), X(0x3ace47a0), X(0x3ade686d), + X(0x3aee8963), X(0x3afeaa82), X(0x3b0ecbc7), X(0x3b1eed32), + X(0x3b2f0ec2), X(0x3b3f3075), X(0x3b4f524a), X(0x3b5f7440), + X(0x3b6f9656), X(0x3b7fb889), X(0x3b8fdada), X(0x3b9ffd46), + X(0x3bb01fce), X(0x3bc0426e), X(0x3bd06526), X(0x3be087f6), + X(0x3bf0aada), X(0x3c00cdd4), X(0x3c10f0e0), X(0x3c2113fe), + X(0x3c31372d), X(0x3c415a6b), X(0x3c517db7), X(0x3c61a110), + X(0x3c71c475), X(0x3c81e7e4), X(0x3c920b5c), X(0x3ca22edc), + X(0x3cb25262), X(0x3cc275ee), X(0x3cd2997e), X(0x3ce2bd11), + X(0x3cf2e0a6), X(0x3d03043b), X(0x3d1327cf), X(0x3d234b61), + X(0x3d336ef0), X(0x3d43927a), X(0x3d53b5ff), X(0x3d63d97c), + X(0x3d73fcf1), X(0x3d84205c), X(0x3d9443bd), X(0x3da46711), + X(0x3db48a58), X(0x3dc4ad91), X(0x3dd4d0ba), X(0x3de4f3d1), + X(0x3df516d7), X(0x3e0539c9), X(0x3e155ca6), X(0x3e257f6d), + X(0x3e35a21d), X(0x3e45c4b4), X(0x3e55e731), X(0x3e660994), + X(0x3e762bda), X(0x3e864e03), X(0x3e96700d), X(0x3ea691f7), + X(0x3eb6b3bf), X(0x3ec6d565), X(0x3ed6f6e8), X(0x3ee71845), + X(0x3ef7397c), X(0x3f075a8c), X(0x3f177b73), X(0x3f279c30), + X(0x3f37bcc2), X(0x3f47dd27), X(0x3f57fd5f), X(0x3f681d68), + X(0x3f783d40), X(0x3f885ce7), X(0x3f987c5c), X(0x3fa89b9c), + X(0x3fb8baa7), X(0x3fc8d97c), X(0x3fd8f819), X(0x3fe9167e), + X(0x3ff934a8), X(0x40095296), X(0x40197049), X(0x40298dbd), + X(0x4039aaf2), X(0x4049c7e7), X(0x4059e49a), X(0x406a010a), + X(0x407a1d36), X(0x408a391d), X(0x409a54bd), X(0x40aa7015), + X(0x40ba8b25), X(0x40caa5ea), X(0x40dac063), X(0x40eada90), + X(0x40faf46e), X(0x410b0dfe), X(0x411b273d), X(0x412b402a), + X(0x413b58c4), X(0x414b710a), X(0x415b88fa), X(0x416ba093), + X(0x417bb7d5), X(0x418bcebe), X(0x419be54c), X(0x41abfb7e), + X(0x41bc1153), X(0x41cc26ca), X(0x41dc3be2), X(0x41ec5099), + X(0x41fc64ef), X(0x420c78e1), X(0x421c8c6f), X(0x422c9f97), + X(0x423cb258), X(0x424cc4b2), X(0x425cd6a2), X(0x426ce827), + X(0x427cf941), X(0x428d09ee), X(0x429d1a2c), X(0x42ad29fb), + X(0x42bd3959), X(0x42cd4846), X(0x42dd56bf), X(0x42ed64c3), + X(0x42fd7252), X(0x430d7f6a), X(0x431d8c0a), X(0x432d9831), + X(0x433da3dd), X(0x434daf0d), X(0x435db9c0), X(0x436dc3f5), + X(0x437dcdab), X(0x438dd6df), X(0x439ddf92), X(0x43ade7c1), + X(0x43bdef6c), X(0x43cdf691), X(0x43ddfd2f), X(0x43ee0345), + X(0x43fe08d2), X(0x440e0dd4), X(0x441e124b), X(0x442e1634), + X(0x443e198f), X(0x444e1c5a), X(0x445e1e95), X(0x446e203e), + X(0x447e2153), X(0x448e21d5), X(0x449e21c0), X(0x44ae2115), + X(0x44be1fd1), X(0x44ce1df4), X(0x44de1b7d), X(0x44ee186a), + X(0x44fe14ba), X(0x450e106b), X(0x451e0b7e), X(0x452e05ef), + X(0x453dffbf), X(0x454df8eb), X(0x455df173), X(0x456de956), + X(0x457de092), X(0x458dd726), X(0x459dcd10), X(0x45adc251), + X(0x45bdb6e5), X(0x45cdaacd), X(0x45dd9e06), X(0x45ed9091), + X(0x45fd826a), X(0x460d7392), X(0x461d6407), X(0x462d53c8), + X(0x463d42d4), X(0x464d3129), X(0x465d1ec6), X(0x466d0baa), + X(0x467cf7d3), X(0x468ce342), X(0x469ccdf3), X(0x46acb7e7), + X(0x46bca11c), X(0x46cc8990), X(0x46dc7143), X(0x46ec5833), + X(0x46fc3e5f), X(0x470c23c6), X(0x471c0867), X(0x472bec40), + X(0x473bcf50), X(0x474bb196), X(0x475b9311), X(0x476b73c0), + X(0x477b53a1), X(0x478b32b4), X(0x479b10f6), X(0x47aaee67), + X(0x47bacb06), X(0x47caa6d1), X(0x47da81c7), X(0x47ea5be7), + X(0x47fa3530), X(0x480a0da1), X(0x4819e537), X(0x4829bbf3), + X(0x483991d3), X(0x484966d6), X(0x48593afb), X(0x48690e3f), + X(0x4878e0a3), X(0x4888b225), X(0x489882c4), X(0x48a8527e), + X(0x48b82153), X(0x48c7ef41), X(0x48d7bc47), X(0x48e78863), + X(0x48f75396), X(0x49071ddc), X(0x4916e736), X(0x4926afa2), + X(0x4936771f), X(0x49463dac), X(0x49560347), X(0x4965c7ef), + X(0x49758ba4), X(0x49854e63), X(0x4995102c), X(0x49a4d0fe), + X(0x49b490d7), X(0x49c44fb6), X(0x49d40d9a), X(0x49e3ca82), + X(0x49f3866c), X(0x4a034159), X(0x4a12fb45), X(0x4a22b430), + X(0x4a326c19), X(0x4a4222ff), X(0x4a51d8e1), X(0x4a618dbd), + X(0x4a714192), X(0x4a80f45f), X(0x4a90a623), X(0x4aa056dd), + X(0x4ab0068b), X(0x4abfb52c), X(0x4acf62c0), X(0x4adf0f44), + X(0x4aeebab9), X(0x4afe651c), X(0x4b0e0e6c), X(0x4b1db6a9), + X(0x4b2d5dd1), X(0x4b3d03e2), X(0x4b4ca8dd), X(0x4b5c4cbf), + X(0x4b6bef88), X(0x4b7b9136), X(0x4b8b31c8), X(0x4b9ad13d), + X(0x4baa6f93), X(0x4bba0ccb), X(0x4bc9a8e2), X(0x4bd943d7), + X(0x4be8dda9), X(0x4bf87658), X(0x4c080de1), X(0x4c17a444), + X(0x4c27397f), X(0x4c36cd92), X(0x4c46607b), X(0x4c55f239), + X(0x4c6582cb), X(0x4c75122f), X(0x4c84a065), X(0x4c942d6c), + X(0x4ca3b942), X(0x4cb343e6), X(0x4cc2cd57), X(0x4cd25594), + X(0x4ce1dc9c), X(0x4cf1626d), X(0x4d00e707), X(0x4d106a68), + X(0x4d1fec8f), X(0x4d2f6d7a), X(0x4d3eed2a), X(0x4d4e6b9d), + X(0x4d5de8d1), X(0x4d6d64c5), X(0x4d7cdf79), X(0x4d8c58eb), + X(0x4d9bd11a), X(0x4dab4804), X(0x4dbabdaa), X(0x4dca3209), + X(0x4dd9a520), X(0x4de916ef), X(0x4df88774), X(0x4e07f6ae), + X(0x4e17649c), X(0x4e26d13c), X(0x4e363c8f), X(0x4e45a692), + X(0x4e550f44), X(0x4e6476a4), X(0x4e73dcb2), X(0x4e83416c), + X(0x4e92a4d1), X(0x4ea206df), X(0x4eb16796), X(0x4ec0c6f5), + X(0x4ed024fa), X(0x4edf81a5), X(0x4eeedcf3), X(0x4efe36e5), + X(0x4f0d8f79), X(0x4f1ce6ad), X(0x4f2c3c82), X(0x4f3b90f4), + X(0x4f4ae405), X(0x4f5a35b1), X(0x4f6985fa), X(0x4f78d4dc), + X(0x4f882257), X(0x4f976e6a), X(0x4fa6b914), X(0x4fb60254), + X(0x4fc54a28), X(0x4fd49090), X(0x4fe3d58b), X(0x4ff31917), + X(0x50025b33), X(0x50119bde), X(0x5020db17), X(0x503018dd), + X(0x503f552f), X(0x504e900b), X(0x505dc971), X(0x506d0160), + X(0x507c37d7), X(0x508b6cd3), X(0x509aa055), X(0x50a9d25b), + X(0x50b902e4), X(0x50c831ef), X(0x50d75f7b), X(0x50e68b87), + X(0x50f5b612), X(0x5104df1a), X(0x5114069f), X(0x51232ca0), + X(0x5132511a), X(0x5141740f), X(0x5150957b), X(0x515fb55f), + X(0x516ed3b8), X(0x517df087), X(0x518d0bca), X(0x519c257f), + X(0x51ab3da7), X(0x51ba543f), X(0x51c96947), X(0x51d87cbd), + X(0x51e78ea1), X(0x51f69ef1), X(0x5205adad), X(0x5214bad3), + X(0x5223c662), X(0x5232d05a), X(0x5241d8b9), X(0x5250df7d), + X(0x525fe4a7), X(0x526ee835), X(0x527dea26), X(0x528cea78), + X(0x529be92c), X(0x52aae63f), X(0x52b9e1b0), X(0x52c8db80), + X(0x52d7d3ac), X(0x52e6ca33), X(0x52f5bf15), X(0x5304b251), + X(0x5313a3e5), X(0x532293d0), X(0x53318212), X(0x53406ea8), + X(0x534f5993), X(0x535e42d2), X(0x536d2a62), X(0x537c1043), + X(0x538af475), X(0x5399d6f6), X(0x53a8b7c4), X(0x53b796e0), + X(0x53c67447), X(0x53d54ffa), X(0x53e429f6), X(0x53f3023b), + X(0x5401d8c8), X(0x5410ad9c), X(0x541f80b5), X(0x542e5213), + X(0x543d21b5), X(0x544bef9a), X(0x545abbc0), X(0x54698627), + X(0x54784ece), X(0x548715b3), X(0x5495dad6), X(0x54a49e35), + X(0x54b35fd0), X(0x54c21fa6), X(0x54d0ddb5), X(0x54df99fd), + X(0x54ee547c), X(0x54fd0d32), X(0x550bc41d), X(0x551a793d), + X(0x55292c91), X(0x5537de16), X(0x55468dce), X(0x55553bb6), + X(0x5563e7cd), X(0x55729213), X(0x55813a87), X(0x558fe127), + X(0x559e85f2), X(0x55ad28e9), X(0x55bbca08), X(0x55ca6950), + X(0x55d906c0), X(0x55e7a257), X(0x55f63c13), X(0x5604d3f4), + X(0x561369f8), X(0x5621fe1f), X(0x56309067), X(0x563f20d1), + X(0x564daf5a), X(0x565c3c02), X(0x566ac6c7), X(0x56794faa), + X(0x5687d6a8), X(0x56965bc1), X(0x56a4def4), X(0x56b36040), + X(0x56c1dfa4), X(0x56d05d1f), X(0x56ded8af), X(0x56ed5255), + X(0x56fbca0f), X(0x570a3fdc), X(0x5718b3bc), X(0x572725ac), + X(0x573595ad), X(0x574403bd), X(0x57526fdb), X(0x5760da07), + X(0x576f423f), X(0x577da883), X(0x578c0cd1), X(0x579a6f29), + X(0x57a8cf8a), X(0x57b72df2), X(0x57c58a61), X(0x57d3e4d6), + X(0x57e23d50), X(0x57f093cd), X(0x57fee84e), X(0x580d3ad1), + X(0x581b8b54), X(0x5829d9d8), X(0x5838265c), X(0x584670dd), + X(0x5854b95c), X(0x5862ffd8), X(0x5871444f), X(0x587f86c1), + X(0x588dc72c), X(0x589c0591), X(0x58aa41ed), X(0x58b87c40), + X(0x58c6b489), X(0x58d4eac7), X(0x58e31ef9), X(0x58f1511f), + X(0x58ff8137), X(0x590daf40), X(0x591bdb3a), X(0x592a0524), + X(0x59382cfc), X(0x594652c2), X(0x59547675), X(0x59629815), + X(0x5970b79f), X(0x597ed513), X(0x598cf071), X(0x599b09b7), + X(0x59a920e5), X(0x59b735f9), X(0x59c548f4), X(0x59d359d2), + X(0x59e16895), X(0x59ef753b), X(0x59fd7fc4), X(0x5a0b882d), + X(0x5a198e77), X(0x5a2792a0), X(0x5a3594a9), X(0x5a43948e), + X(0x5a519251), X(0x5a5f8df0), X(0x5a6d876a), X(0x5a7b7ebe), + X(0x5a8973ec), X(0x5a9766f2), X(0x5aa557d0), X(0x5ab34685), + X(0x5ac1330f), X(0x5acf1d6f), X(0x5add05a3), X(0x5aeaebaa), + X(0x5af8cf84), X(0x5b06b12f), X(0x5b1490ab), X(0x5b226df7), + X(0x5b304912), X(0x5b3e21fc), X(0x5b4bf8b2), X(0x5b59cd35), + X(0x5b679f84), X(0x5b756f9e), X(0x5b833d82), X(0x5b91092e), + X(0x5b9ed2a3), X(0x5bac99e0), X(0x5bba5ee3), X(0x5bc821ac), + X(0x5bd5e23a), X(0x5be3a08c), X(0x5bf15ca1), X(0x5bff1679), + X(0x5c0cce12), X(0x5c1a836c), X(0x5c283686), X(0x5c35e760), + X(0x5c4395f7), X(0x5c51424c), X(0x5c5eec5e), X(0x5c6c942b), + X(0x5c7a39b4), X(0x5c87dcf7), X(0x5c957df3), X(0x5ca31ca8), + X(0x5cb0b915), X(0x5cbe5338), X(0x5ccbeb12), X(0x5cd980a1), + X(0x5ce713e5), X(0x5cf4a4dd), X(0x5d023387), X(0x5d0fbfe4), + X(0x5d1d49f2), X(0x5d2ad1b1), X(0x5d38571f), X(0x5d45da3c), + X(0x5d535b08), X(0x5d60d981), X(0x5d6e55a7), X(0x5d7bcf78), + X(0x5d8946f5), X(0x5d96bc1c), X(0x5da42eec), X(0x5db19f65), + X(0x5dbf0d86), X(0x5dcc794e), X(0x5dd9e2bd), X(0x5de749d1), + X(0x5df4ae8a), X(0x5e0210e7), X(0x5e0f70e7), X(0x5e1cce8a), + X(0x5e2a29ce), X(0x5e3782b4), X(0x5e44d93a), X(0x5e522d5f), + X(0x5e5f7f23), X(0x5e6cce85), X(0x5e7a1b85), X(0x5e876620), + X(0x5e94ae58), X(0x5ea1f42a), X(0x5eaf3797), X(0x5ebc789d), + X(0x5ec9b73c), X(0x5ed6f372), X(0x5ee42d41), X(0x5ef164a5), + X(0x5efe999f), X(0x5f0bcc2f), X(0x5f18fc52), X(0x5f262a09), + X(0x5f335553), X(0x5f407e2f), X(0x5f4da49d), X(0x5f5ac89b), + X(0x5f67ea29), X(0x5f750946), X(0x5f8225f2), X(0x5f8f402b), + X(0x5f9c57f2), X(0x5fa96d44), X(0x5fb68023), X(0x5fc3908c), + X(0x5fd09e7f), X(0x5fdda9fc), X(0x5feab302), X(0x5ff7b990), + X(0x6004bda5), X(0x6011bf40), X(0x601ebe62), X(0x602bbb09), + X(0x6038b534), X(0x6045ace4), X(0x6052a216), X(0x605f94cb), + X(0x606c8502), X(0x607972b9), X(0x60865df2), X(0x609346aa), + X(0x60a02ce1), X(0x60ad1096), X(0x60b9f1c9), X(0x60c6d079), + X(0x60d3aca5), X(0x60e0864d), X(0x60ed5d70), X(0x60fa320d), + X(0x61070424), X(0x6113d3b4), X(0x6120a0bc), X(0x612d6b3c), + X(0x613a3332), X(0x6146f89f), X(0x6153bb82), X(0x61607bd9), + X(0x616d39a5), X(0x6179f4e5), X(0x6186ad98), X(0x619363bd), + X(0x61a01753), X(0x61acc85b), X(0x61b976d3), X(0x61c622bc), + X(0x61d2cc13), X(0x61df72d8), X(0x61ec170c), X(0x61f8b8ad), + X(0x620557ba), X(0x6211f434), X(0x621e8e18), X(0x622b2568), + X(0x6237ba21), X(0x62444c44), X(0x6250dbd0), X(0x625d68c4), + X(0x6269f320), X(0x62767ae2), X(0x6283000b), X(0x628f829a), + X(0x629c028e), X(0x62a87fe6), X(0x62b4faa2), X(0x62c172c2), + X(0x62cde844), X(0x62da5b29), X(0x62e6cb6e), X(0x62f33915), + X(0x62ffa41c), X(0x630c0c83), X(0x63187248), X(0x6324d56d), + X(0x633135ef), X(0x633d93ce), X(0x6349ef0b), X(0x635647a3), + X(0x63629d97), X(0x636ef0e6), X(0x637b418f), X(0x63878f92), + X(0x6393daef), X(0x63a023a4), X(0x63ac69b1), X(0x63b8ad15), + X(0x63c4edd1), X(0x63d12be3), X(0x63dd674b), X(0x63e9a008), + X(0x63f5d61a), X(0x64020980), X(0x640e3a39), X(0x641a6846), + X(0x642693a5), X(0x6432bc56), X(0x643ee258), X(0x644b05ab), + X(0x6457264e), X(0x64634441), X(0x646f5f83), X(0x647b7814), + X(0x64878df3), X(0x6493a120), X(0x649fb199), X(0x64abbf5f), + X(0x64b7ca71), X(0x64c3d2ce), X(0x64cfd877), X(0x64dbdb69), + X(0x64e7dba6), X(0x64f3d92b), X(0x64ffd3fa), X(0x650bcc11), + X(0x6517c16f), X(0x6523b415), X(0x652fa402), X(0x653b9134), + X(0x65477bad), X(0x6553636a), X(0x655f486d), X(0x656b2ab3), + X(0x65770a3d), X(0x6582e70a), X(0x658ec11a), X(0x659a986d), + X(0x65a66d00), X(0x65b23ed5), X(0x65be0deb), X(0x65c9da41), + X(0x65d5a3d7), X(0x65e16aac), X(0x65ed2ebf), X(0x65f8f011), + X(0x6604aea1), X(0x66106a6e), X(0x661c2377), X(0x6627d9be), + X(0x66338d40), X(0x663f3dfd), X(0x664aebf5), X(0x66569728), + X(0x66623f95), X(0x666de53b), X(0x6679881b), X(0x66852833), + X(0x6690c583), X(0x669c600b), X(0x66a7f7ca), X(0x66b38cc0), + X(0x66bf1eec), X(0x66caae4f), X(0x66d63ae6), X(0x66e1c4b3), + X(0x66ed4bb4), X(0x66f8cfea), X(0x67045153), X(0x670fcfef), + X(0x671b4bbe), X(0x6726c4bf), X(0x67323af3), X(0x673dae58), + X(0x67491eee), X(0x67548cb5), X(0x675ff7ab), X(0x676b5fd2), + X(0x6776c528), X(0x678227ad), X(0x678d8761), X(0x6798e443), + X(0x67a43e52), X(0x67af958f), X(0x67bae9f9), X(0x67c63b8f), + X(0x67d18a52), X(0x67dcd640), X(0x67e81f59), X(0x67f3659d), + X(0x67fea90c), X(0x6809e9a5), X(0x68152768), X(0x68206254), + X(0x682b9a68), X(0x6836cfa6), X(0x6842020b), X(0x684d3199), + X(0x68585e4d), X(0x68638829), X(0x686eaf2b), X(0x6879d354), + X(0x6884f4a2), X(0x68901316), X(0x689b2eb0), X(0x68a6476d), + X(0x68b15d50), X(0x68bc7056), X(0x68c78080), X(0x68d28dcd), + X(0x68dd983e), X(0x68e89fd0), X(0x68f3a486), X(0x68fea65d), + X(0x6909a555), X(0x6914a16f), X(0x691f9aa9), X(0x692a9104), + X(0x69358480), X(0x6940751b), X(0x694b62d5), X(0x69564daf), + X(0x696135a7), X(0x696c1abe), X(0x6976fcf3), X(0x6981dc46), + X(0x698cb8b6), X(0x69979243), X(0x69a268ed), X(0x69ad3cb4), + X(0x69b80d97), X(0x69c2db96), X(0x69cda6b0), X(0x69d86ee5), + X(0x69e33436), X(0x69edf6a1), X(0x69f8b626), X(0x6a0372c5), + X(0x6a0e2c7e), X(0x6a18e350), X(0x6a23973c), X(0x6a2e4840), + X(0x6a38f65d), X(0x6a43a191), X(0x6a4e49de), X(0x6a58ef42), + X(0x6a6391be), X(0x6a6e3151), X(0x6a78cdfa), X(0x6a8367ba), + X(0x6a8dfe90), X(0x6a98927c), X(0x6aa3237d), X(0x6aadb194), + X(0x6ab83cc0), X(0x6ac2c500), X(0x6acd4a55), X(0x6ad7ccbf), + X(0x6ae24c3c), X(0x6aecc8cd), X(0x6af74271), X(0x6b01b929), + X(0x6b0c2cf4), X(0x6b169dd1), X(0x6b210bc1), X(0x6b2b76c2), + X(0x6b35ded6), X(0x6b4043fc), X(0x6b4aa632), X(0x6b55057a), + X(0x6b5f61d3), X(0x6b69bb3d), X(0x6b7411b7), X(0x6b7e6541), + X(0x6b88b5db), X(0x6b930385), X(0x6b9d4e3f), X(0x6ba79607), + X(0x6bb1dadf), X(0x6bbc1cc6), X(0x6bc65bbb), X(0x6bd097bf), + X(0x6bdad0d0), X(0x6be506f0), X(0x6bef3a1d), X(0x6bf96a58), + X(0x6c0397a0), X(0x6c0dc1f5), X(0x6c17e957), X(0x6c220dc6), + X(0x6c2c2f41), X(0x6c364dc9), X(0x6c40695c), X(0x6c4a81fc), + X(0x6c5497a7), X(0x6c5eaa5d), X(0x6c68ba1f), X(0x6c72c6eb), + X(0x6c7cd0c3), X(0x6c86d7a6), X(0x6c90db92), X(0x6c9adc8a), + X(0x6ca4da8b), X(0x6caed596), X(0x6cb8cdab), X(0x6cc2c2ca), + X(0x6cccb4f2), X(0x6cd6a424), X(0x6ce0905e), X(0x6cea79a1), + X(0x6cf45fee), X(0x6cfe4342), X(0x6d0823a0), X(0x6d120105), + X(0x6d1bdb73), X(0x6d25b2e8), X(0x6d2f8765), X(0x6d3958ea), + X(0x6d432777), X(0x6d4cf30a), X(0x6d56bba5), X(0x6d608147), + X(0x6d6a43f0), X(0x6d7403a0), X(0x6d7dc056), X(0x6d877a13), + X(0x6d9130d6), X(0x6d9ae4a0), X(0x6da4956f), X(0x6dae4345), + X(0x6db7ee20), X(0x6dc19601), X(0x6dcb3ae7), X(0x6dd4dcd3), + X(0x6dde7bc4), X(0x6de817bb), X(0x6df1b0b6), X(0x6dfb46b7), + X(0x6e04d9bc), X(0x6e0e69c7), X(0x6e17f6d5), X(0x6e2180e9), + X(0x6e2b0801), X(0x6e348c1d), X(0x6e3e0d3d), X(0x6e478b62), + X(0x6e51068a), X(0x6e5a7eb7), X(0x6e63f3e7), X(0x6e6d661b), + X(0x6e76d552), X(0x6e80418e), X(0x6e89aacc), X(0x6e93110f), + X(0x6e9c7454), X(0x6ea5d49d), X(0x6eaf31e9), X(0x6eb88c37), + X(0x6ec1e389), X(0x6ecb37de), X(0x6ed48936), X(0x6eddd790), + X(0x6ee722ee), X(0x6ef06b4d), X(0x6ef9b0b0), X(0x6f02f315), + X(0x6f0c327c), X(0x6f156ee6), X(0x6f1ea852), X(0x6f27dec1), + X(0x6f311232), X(0x6f3a42a5), X(0x6f43701a), X(0x6f4c9a91), + X(0x6f55c20a), X(0x6f5ee686), X(0x6f680803), X(0x6f712682), + X(0x6f7a4203), X(0x6f835a86), X(0x6f8c700b), X(0x6f958291), + X(0x6f9e921a), X(0x6fa79ea4), X(0x6fb0a830), X(0x6fb9aebd), + X(0x6fc2b24c), X(0x6fcbb2dd), X(0x6fd4b06f), X(0x6fddab03), + X(0x6fe6a299), X(0x6fef9730), X(0x6ff888c9), X(0x70017763), + X(0x700a62ff), X(0x70134b9c), X(0x701c313b), X(0x702513dc), + X(0x702df37e), X(0x7036d021), X(0x703fa9c6), X(0x7048806d), + X(0x70515415), X(0x705a24bf), X(0x7062f26b), X(0x706bbd17), + X(0x707484c6), X(0x707d4976), X(0x70860b28), X(0x708ec9dc), + X(0x70978591), X(0x70a03e48), X(0x70a8f400), X(0x70b1a6bb), + X(0x70ba5677), X(0x70c30335), X(0x70cbacf5), X(0x70d453b6), + X(0x70dcf77a), X(0x70e59840), X(0x70ee3607), X(0x70f6d0d1), + X(0x70ff689d), X(0x7107fd6b), X(0x71108f3b), X(0x71191e0d), + X(0x7121a9e2), X(0x712a32b9), X(0x7132b892), X(0x713b3b6e), + X(0x7143bb4c), X(0x714c382d), X(0x7154b211), X(0x715d28f7), + X(0x71659ce0), X(0x716e0dcc), X(0x71767bbb), X(0x717ee6ac), + X(0x71874ea1), X(0x718fb399), X(0x71981594), X(0x71a07493), + X(0x71a8d094), X(0x71b1299a), X(0x71b97fa2), X(0x71c1d2af), + X(0x71ca22bf), X(0x71d26fd2), X(0x71dab9ea), X(0x71e30106), + X(0x71eb4526), X(0x71f3864a), X(0x71fbc472), X(0x7203ff9e), + X(0x720c37cf), X(0x72146d05), X(0x721c9f3f), X(0x7224ce7e), + X(0x722cfac2), X(0x7235240b), X(0x723d4a59), X(0x72456dad), + X(0x724d8e05), X(0x7255ab63), X(0x725dc5c7), X(0x7265dd31), + X(0x726df1a0), X(0x72760315), X(0x727e1191), X(0x72861d12), + X(0x728e259a), X(0x72962b28), X(0x729e2dbd), X(0x72a62d59), + X(0x72ae29fc), X(0x72b623a5), X(0x72be1a56), X(0x72c60e0e), + X(0x72cdfece), X(0x72d5ec95), X(0x72ddd764), X(0x72e5bf3b), + X(0x72eda41a), X(0x72f58601), X(0x72fd64f1), X(0x730540e9), + X(0x730d19e9), X(0x7314eff3), X(0x731cc305), X(0x73249321), + X(0x732c6046), X(0x73342a75), X(0x733bf1ad), X(0x7343b5ef), + X(0x734b773b), X(0x73533591), X(0x735af0f2), X(0x7362a95d), + X(0x736a5ed3), X(0x73721153), X(0x7379c0df), X(0x73816d76), + X(0x73891719), X(0x7390bdc7), X(0x73986181), X(0x73a00247), + X(0x73a7a01a), X(0x73af3af8), X(0x73b6d2e4), X(0x73be67dc), + X(0x73c5f9e1), X(0x73cd88f3), X(0x73d51513), X(0x73dc9e40), + X(0x73e4247c), X(0x73eba7c5), X(0x73f3281c), X(0x73faa582), + X(0x74021ff7), X(0x7409977b), X(0x74110c0d), X(0x74187daf), + X(0x741fec61), X(0x74275822), X(0x742ec0f3), X(0x743626d5), + X(0x743d89c7), X(0x7444e9c9), X(0x744c46dd), X(0x7453a101), + X(0x745af837), X(0x74624c7f), X(0x74699dd8), X(0x7470ec44), + X(0x747837c2), X(0x747f8052), X(0x7486c5f5), X(0x748e08ac), + X(0x74954875), X(0x749c8552), X(0x74a3bf43), X(0x74aaf648), + X(0x74b22a62), X(0x74b95b90), X(0x74c089d2), X(0x74c7b52a), + X(0x74cedd97), X(0x74d6031a), X(0x74dd25b2), X(0x74e44561), + X(0x74eb6226), X(0x74f27c02), X(0x74f992f5), X(0x7500a6ff), + X(0x7507b820), X(0x750ec659), X(0x7515d1aa), X(0x751cda14), + X(0x7523df96), X(0x752ae231), X(0x7531e1e5), X(0x7538deb2), + X(0x753fd89a), X(0x7546cf9b), X(0x754dc3b7), X(0x7554b4ed), + X(0x755ba33e), X(0x75628eaa), X(0x75697732), X(0x75705cd5), + X(0x75773f95), X(0x757e1f71), X(0x7584fc6a), X(0x758bd67f), + X(0x7592adb2), X(0x75998203), X(0x75a05371), X(0x75a721fe), + X(0x75adeda9), X(0x75b4b673), X(0x75bb7c5c), X(0x75c23f65), + X(0x75c8ff8d), X(0x75cfbcd6), X(0x75d6773f), X(0x75dd2ec8), + X(0x75e3e373), X(0x75ea953f), X(0x75f1442d), X(0x75f7f03d), + X(0x75fe996f), X(0x76053fc5), X(0x760be33d), X(0x761283d8), + X(0x76192197), X(0x761fbc7b), X(0x76265482), X(0x762ce9af), + X(0x76337c01), X(0x763a0b78), X(0x76409814), X(0x764721d7), + X(0x764da8c1), X(0x76542cd1), X(0x765aae08), X(0x76612c67), + X(0x7667a7ee), X(0x766e209d), X(0x76749675), X(0x767b0975), + X(0x7681799f), X(0x7687e6f3), X(0x768e5170), X(0x7694b918), + X(0x769b1deb), X(0x76a17fe9), X(0x76a7df13), X(0x76ae3b68), + X(0x76b494ea), X(0x76baeb98), X(0x76c13f74), X(0x76c7907c), + X(0x76cddeb3), X(0x76d42a18), X(0x76da72ab), X(0x76e0b86d), + X(0x76e6fb5e), X(0x76ed3b7f), X(0x76f378d0), X(0x76f9b352), + X(0x76ffeb05), X(0x77061fe8), X(0x770c51fe), X(0x77128145), + X(0x7718adbf), X(0x771ed76c), X(0x7724fe4c), X(0x772b225f), + X(0x773143a7), X(0x77376223), X(0x773d7dd3), X(0x774396ba), + X(0x7749acd5), X(0x774fc027), X(0x7755d0af), X(0x775bde6f), + X(0x7761e965), X(0x7767f193), X(0x776df6fa), X(0x7773f998), + X(0x7779f970), X(0x777ff681), X(0x7785f0cd), X(0x778be852), + X(0x7791dd12), X(0x7797cf0d), X(0x779dbe43), X(0x77a3aab6), + X(0x77a99465), X(0x77af7b50), X(0x77b55f79), X(0x77bb40e0), + X(0x77c11f85), X(0x77c6fb68), X(0x77ccd48a), X(0x77d2aaec), + X(0x77d87e8d), X(0x77de4f6f), X(0x77e41d92), X(0x77e9e8f5), + X(0x77efb19b), X(0x77f57782), X(0x77fb3aad), X(0x7800fb1a), + X(0x7806b8ca), X(0x780c73bf), X(0x78122bf7), X(0x7817e175), + X(0x781d9438), X(0x78234440), X(0x7828f18f), X(0x782e9c25), + X(0x78344401), X(0x7839e925), X(0x783f8b92), X(0x78452b46), + X(0x784ac844), X(0x7850628b), X(0x7855fa1c), X(0x785b8ef8), + X(0x7861211e), X(0x7866b090), X(0x786c3d4d), X(0x7871c757), + X(0x78774ead), X(0x787cd351), X(0x78825543), X(0x7887d483), + X(0x788d5111), X(0x7892caef), X(0x7898421c), X(0x789db69a), + X(0x78a32868), X(0x78a89787), X(0x78ae03f8), X(0x78b36dbb), + X(0x78b8d4d1), X(0x78be393a), X(0x78c39af6), X(0x78c8fa06), + X(0x78ce566c), X(0x78d3b026), X(0x78d90736), X(0x78de5b9c), + X(0x78e3ad58), X(0x78e8fc6c), X(0x78ee48d7), X(0x78f3929b), + X(0x78f8d9b7), X(0x78fe1e2c), X(0x79035ffb), X(0x79089f24), + X(0x790ddba8), X(0x79131587), X(0x79184cc2), X(0x791d8159), + X(0x7922b34d), X(0x7927e29e), X(0x792d0f4d), X(0x7932395a), + X(0x793760c6), X(0x793c8591), X(0x7941a7bd), X(0x7946c749), + X(0x794be435), X(0x7950fe84), X(0x79561634), X(0x795b2b47), + X(0x79603dbc), X(0x79654d96), X(0x796a5ad4), X(0x796f6576), + X(0x79746d7e), X(0x797972eb), X(0x797e75bf), X(0x798375f9), + X(0x7988739b), X(0x798d6ea5), X(0x79926717), X(0x79975cf2), + X(0x799c5037), X(0x79a140e6), X(0x79a62f00), X(0x79ab1a85), + X(0x79b00376), X(0x79b4e9d3), X(0x79b9cd9d), X(0x79beaed4), + X(0x79c38d79), X(0x79c8698d), X(0x79cd4310), X(0x79d21a03), + X(0x79d6ee66), X(0x79dbc03a), X(0x79e08f7f), X(0x79e55c36), + X(0x79ea265f), X(0x79eeedfc), X(0x79f3b30c), X(0x79f87590), + X(0x79fd3589), X(0x7a01f2f7), X(0x7a06addc), X(0x7a0b6636), + X(0x7a101c08), X(0x7a14cf52), X(0x7a198013), X(0x7a1e2e4d), + X(0x7a22da01), X(0x7a27832f), X(0x7a2c29d7), X(0x7a30cdfa), + X(0x7a356f99), X(0x7a3a0eb4), X(0x7a3eab4c), X(0x7a434561), + X(0x7a47dcf5), X(0x7a4c7207), X(0x7a510498), X(0x7a5594a9), + X(0x7a5a223a), X(0x7a5ead4d), X(0x7a6335e0), X(0x7a67bbf6), + X(0x7a6c3f8f), X(0x7a70c0ab), X(0x7a753f4b), X(0x7a79bb6f), + X(0x7a7e3519), X(0x7a82ac48), X(0x7a8720fe), X(0x7a8b933b), + X(0x7a9002ff), X(0x7a94704b), X(0x7a98db20), X(0x7a9d437e), + X(0x7aa1a967), X(0x7aa60cd9), X(0x7aaa6dd7), X(0x7aaecc61), + X(0x7ab32877), X(0x7ab7821b), X(0x7abbd94b), X(0x7ac02e0a), + X(0x7ac48058), X(0x7ac8d035), X(0x7acd1da3), X(0x7ad168a1), + X(0x7ad5b130), X(0x7ad9f751), X(0x7ade3b05), X(0x7ae27c4c), + X(0x7ae6bb27), X(0x7aeaf796), X(0x7aef319a), X(0x7af36934), + X(0x7af79e64), X(0x7afbd12c), X(0x7b00018a), X(0x7b042f81), + X(0x7b085b10), X(0x7b0c8439), X(0x7b10aafc), X(0x7b14cf5a), + X(0x7b18f153), X(0x7b1d10e8), X(0x7b212e1a), X(0x7b2548e9), + X(0x7b296155), X(0x7b2d7761), X(0x7b318b0b), X(0x7b359c55), + X(0x7b39ab3f), X(0x7b3db7cb), X(0x7b41c1f8), X(0x7b45c9c8), + X(0x7b49cf3b), X(0x7b4dd251), X(0x7b51d30b), X(0x7b55d16b), + X(0x7b59cd70), X(0x7b5dc71b), X(0x7b61be6d), X(0x7b65b366), + X(0x7b69a608), X(0x7b6d9653), X(0x7b718447), X(0x7b756fe5), + X(0x7b79592e), X(0x7b7d4022), X(0x7b8124c3), X(0x7b850710), + X(0x7b88e70a), X(0x7b8cc4b3), X(0x7b90a00a), X(0x7b947911), + X(0x7b984fc8), X(0x7b9c242f), X(0x7b9ff648), X(0x7ba3c612), + X(0x7ba79390), X(0x7bab5ec1), X(0x7baf27a5), X(0x7bb2ee3f), + X(0x7bb6b28e), X(0x7bba7493), X(0x7bbe344e), X(0x7bc1f1c1), + X(0x7bc5acec), X(0x7bc965cf), X(0x7bcd1c6c), X(0x7bd0d0c3), + X(0x7bd482d4), X(0x7bd832a1), X(0x7bdbe02a), X(0x7bdf8b70), + X(0x7be33473), X(0x7be6db34), X(0x7bea7fb4), X(0x7bee21f4), + X(0x7bf1c1f3), X(0x7bf55fb3), X(0x7bf8fb35), X(0x7bfc9479), + X(0x7c002b7f), X(0x7c03c04a), X(0x7c0752d8), X(0x7c0ae32b), + X(0x7c0e7144), X(0x7c11fd23), X(0x7c1586c9), X(0x7c190e36), + X(0x7c1c936c), X(0x7c20166b), X(0x7c239733), X(0x7c2715c6), + X(0x7c2a9224), X(0x7c2e0c4e), X(0x7c318444), X(0x7c34fa07), + X(0x7c386d98), X(0x7c3bdef8), X(0x7c3f4e26), X(0x7c42bb25), + X(0x7c4625f4), X(0x7c498e95), X(0x7c4cf507), X(0x7c50594c), + X(0x7c53bb65), X(0x7c571b51), X(0x7c5a7913), X(0x7c5dd4aa), + X(0x7c612e17), X(0x7c64855b), X(0x7c67da76), X(0x7c6b2d6a), + X(0x7c6e7e37), X(0x7c71ccdd), X(0x7c75195e), X(0x7c7863ba), + X(0x7c7babf1), X(0x7c7ef206), X(0x7c8235f7), X(0x7c8577c6), + X(0x7c88b774), X(0x7c8bf502), X(0x7c8f306f), X(0x7c9269bd), + X(0x7c95a0ec), X(0x7c98d5fe), X(0x7c9c08f2), X(0x7c9f39cb), + X(0x7ca26887), X(0x7ca59528), X(0x7ca8bfb0), X(0x7cabe81d), + X(0x7caf0e72), X(0x7cb232af), X(0x7cb554d4), X(0x7cb874e2), + X(0x7cbb92db), X(0x7cbeaebe), X(0x7cc1c88d), X(0x7cc4e047), + X(0x7cc7f5ef), X(0x7ccb0984), X(0x7cce1b08), X(0x7cd12a7b), + X(0x7cd437dd), X(0x7cd74330), X(0x7cda4c74), X(0x7cdd53aa), + X(0x7ce058d3), X(0x7ce35bef), X(0x7ce65cff), X(0x7ce95c04), + X(0x7cec58ff), X(0x7cef53f0), X(0x7cf24cd7), X(0x7cf543b7), + X(0x7cf8388f), X(0x7cfb2b60), X(0x7cfe1c2b), X(0x7d010af1), + X(0x7d03f7b2), X(0x7d06e26f), X(0x7d09cb29), X(0x7d0cb1e0), + X(0x7d0f9696), X(0x7d12794b), X(0x7d1559ff), X(0x7d1838b4), + X(0x7d1b156a), X(0x7d1df022), X(0x7d20c8dd), X(0x7d239f9b), + X(0x7d26745e), X(0x7d294725), X(0x7d2c17f1), X(0x7d2ee6c4), + X(0x7d31b39f), X(0x7d347e81), X(0x7d37476b), X(0x7d3a0e5f), + X(0x7d3cd35d), X(0x7d3f9665), X(0x7d425779), X(0x7d451699), + X(0x7d47d3c6), X(0x7d4a8f01), X(0x7d4d484b), X(0x7d4fffa3), + X(0x7d52b50c), X(0x7d556885), X(0x7d581a0f), X(0x7d5ac9ac), + X(0x7d5d775c), X(0x7d60231f), X(0x7d62ccf6), X(0x7d6574e3), + X(0x7d681ae6), X(0x7d6abeff), X(0x7d6d612f), X(0x7d700178), + X(0x7d729fd9), X(0x7d753c54), X(0x7d77d6e9), X(0x7d7a6f9a), + X(0x7d7d0666), X(0x7d7f9b4f), X(0x7d822e55), X(0x7d84bf79), + X(0x7d874ebc), X(0x7d89dc1e), X(0x7d8c67a1), X(0x7d8ef144), + X(0x7d91790a), X(0x7d93fef2), X(0x7d9682fd), X(0x7d99052d), + X(0x7d9b8581), X(0x7d9e03fb), X(0x7da0809b), X(0x7da2fb62), + X(0x7da57451), X(0x7da7eb68), X(0x7daa60a8), X(0x7dacd413), + X(0x7daf45a9), X(0x7db1b56a), X(0x7db42357), X(0x7db68f71), + X(0x7db8f9b9), X(0x7dbb6230), X(0x7dbdc8d6), X(0x7dc02dac), + X(0x7dc290b3), X(0x7dc4f1eb), X(0x7dc75156), X(0x7dc9aef4), + X(0x7dcc0ac5), X(0x7dce64cc), X(0x7dd0bd07), X(0x7dd31379), + X(0x7dd56821), X(0x7dd7bb01), X(0x7dda0c1a), X(0x7ddc5b6b), + X(0x7ddea8f7), X(0x7de0f4bd), X(0x7de33ebe), X(0x7de586fc), + X(0x7de7cd76), X(0x7dea122e), X(0x7dec5525), X(0x7dee965a), + X(0x7df0d5d0), X(0x7df31386), X(0x7df54f7e), X(0x7df789b8), + X(0x7df9c235), X(0x7dfbf8f5), X(0x7dfe2dfa), X(0x7e006145), + X(0x7e0292d5), X(0x7e04c2ac), X(0x7e06f0cb), X(0x7e091d32), + X(0x7e0b47e1), X(0x7e0d70db), X(0x7e0f981f), X(0x7e11bdaf), + X(0x7e13e18a), X(0x7e1603b3), X(0x7e182429), X(0x7e1a42ed), + X(0x7e1c6001), X(0x7e1e7b64), X(0x7e209518), X(0x7e22ad1d), + X(0x7e24c375), X(0x7e26d81f), X(0x7e28eb1d), X(0x7e2afc70), + X(0x7e2d0c17), X(0x7e2f1a15), X(0x7e31266a), X(0x7e333115), + X(0x7e353a1a), X(0x7e374177), X(0x7e39472e), X(0x7e3b4b3f), + X(0x7e3d4dac), X(0x7e3f4e75), X(0x7e414d9a), X(0x7e434b1e), + X(0x7e4546ff), X(0x7e474140), X(0x7e4939e0), X(0x7e4b30e2), + X(0x7e4d2644), X(0x7e4f1a09), X(0x7e510c30), X(0x7e52fcbc), + X(0x7e54ebab), X(0x7e56d900), X(0x7e58c4bb), X(0x7e5aaedd), + X(0x7e5c9766), X(0x7e5e7e57), X(0x7e6063b2), X(0x7e624776), + X(0x7e6429a5), X(0x7e660a3f), X(0x7e67e945), X(0x7e69c6b8), + X(0x7e6ba299), X(0x7e6d7ce7), X(0x7e6f55a5), X(0x7e712cd3), + X(0x7e730272), X(0x7e74d682), X(0x7e76a904), X(0x7e7879f9), + X(0x7e7a4962), X(0x7e7c173f), X(0x7e7de392), X(0x7e7fae5a), + X(0x7e817799), X(0x7e833f50), X(0x7e85057f), X(0x7e86ca27), + X(0x7e888d49), X(0x7e8a4ee5), X(0x7e8c0efd), X(0x7e8dcd91), + X(0x7e8f8aa1), X(0x7e914630), X(0x7e93003c), X(0x7e94b8c8), + X(0x7e966fd4), X(0x7e982560), X(0x7e99d96e), X(0x7e9b8bfe), + X(0x7e9d3d10), X(0x7e9eeca7), X(0x7ea09ac2), X(0x7ea24762), + X(0x7ea3f288), X(0x7ea59c35), X(0x7ea7446a), X(0x7ea8eb27), + X(0x7eaa906c), X(0x7eac343c), X(0x7eadd696), X(0x7eaf777b), + X(0x7eb116ed), X(0x7eb2b4eb), X(0x7eb45177), X(0x7eb5ec91), + X(0x7eb7863b), X(0x7eb91e74), X(0x7ebab53e), X(0x7ebc4a99), + X(0x7ebdde87), X(0x7ebf7107), X(0x7ec1021b), X(0x7ec291c3), + X(0x7ec42001), X(0x7ec5acd5), X(0x7ec7383f), X(0x7ec8c241), + X(0x7eca4adb), X(0x7ecbd20d), X(0x7ecd57da), X(0x7ecedc41), + X(0x7ed05f44), X(0x7ed1e0e2), X(0x7ed3611d), X(0x7ed4dff6), + X(0x7ed65d6d), X(0x7ed7d983), X(0x7ed95438), X(0x7edacd8f), + X(0x7edc4586), X(0x7eddbc20), X(0x7edf315c), X(0x7ee0a53c), + X(0x7ee217c1), X(0x7ee388ea), X(0x7ee4f8b9), X(0x7ee6672f), + X(0x7ee7d44c), X(0x7ee94012), X(0x7eeaaa80), X(0x7eec1397), + X(0x7eed7b59), X(0x7eeee1c6), X(0x7ef046df), X(0x7ef1aaa5), + X(0x7ef30d18), X(0x7ef46e39), X(0x7ef5ce09), X(0x7ef72c88), + X(0x7ef889b8), X(0x7ef9e599), X(0x7efb402c), X(0x7efc9972), + X(0x7efdf16b), X(0x7eff4818), X(0x7f009d79), X(0x7f01f191), + X(0x7f03445f), X(0x7f0495e4), X(0x7f05e620), X(0x7f073516), + X(0x7f0882c5), X(0x7f09cf2d), X(0x7f0b1a51), X(0x7f0c6430), + X(0x7f0daccc), X(0x7f0ef425), X(0x7f103a3b), X(0x7f117f11), + X(0x7f12c2a5), X(0x7f1404fa), X(0x7f15460f), X(0x7f1685e6), + X(0x7f17c47f), X(0x7f1901db), X(0x7f1a3dfb), X(0x7f1b78e0), + X(0x7f1cb28a), X(0x7f1deafa), X(0x7f1f2231), X(0x7f20582f), + X(0x7f218cf5), X(0x7f22c085), X(0x7f23f2de), X(0x7f252401), + X(0x7f2653f0), X(0x7f2782ab), X(0x7f28b032), X(0x7f29dc87), + X(0x7f2b07aa), X(0x7f2c319c), X(0x7f2d5a5e), X(0x7f2e81f0), + X(0x7f2fa853), X(0x7f30cd88), X(0x7f31f18f), X(0x7f33146a), + X(0x7f343619), X(0x7f35569c), X(0x7f3675f6), X(0x7f379425), + X(0x7f38b12c), X(0x7f39cd0a), X(0x7f3ae7c0), X(0x7f3c0150), + X(0x7f3d19ba), X(0x7f3e30fe), X(0x7f3f471e), X(0x7f405c1a), + X(0x7f416ff3), X(0x7f4282a9), X(0x7f43943e), X(0x7f44a4b2), + X(0x7f45b405), X(0x7f46c239), X(0x7f47cf4e), X(0x7f48db45), + X(0x7f49e61f), X(0x7f4aefdc), X(0x7f4bf87e), X(0x7f4d0004), + X(0x7f4e0670), X(0x7f4f0bc2), X(0x7f500ffb), X(0x7f51131c), + X(0x7f521525), X(0x7f531618), X(0x7f5415f4), X(0x7f5514bb), + X(0x7f56126e), X(0x7f570f0c), X(0x7f580a98), X(0x7f590511), + X(0x7f59fe78), X(0x7f5af6ce), X(0x7f5bee14), X(0x7f5ce44a), + X(0x7f5dd972), X(0x7f5ecd8b), X(0x7f5fc097), X(0x7f60b296), + X(0x7f61a389), X(0x7f629370), X(0x7f63824e), X(0x7f647021), + X(0x7f655ceb), X(0x7f6648ad), X(0x7f673367), X(0x7f681d19), + X(0x7f6905c6), X(0x7f69ed6d), X(0x7f6ad40f), X(0x7f6bb9ad), + X(0x7f6c9e48), X(0x7f6d81e0), X(0x7f6e6475), X(0x7f6f460a), + X(0x7f70269d), X(0x7f710631), X(0x7f71e4c6), X(0x7f72c25c), + X(0x7f739ef4), X(0x7f747a8f), X(0x7f75552e), X(0x7f762ed1), + X(0x7f770779), X(0x7f77df27), X(0x7f78b5db), X(0x7f798b97), + X(0x7f7a605a), X(0x7f7b3425), X(0x7f7c06fa), X(0x7f7cd8d9), + X(0x7f7da9c2), X(0x7f7e79b7), X(0x7f7f48b8), X(0x7f8016c5), + X(0x7f80e3e0), X(0x7f81b009), X(0x7f827b40), X(0x7f834588), + X(0x7f840edf), X(0x7f84d747), X(0x7f859ec1), X(0x7f86654d), + X(0x7f872aec), X(0x7f87ef9e), X(0x7f88b365), X(0x7f897641), + X(0x7f8a3832), X(0x7f8af93a), X(0x7f8bb959), X(0x7f8c7890), + X(0x7f8d36df), X(0x7f8df448), X(0x7f8eb0ca), X(0x7f8f6c67), + X(0x7f90271e), X(0x7f90e0f2), X(0x7f9199e2), X(0x7f9251f0), + X(0x7f93091b), X(0x7f93bf65), X(0x7f9474ce), X(0x7f952958), + X(0x7f95dd01), X(0x7f968fcd), X(0x7f9741ba), X(0x7f97f2ca), + X(0x7f98a2fd), X(0x7f995254), X(0x7f9a00d0), X(0x7f9aae71), + X(0x7f9b5b38), X(0x7f9c0726), X(0x7f9cb23b), X(0x7f9d5c78), + X(0x7f9e05de), X(0x7f9eae6e), X(0x7f9f5627), X(0x7f9ffd0b), + X(0x7fa0a31b), X(0x7fa14856), X(0x7fa1ecbf), X(0x7fa29054), + X(0x7fa33318), X(0x7fa3d50b), X(0x7fa4762c), X(0x7fa5167e), + X(0x7fa5b601), X(0x7fa654b5), X(0x7fa6f29b), X(0x7fa78fb3), + X(0x7fa82bff), X(0x7fa8c77f), X(0x7fa96234), X(0x7fa9fc1e), + X(0x7faa953e), X(0x7fab2d94), X(0x7fabc522), X(0x7fac5be8), + X(0x7facf1e6), X(0x7fad871d), X(0x7fae1b8f), X(0x7faeaf3b), + X(0x7faf4222), X(0x7fafd445), X(0x7fb065a4), X(0x7fb0f641), + X(0x7fb1861b), X(0x7fb21534), X(0x7fb2a38c), X(0x7fb33124), + X(0x7fb3bdfb), X(0x7fb44a14), X(0x7fb4d56f), X(0x7fb5600c), + X(0x7fb5e9ec), X(0x7fb6730f), X(0x7fb6fb76), X(0x7fb78323), + X(0x7fb80a15), X(0x7fb8904d), X(0x7fb915cc), X(0x7fb99a92), + X(0x7fba1ea0), X(0x7fbaa1f7), X(0x7fbb2497), X(0x7fbba681), + X(0x7fbc27b5), X(0x7fbca835), X(0x7fbd2801), X(0x7fbda719), + X(0x7fbe257e), X(0x7fbea331), X(0x7fbf2032), X(0x7fbf9c82), + X(0x7fc01821), X(0x7fc09311), X(0x7fc10d52), X(0x7fc186e4), + X(0x7fc1ffc8), X(0x7fc277ff), X(0x7fc2ef89), X(0x7fc36667), + X(0x7fc3dc9a), X(0x7fc45221), X(0x7fc4c6ff), X(0x7fc53b33), + X(0x7fc5aebe), X(0x7fc621a0), X(0x7fc693db), X(0x7fc7056f), + X(0x7fc7765c), X(0x7fc7e6a3), X(0x7fc85645), X(0x7fc8c542), + X(0x7fc9339b), X(0x7fc9a150), X(0x7fca0e63), X(0x7fca7ad3), + X(0x7fcae6a2), X(0x7fcb51cf), X(0x7fcbbc5c), X(0x7fcc2649), + X(0x7fcc8f97), X(0x7fccf846), X(0x7fcd6058), X(0x7fcdc7cb), + X(0x7fce2ea2), X(0x7fce94dd), X(0x7fcefa7b), X(0x7fcf5f7f), + X(0x7fcfc3e8), X(0x7fd027b7), X(0x7fd08aed), X(0x7fd0ed8b), + X(0x7fd14f90), X(0x7fd1b0fd), X(0x7fd211d4), X(0x7fd27214), + X(0x7fd2d1bf), X(0x7fd330d4), X(0x7fd38f55), X(0x7fd3ed41), + X(0x7fd44a9a), X(0x7fd4a761), X(0x7fd50395), X(0x7fd55f37), + X(0x7fd5ba48), X(0x7fd614c9), X(0x7fd66eba), X(0x7fd6c81b), + X(0x7fd720ed), X(0x7fd77932), X(0x7fd7d0e8), X(0x7fd82812), + X(0x7fd87eae), X(0x7fd8d4bf), X(0x7fd92a45), X(0x7fd97f40), + X(0x7fd9d3b0), X(0x7fda2797), X(0x7fda7af5), X(0x7fdacdca), + X(0x7fdb2018), X(0x7fdb71dd), X(0x7fdbc31c), X(0x7fdc13d5), + X(0x7fdc6408), X(0x7fdcb3b6), X(0x7fdd02df), X(0x7fdd5184), + X(0x7fdd9fa5), X(0x7fdded44), X(0x7fde3a60), X(0x7fde86fb), + X(0x7fded314), X(0x7fdf1eac), X(0x7fdf69c4), X(0x7fdfb45d), + X(0x7fdffe76), X(0x7fe04811), X(0x7fe0912e), X(0x7fe0d9ce), + X(0x7fe121f0), X(0x7fe16996), X(0x7fe1b0c1), X(0x7fe1f770), + X(0x7fe23da4), X(0x7fe2835f), X(0x7fe2c89f), X(0x7fe30d67), + X(0x7fe351b5), X(0x7fe3958c), X(0x7fe3d8ec), X(0x7fe41bd4), + X(0x7fe45e46), X(0x7fe4a042), X(0x7fe4e1c8), X(0x7fe522da), + X(0x7fe56378), X(0x7fe5a3a1), X(0x7fe5e358), X(0x7fe6229b), + X(0x7fe6616d), X(0x7fe69fcc), X(0x7fe6ddbb), X(0x7fe71b39), + X(0x7fe75847), X(0x7fe794e5), X(0x7fe7d114), X(0x7fe80cd5), + X(0x7fe84827), X(0x7fe8830c), X(0x7fe8bd84), X(0x7fe8f78f), + X(0x7fe9312f), X(0x7fe96a62), X(0x7fe9a32b), X(0x7fe9db8a), + X(0x7fea137e), X(0x7fea4b09), X(0x7fea822b), X(0x7feab8e5), + X(0x7feaef37), X(0x7feb2521), X(0x7feb5aa4), X(0x7feb8fc1), + X(0x7febc478), X(0x7febf8ca), X(0x7fec2cb6), X(0x7fec603e), + X(0x7fec9363), X(0x7fecc623), X(0x7fecf881), X(0x7fed2a7c), + X(0x7fed5c16), X(0x7fed8d4e), X(0x7fedbe24), X(0x7fedee9b), + X(0x7fee1eb1), X(0x7fee4e68), X(0x7fee7dc0), X(0x7feeacb9), + X(0x7feedb54), X(0x7fef0991), X(0x7fef3771), X(0x7fef64f5), + X(0x7fef921d), X(0x7fefbee8), X(0x7fefeb59), X(0x7ff0176f), + X(0x7ff0432a), X(0x7ff06e8c), X(0x7ff09995), X(0x7ff0c444), + X(0x7ff0ee9c), X(0x7ff1189b), X(0x7ff14243), X(0x7ff16b94), + X(0x7ff1948e), X(0x7ff1bd32), X(0x7ff1e581), X(0x7ff20d7b), + X(0x7ff2351f), X(0x7ff25c70), X(0x7ff2836d), X(0x7ff2aa17), + X(0x7ff2d06d), X(0x7ff2f672), X(0x7ff31c24), X(0x7ff34185), + X(0x7ff36695), X(0x7ff38b55), X(0x7ff3afc4), X(0x7ff3d3e4), + X(0x7ff3f7b4), X(0x7ff41b35), X(0x7ff43e69), X(0x7ff4614e), + X(0x7ff483e6), X(0x7ff4a631), X(0x7ff4c82f), X(0x7ff4e9e1), + X(0x7ff50b47), X(0x7ff52c62), X(0x7ff54d33), X(0x7ff56db9), + X(0x7ff58df5), X(0x7ff5ade7), X(0x7ff5cd90), X(0x7ff5ecf1), + X(0x7ff60c09), X(0x7ff62ada), X(0x7ff64963), X(0x7ff667a5), + X(0x7ff685a1), X(0x7ff6a357), X(0x7ff6c0c7), X(0x7ff6ddf1), + X(0x7ff6fad7), X(0x7ff71778), X(0x7ff733d6), X(0x7ff74fef), + X(0x7ff76bc6), X(0x7ff78759), X(0x7ff7a2ab), X(0x7ff7bdba), + X(0x7ff7d888), X(0x7ff7f315), X(0x7ff80d61), X(0x7ff8276c), + X(0x7ff84138), X(0x7ff85ac4), X(0x7ff87412), X(0x7ff88d20), + X(0x7ff8a5f0), X(0x7ff8be82), X(0x7ff8d6d7), X(0x7ff8eeef), + X(0x7ff906c9), X(0x7ff91e68), X(0x7ff935cb), X(0x7ff94cf2), + X(0x7ff963dd), X(0x7ff97a8f), X(0x7ff99105), X(0x7ff9a742), + X(0x7ff9bd45), X(0x7ff9d30f), X(0x7ff9e8a0), X(0x7ff9fdf9), + X(0x7ffa131a), X(0x7ffa2803), X(0x7ffa3cb4), X(0x7ffa512f), + X(0x7ffa6573), X(0x7ffa7981), X(0x7ffa8d59), X(0x7ffaa0fc), + X(0x7ffab46a), X(0x7ffac7a3), X(0x7ffadaa8), X(0x7ffaed78), + X(0x7ffb0015), X(0x7ffb127f), X(0x7ffb24b6), X(0x7ffb36bb), + X(0x7ffb488d), X(0x7ffb5a2e), X(0x7ffb6b9d), X(0x7ffb7cdb), + X(0x7ffb8de9), X(0x7ffb9ec6), X(0x7ffbaf73), X(0x7ffbbff1), + X(0x7ffbd03f), X(0x7ffbe05e), X(0x7ffbf04f), X(0x7ffc0012), + X(0x7ffc0fa6), X(0x7ffc1f0d), X(0x7ffc2e47), X(0x7ffc3d54), + X(0x7ffc4c35), X(0x7ffc5ae9), X(0x7ffc6971), X(0x7ffc77ce), + X(0x7ffc8600), X(0x7ffc9407), X(0x7ffca1e4), X(0x7ffcaf96), + X(0x7ffcbd1f), X(0x7ffcca7e), X(0x7ffcd7b4), X(0x7ffce4c1), + X(0x7ffcf1a5), X(0x7ffcfe62), X(0x7ffd0af6), X(0x7ffd1763), + X(0x7ffd23a9), X(0x7ffd2fc8), X(0x7ffd3bc1), X(0x7ffd4793), + X(0x7ffd533f), X(0x7ffd5ec5), X(0x7ffd6a27), X(0x7ffd7563), + X(0x7ffd807a), X(0x7ffd8b6e), X(0x7ffd963d), X(0x7ffda0e8), + X(0x7ffdab70), X(0x7ffdb5d5), X(0x7ffdc017), X(0x7ffdca36), + X(0x7ffdd434), X(0x7ffdde0f), X(0x7ffde7c9), X(0x7ffdf161), + X(0x7ffdfad8), X(0x7ffe042f), X(0x7ffe0d65), X(0x7ffe167b), + X(0x7ffe1f71), X(0x7ffe2848), X(0x7ffe30ff), X(0x7ffe3997), + X(0x7ffe4211), X(0x7ffe4a6c), X(0x7ffe52a9), X(0x7ffe5ac8), + X(0x7ffe62c9), X(0x7ffe6aae), X(0x7ffe7275), X(0x7ffe7a1f), + X(0x7ffe81ad), X(0x7ffe891f), X(0x7ffe9075), X(0x7ffe97b0), + X(0x7ffe9ece), X(0x7ffea5d2), X(0x7ffeacbb), X(0x7ffeb38a), + X(0x7ffeba3e), X(0x7ffec0d8), X(0x7ffec758), X(0x7ffecdbf), + X(0x7ffed40d), X(0x7ffeda41), X(0x7ffee05d), X(0x7ffee660), + X(0x7ffeec4b), X(0x7ffef21f), X(0x7ffef7da), X(0x7ffefd7e), + X(0x7fff030b), X(0x7fff0881), X(0x7fff0de0), X(0x7fff1328), + X(0x7fff185b), X(0x7fff1d77), X(0x7fff227e), X(0x7fff276f), + X(0x7fff2c4b), X(0x7fff3112), X(0x7fff35c4), X(0x7fff3a62), + X(0x7fff3eeb), X(0x7fff4360), X(0x7fff47c2), X(0x7fff4c0f), + X(0x7fff504a), X(0x7fff5471), X(0x7fff5885), X(0x7fff5c87), + X(0x7fff6076), X(0x7fff6452), X(0x7fff681d), X(0x7fff6bd6), + X(0x7fff6f7d), X(0x7fff7313), X(0x7fff7698), X(0x7fff7a0c), + X(0x7fff7d6f), X(0x7fff80c2), X(0x7fff8404), X(0x7fff8736), + X(0x7fff8a58), X(0x7fff8d6b), X(0x7fff906e), X(0x7fff9362), + X(0x7fff9646), X(0x7fff991c), X(0x7fff9be3), X(0x7fff9e9c), + X(0x7fffa146), X(0x7fffa3e2), X(0x7fffa671), X(0x7fffa8f1), + X(0x7fffab65), X(0x7fffadca), X(0x7fffb023), X(0x7fffb26f), + X(0x7fffb4ae), X(0x7fffb6e0), X(0x7fffb906), X(0x7fffbb20), + X(0x7fffbd2e), X(0x7fffbf30), X(0x7fffc126), X(0x7fffc311), + X(0x7fffc4f1), X(0x7fffc6c5), X(0x7fffc88f), X(0x7fffca4d), + X(0x7fffcc01), X(0x7fffcdab), X(0x7fffcf4a), X(0x7fffd0e0), + X(0x7fffd26b), X(0x7fffd3ec), X(0x7fffd564), X(0x7fffd6d2), + X(0x7fffd838), X(0x7fffd993), X(0x7fffdae6), X(0x7fffdc31), + X(0x7fffdd72), X(0x7fffdeab), X(0x7fffdfdb), X(0x7fffe104), + X(0x7fffe224), X(0x7fffe33c), X(0x7fffe44d), X(0x7fffe556), + X(0x7fffe657), X(0x7fffe751), X(0x7fffe844), X(0x7fffe930), + X(0x7fffea15), X(0x7fffeaf3), X(0x7fffebca), X(0x7fffec9b), + X(0x7fffed66), X(0x7fffee2a), X(0x7fffeee8), X(0x7fffefa0), + X(0x7ffff053), X(0x7ffff0ff), X(0x7ffff1a6), X(0x7ffff247), + X(0x7ffff2e4), X(0x7ffff37a), X(0x7ffff40c), X(0x7ffff499), + X(0x7ffff520), X(0x7ffff5a3), X(0x7ffff621), X(0x7ffff69b), + X(0x7ffff710), X(0x7ffff781), X(0x7ffff7ee), X(0x7ffff857), + X(0x7ffff8bb), X(0x7ffff91c), X(0x7ffff979), X(0x7ffff9d2), + X(0x7ffffa27), X(0x7ffffa79), X(0x7ffffac8), X(0x7ffffb13), + X(0x7ffffb5b), X(0x7ffffba0), X(0x7ffffbe2), X(0x7ffffc21), + X(0x7ffffc5d), X(0x7ffffc96), X(0x7ffffccd), X(0x7ffffd01), + X(0x7ffffd32), X(0x7ffffd61), X(0x7ffffd8e), X(0x7ffffdb8), + X(0x7ffffde0), X(0x7ffffe07), X(0x7ffffe2b), X(0x7ffffe4d), + X(0x7ffffe6d), X(0x7ffffe8b), X(0x7ffffea8), X(0x7ffffec3), + X(0x7ffffedc), X(0x7ffffef4), X(0x7fffff0a), X(0x7fffff1f), + X(0x7fffff33), X(0x7fffff45), X(0x7fffff56), X(0x7fffff66), + X(0x7fffff75), X(0x7fffff82), X(0x7fffff8f), X(0x7fffff9a), + X(0x7fffffa5), X(0x7fffffaf), X(0x7fffffb8), X(0x7fffffc0), + X(0x7fffffc8), X(0x7fffffce), X(0x7fffffd5), X(0x7fffffda), + X(0x7fffffdf), X(0x7fffffe4), X(0x7fffffe8), X(0x7fffffeb), + X(0x7fffffef), X(0x7ffffff1), X(0x7ffffff4), X(0x7ffffff6), + X(0x7ffffff8), X(0x7ffffff9), X(0x7ffffffb), X(0x7ffffffc), + X(0x7ffffffd), X(0x7ffffffd), X(0x7ffffffe), X(0x7fffffff), + X(0x7fffffff), X(0x7fffffff), X(0x7fffffff), X(0x7fffffff), + X(0x7fffffff), X(0x7fffffff), X(0x7fffffff), X(0x7fffffff), + X(0x7fffffff), X(0x7fffffff), X(0x7fffffff), X(0x7fffffff), +}; + diff --git a/source/audio.c b/source/audio.c index 2bc9024..6aa75b4 100644 --- a/source/audio.c +++ b/source/audio.c @@ -15,8 +15,8 @@ There are 24 audio channels available, numbered from 0 to 23. #include #include -#include -#include +#include +#include // Audio object type typedef enum { @@ -98,17 +98,13 @@ static int audio_load(lua_State *L) { audio->type = TYPE_OGG; // Load audio file - if (ov_fopen(path, &audio->vf) < 0) { + FILE *file = fopen(path, "rb"); + if (ov_open(file, &audio->vf, NULL, 0) < 0) { lua_pushnil(L); lua_pushstring(L, "input does not appear to be a valid ogg vorbis file or doesn't exist"); return 2; } - // Some Ogg Vorbis decoding parameters - bool bigendianp = false; // bigendianness - u8 word = 2; // word size; 1 or 2 (8bits/sample or 16bits/sample) - bool sgned = true; // signed data - // Decoding Ogg Vorbis bitstream vorbis_info* vi = ov_info(&audio->vf, -1); if (vi == NULL) luaL_error(L, "could not retrieve ogg audio stream informations"); @@ -117,7 +113,7 @@ static int audio_load(lua_State *L) { audio->channels = vi->channels; audio->encoding = NDSP_ENCODING_PCM16; audio->nsamples = ov_pcm_total(&audio->vf, -1); - audio->size = audio->nsamples * word; + audio->size = audio->nsamples * audio->channels * 2; // *2 because output is PCM16 (2 bytes/sample) if (linearSpaceFree() < audio->size) luaL_error(L, "not enough linear memory available"); audio->data = linearAlloc(audio->size); @@ -127,7 +123,7 @@ static int audio_load(lua_State *L) { int eof = 0; int current_section; while (!eof) { - long ret = ov_read(&audio->vf, &audio->data[offset], 4096, bigendianp, word, sgned, ¤t_section); + long ret = ov_read(&audio->vf, &audio->data[offset], 4096, ¤t_section); if (ret == 0) { eof = 1; diff --git a/source/texture.c b/source/texture.c index 9416e68..a369737 100644 --- a/source/texture.c +++ b/source/texture.c @@ -41,7 +41,7 @@ Load a texture from a file. Supported formats: PNG, JPEG, BMP. */ static int texture_load(lua_State *L) { const char *path = luaL_checkstring(L, 1); - u8 place = luaL_optinteger(L, 2, SF2D_PLACE_RAM); //place in vram by default + u8 place = luaL_optinteger(L, 2, SF2D_PLACE_RAM); //place in ram by default u8 type = luaL_optinteger(L, 3, 3); //type 3 is "search at the end of the filename" texture_userdata *texture; From 07d5316d722da82c3d505b7ac48f6821ba4cd932 Mon Sep 17 00:00:00 2001 From: Reuh Date: Wed, 30 Dec 2015 14:51:51 +0100 Subject: [PATCH 049/101] Changed map format to CSV, added ability to load maps from a Lua table --- source/map.c | 146 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 93 insertions(+), 53 deletions(-) diff --git a/source/map.c b/source/map.c index e0b2f78..6509d0c 100644 --- a/source/map.c +++ b/source/map.c @@ -1,5 +1,6 @@ /*** The `map` module. +Tile coordinates start at x=0,y=0. @module ctr.gfx.map @usage local map = require("ctr.gfx.map") */ @@ -42,20 +43,18 @@ u16 getTile(map_userdata *map, int x, int y) { /*** Load a map from a file. @function load -@tparam string path path to the .map +@tparam string/table map path to the .map or a 2D table containing tile data (`{ [Y1]={[X1]=tile1, [X2]=tile2}, [Y2]=..., ... }`) @tparam texture tileset containing the tileset @tparam number tileWidth tile width @tparam number tileHeight tile height @treturn map loaded map object */ static int map_load(lua_State *L) { - const char *mapPath = luaL_checkstring(L, 1); texture_userdata *texture = luaL_checkudata(L, 2, "LTexture"); u8 tileSizeX = luaL_checkinteger(L, 3); u8 tileSizeY = luaL_checkinteger(L, 4); - map_userdata *map; - map = (map_userdata*)lua_newuserdata(L, sizeof(map_userdata)); + map_userdata *map = lua_newuserdata(L, sizeof(map_userdata)); luaL_getmetatable(L, "LMap"); lua_setmetatable(L, -2); @@ -68,47 +67,87 @@ static int map_load(lua_State *L) { map->spaceY = 0; // read the map file - FILE *mapFile = fopen(mapPath, "r"); - if (mapFile == NULL) { - lua_pushnil(L); - lua_pushstring(L, "No such file"); - return 2; - } - fseek(mapFile, 0L, SEEK_END); - int fileSize = ftell(mapFile); - fseek(mapFile, 0L, SEEK_SET); - char *buffer = (char *)malloc(sizeof(char)*fileSize); - fread(buffer, 1, fileSize, mapFile); - fclose(mapFile); - - int width = 0; - for (int i=0; buffer[i]; i++) { - if (buffer[i] == '|') { - width++; - } else if (buffer[i] == '\n') { - break; + if (lua_isstring(L, 1)) { + const char *mapPath = luaL_checkstring(L, 1); + + FILE *mapFile = fopen(mapPath, "r"); + if (mapFile == NULL) { + lua_pushnil(L); + lua_pushfstring(L, "no such file \"%s\"", mapPath); + return 2; } + fseek(mapFile, 0L, SEEK_END); + int fileSize = ftell(mapFile); + fseek(mapFile, 0L, SEEK_SET); + char *buffer = (char *)malloc(sizeof(char)*fileSize); + fread(buffer, 1, fileSize, mapFile); + fclose(mapFile); + + int width = 0; + for (int i=0; buffer[i]; i++) { + if (buffer[i] == ',') { + width++; + } else if (buffer[i] == '\n') { + width++; + break; + } + } + int height = 0; + for (int i=0; buffer[i]; i++) { // this should do + if (buffer[i] == '\n' && (buffer[i+1] != '\n' || !buffer[i+1])) height++; + } + + map->width = width; + map->height = height; + + map->data = malloc(sizeof(u16)*width*height); + int i = 0; + char *token = strtok(buffer, ",\n"); + while (token != NULL) { + map->data[i] = (u16)atoi(token); + i++; + token = strtok(NULL, ",\n"); + } + free(buffer); + + return 1; + + } else if (lua_istable(L, 1)) { + int height = luaL_len(L, 1); + if (height < 1) luaL_error(L, "map height must be greater or equal to 1"); + + lua_geti(L, 1, 1); + int width = luaL_len(L, -1); + if (width < 1) luaL_error(L, "map width must be greater or equal to 1"); + lua_pop(L, 1); + + map->width = width; + map->height = height; + + map->data = malloc(sizeof(u16)*width*height); + for (int y=1; y<=height; y++) { + if (lua_geti(L, 1, y) != LUA_TTABLE) luaL_error(L, "map table must be an array of tables"); + if (luaL_len(L, -1) < width) luaL_error(L, "table line y=%d is shorter than the map width", y); + + for (int x=1; x<=width; x++) { + lua_geti(L, -1, x); + + bool isnum; + map->data[(x-1)+((y-1)*map->width)] = (u16)lua_tointegerx(L, -1, (int *)&isnum); + if (!isnum) luaL_error(L, "tiles must be integers"); + + lua_pop(L, 1); + } + + lua_pop(L, 1); + } + + return 1; + + } else { + luaL_error(L, "map (first argument) must be a string or a table"); + return 0; } - int height = 0; - for (int i=0; buffer[i]; i++) { // this should do - if (buffer[i] == '\n' && (buffer[i+1] != '\n' || !buffer[i+1])) height++; - } - - map->width = width; - map->height = height; - - map->data = malloc(sizeof(u16)*width*height); - int i = 0; - char *token; - token = strtok(buffer, "|"); - while (token != NULL) { - map->data[i] = (u16)atoi(token); - i++; - token = strtok(NULL, "|"); - } - free(buffer); - - return 1; } /*** @@ -130,7 +169,6 @@ static int map_draw(lua_State *L) { int texX = 0; int texY = 0; - if (map->texture->blendColor == 0xffffffff) { for (int xp=0; xpwidth; xp++) { for (int yp=0; ypheight; yp++) { @@ -159,8 +197,9 @@ Unload a map. */ static int map_unload(lua_State *L) { map_userdata *map = luaL_checkudata(L, 1, "LMap"); + free(map->data); - free(map); + return 0; } @@ -183,8 +222,8 @@ static int map_getSize(lua_State *L) { /*** Return the value of a tile. @function :getTile -@tparam number x X position of the tile -@tparam number y Y position of the tile +@tparam number x X position of the tile (in tiles) +@tparam number y Y position of the tile (in tiles) @treturn number value of the tile @within Methods */ @@ -194,15 +233,16 @@ static int map_getTile(lua_State *L) { int y = luaL_checkinteger(L, 3); lua_pushinteger(L, getTile(map, x, y)); + return 1; } /*** Set the value of a tile. @function :setTile -@tparam number x X position of the tile -@tparam number y Y position of the tile -@tparam number value New value for the tile +@tparam number x X position of the tile (in tiles) +@tparam number y Y position of the tile (in tiles) +@tparam number value new value for the tile @within Methods */ static int map_setTile(lua_State *L) { @@ -219,8 +259,8 @@ static int map_setTile(lua_State *L) { /*** Set the space between draw tiles (in pixels). @function :setSpace -@tparam number x X space -@tparam number y Y space +@tparam number x X space (in pixels) +@tparam number y Y space (in pixels) */ static int map_setSpace(lua_State *L) { map_userdata *map = luaL_checkudata(L, 1, "LMap"); @@ -247,7 +287,7 @@ static const struct luaL_Reg map_methods[] = { // module static const struct luaL_Reg map_functions[] = { - {"load", map_load}, + { "load", map_load }, {NULL, NULL} }; From 73b5c55133f3d676347134243992d8cfffd2e6c2 Mon Sep 17 00:00:00 2001 From: Reuh Date: Wed, 30 Dec 2015 18:19:13 +0100 Subject: [PATCH 050/101] gfx.startFrame -> gfx.start and gfx.endFrame -> gfx.stop --- sdcard/3ds/ctruLua/editor/main.lua | 16 ++++++++-------- sdcard/3ds/ctruLua/examples/example.lua | 12 ++++++------ sdcard/3ds/ctruLua/libs/openfile.lua | 8 ++++---- sdcard/3ds/ctruLua/main.lua | 4 ++-- sdcard/3ds/ctruLua/tests/audio/main.lua | 4 ++-- sdcard/3ds/ctruLua/tests/httpc.lua | 4 ++-- source/gfx.c | 12 ++++++------ 7 files changed, 30 insertions(+), 30 deletions(-) diff --git a/sdcard/3ds/ctruLua/editor/main.lua b/sdcard/3ds/ctruLua/editor/main.lua index 26709b4..6f2ed5f 100644 --- a/sdcard/3ds/ctruLua/editor/main.lua +++ b/sdcard/3ds/ctruLua/editor/main.lua @@ -86,20 +86,20 @@ while ctr.run() do if not file then local t = os.time() repeat - gfx.startFrame(gfx.GFX_TOP) + gfx.start(gfx.GFX_TOP) gfx.text(3, 3, "Can't open file in write mode") - gfx.endFrame() + gfx.stop() gfx.render() until t + 5 < os.time() else for i = 1, #lines, 1 do file:write(lines[i]..lineEnding) - gfx.startFrame(gfx.GFX_TOP) + gfx.start(gfx.GFX_TOP) gfx.rectangle(0, 0, math.ceil(i/#lines*gfx.TOP_WIDTH), gfx.TOP_HEIGHT, 0, 0xFFFFFFFF) gfx.color.setDefault(color.background) gfx.text(gfx.TOP_WIDTH/2, gfx.TOP_HEIGHT/2, math.ceil(i/#lines*100).."%") gfx.color.setDefault(color.default) - gfx.endFrame() + gfx.stop() gfx.render() end file:flush() @@ -138,7 +138,7 @@ while ctr.run() do end -- Draw - gfx.startFrame(gfx.GFX_TOP) + gfx.start(gfx.GFX_TOP) -- Lines local sI = math.floor(scrollY / lineHeight) @@ -167,15 +167,15 @@ while ctr.run() do gfx.rectangle(-scrollX+ font:width(displayedText(curline:sub(1, (utf8.offset(curline, cursorX) or 0)-1))), -scrollY+ (cursorY-1)*lineHeight, 1, lineHeight, 0, color.cursor) - gfx.endFrame() + gfx.stop() - gfx.startFrame(gfx.GFX_BOTTOM) + gfx.start(gfx.GFX_BOTTOM) gfx.text(3, 3, "FPS: "..math.ceil(gfx.getFPS())) keyboard.draw(5, 115) - gfx.endFrame() + gfx.stop() gfx.render() end diff --git a/sdcard/3ds/ctruLua/examples/example.lua b/sdcard/3ds/ctruLua/examples/example.lua index 9f5fc28..b9f0089 100644 --- a/sdcard/3ds/ctruLua/examples/example.lua +++ b/sdcard/3ds/ctruLua/examples/example.lua @@ -47,19 +47,19 @@ while ctr.run() do dMul = hid.pos3d() - gfx.startFrame(gfx.GFX_TOP, gfx.GFX_LEFT) + gfx.start(gfx.GFX_TOP, gfx.GFX_LEFT) drawStuffIn3D(-1) - gfx.endFrame() + gfx.stop() - gfx.startFrame(gfx.GFX_TOP, gfx.GFX_RIGHT) + gfx.start(gfx.GFX_TOP, gfx.GFX_RIGHT) drawStuffIn3D(1) - gfx.endFrame() + gfx.stop() - gfx.startFrame(gfx.GFX_BOTTOM) + gfx.start(gfx.GFX_BOTTOM) gfx.color.setDefault(gfx.color.RGBA8(0, 0, 0)) gfx.text(5, 5, "FPS: "..math.ceil(gfx.getFPS())) @@ -72,7 +72,7 @@ while ctr.run() do gfx.rectangle(40, 90, 60, 60, 0, 0xDDDDDDFF) gfx.circle(70 + math.ceil(cx/156 * 30), 120 - math.ceil(cy/156 * 30), 10, 0xFF000000) - gfx.endFrame() + gfx.stop() angle = angle + 0.05 if angle > 2*math.pi then angle = angle - 2*math.pi end diff --git a/sdcard/3ds/ctruLua/libs/openfile.lua b/sdcard/3ds/ctruLua/libs/openfile.lua index c08b842..8954558 100644 --- a/sdcard/3ds/ctruLua/libs/openfile.lua +++ b/sdcard/3ds/ctruLua/libs/openfile.lua @@ -98,7 +98,7 @@ return function(title, curdir, exts, type) end -- Draw - gfx.startFrame(gfx.GFX_TOP) + gfx.start(gfx.GFX_TOP) gfx.rectangle(0, 10+(sel-scroll)*15, gfx.TOP_WIDTH, 15, 0, gfx.color.RGBA8(0, 0, 200)) @@ -112,9 +112,9 @@ return function(title, curdir, exts, type) gfx.rectangle(0, 0, gfx.TOP_WIDTH, 25, 0, gfx.color.RGBA8(200, 200, 200)) gfx.text(3, 3, curdir, 13, gfx.color.RGBA8(0, 0, 0)) - gfx.endFrame() + gfx.stop() - gfx.startFrame(gfx.GFX_BOTTOM) + gfx.start(gfx.GFX_BOTTOM) gfx.text(5, 5, title) gfx.text(5, 20, "Accepted file extensions: "..(exts or "all")) @@ -124,7 +124,7 @@ return function(title, curdir, exts, type) keyboard.draw(5, 115) end - gfx.endFrame() + gfx.stop() gfx.render() end diff --git a/sdcard/3ds/ctruLua/main.lua b/sdcard/3ds/ctruLua/main.lua index 05160d6..4b9540d 100644 --- a/sdcard/3ds/ctruLua/main.lua +++ b/sdcard/3ds/ctruLua/main.lua @@ -23,9 +23,9 @@ repeat while true do hid.read() if hid.keys().down.start then break end - gfx.startFrame(gfx.GFX_TOP) + gfx.start(gfx.TOP) gfx.wrappedText(0, 0, err, gfx.TOP_WIDTH) - gfx.endFrame() + gfx.stop() gfx.render() end end diff --git a/sdcard/3ds/ctruLua/tests/audio/main.lua b/sdcard/3ds/ctruLua/tests/audio/main.lua index 169c09c..af7c43f 100644 --- a/sdcard/3ds/ctruLua/tests/audio/main.lua +++ b/sdcard/3ds/ctruLua/tests/audio/main.lua @@ -40,11 +40,11 @@ while true do audio.mix(nil, leftBalance, 1-leftBalance) end - gfx.startFrame(gfx.GFX_TOP) + gfx.start(gfx.GFX_TOP) gfx.text(5, 5, "Audio test! "..tostring(test:time()).."/"..tostring(test:duration()).."s") gfx.text(5, 25, "Last audio played on channel "..tostring(channel)) gfx.text(5, 65, "Speed: "..(speed*100).."% - Left balance: "..(leftBalance*100).."%") - gfx.endFrame() + gfx.stop() gfx.render() end diff --git a/sdcard/3ds/ctruLua/tests/httpc.lua b/sdcard/3ds/ctruLua/tests/httpc.lua index f7d707b..791bed8 100644 --- a/sdcard/3ds/ctruLua/tests/httpc.lua +++ b/sdcard/3ds/ctruLua/tests/httpc.lua @@ -25,9 +25,9 @@ while ctr.run() do data = (data.."!") end - gfx.startFrame(gfx.GFX_TOP) + gfx.start(gfx.GFX_TOP) gfx.text(0, 0, data) - gfx.endFrame() + gfx.stop() gfx.render() end diff --git a/source/gfx.c b/source/gfx.c index 49c8195..c60686e 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -52,11 +52,11 @@ void load_map_lib(lua_State *L); /*** Start drawing to a screen. Must be called before any draw operation. -@function startFrame +@function start @tparam number screen the screen to draw to (`GFX_TOP` or `GFX_BOTTOM`) @tparam[opt=GFX_LEFT] number eye the eye to draw to (`GFX_LEFT` or `GFX_RIGHT`) */ -static int gfx_startFrame(lua_State *L) { +static int gfx_start(lua_State *L) { u8 screen = luaL_checkinteger(L, 1); u8 eye = luaL_optinteger(L, 2, GFX_LEFT); @@ -68,9 +68,9 @@ static int gfx_startFrame(lua_State *L) { /*** End drawing to a screen. Must be called after your draw operations. -@function endFrame +@function stop */ -static int gfx_endFrame(lua_State *L) { +static int gfx_stop(lua_State *L) { sf2d_end_frame(); return 0; @@ -358,8 +358,8 @@ static int gfx_calcBoundingBox(lua_State *L) { // Functions static const struct luaL_Reg gfx_lib[] = { - { "startFrame", gfx_startFrame }, - { "endFrame", gfx_endFrame }, + { "start", gfx_start }, + { "stop", gfx_stop }, { "render", gfx_render }, { "getFPS", gfx_getFPS }, { "set3D", gfx_set3D }, From b87b2676e61e01ce005945cc21b2efb922e79d7a Mon Sep 17 00:00:00 2001 From: Reuh Date: Wed, 30 Dec 2015 19:15:46 +0100 Subject: [PATCH 051/101] gfx.GFX_TOP, gfx.GFX_BOTTOM, gfx.GFX_LEFT, gfx.GFX_RIGHT -> gfx.TOP, gfx.BOTTOM, gfx.LEFT, gfx.RIGHT Also commented and moved tests/audio to examples/audio because the code is quite clean, and we lack examples. --- sdcard/3ds/ctruLua/editor/main.lua | 8 +++---- .../main.lua => examples/audio/audio.lua} | 21 +++++++++--------- .../{tests => examples}/audio/test.wav | Bin sdcard/3ds/ctruLua/examples/example.lua | 6 ++--- sdcard/3ds/ctruLua/libs/openfile.lua | 4 ++-- sdcard/3ds/ctruLua/tests/httpc.lua | 2 +- source/gfx.c | 20 ++++++++--------- 7 files changed, 31 insertions(+), 30 deletions(-) rename sdcard/3ds/ctruLua/{tests/audio/main.lua => examples/audio/audio.lua} (51%) rename sdcard/3ds/ctruLua/{tests => examples}/audio/test.wav (100%) diff --git a/sdcard/3ds/ctruLua/editor/main.lua b/sdcard/3ds/ctruLua/editor/main.lua index 6f2ed5f..64c9145 100644 --- a/sdcard/3ds/ctruLua/editor/main.lua +++ b/sdcard/3ds/ctruLua/editor/main.lua @@ -86,7 +86,7 @@ while ctr.run() do if not file then local t = os.time() repeat - gfx.start(gfx.GFX_TOP) + gfx.start(gfx.TOP) gfx.text(3, 3, "Can't open file in write mode") gfx.stop() gfx.render() @@ -94,7 +94,7 @@ while ctr.run() do else for i = 1, #lines, 1 do file:write(lines[i]..lineEnding) - gfx.start(gfx.GFX_TOP) + gfx.start(gfx.TOP) gfx.rectangle(0, 0, math.ceil(i/#lines*gfx.TOP_WIDTH), gfx.TOP_HEIGHT, 0, 0xFFFFFFFF) gfx.color.setDefault(color.background) gfx.text(gfx.TOP_WIDTH/2, gfx.TOP_HEIGHT/2, math.ceil(i/#lines*100).."%") @@ -138,7 +138,7 @@ while ctr.run() do end -- Draw - gfx.start(gfx.GFX_TOP) + gfx.start(gfx.TOP) -- Lines local sI = math.floor(scrollY / lineHeight) @@ -169,7 +169,7 @@ while ctr.run() do gfx.stop() - gfx.start(gfx.GFX_BOTTOM) + gfx.start(gfx.BOTTOM) gfx.text(3, 3, "FPS: "..math.ceil(gfx.getFPS())) diff --git a/sdcard/3ds/ctruLua/tests/audio/main.lua b/sdcard/3ds/ctruLua/examples/audio/audio.lua similarity index 51% rename from sdcard/3ds/ctruLua/tests/audio/main.lua rename to sdcard/3ds/ctruLua/examples/audio/audio.lua index af7c43f..d794708 100644 --- a/sdcard/3ds/ctruLua/tests/audio/main.lua +++ b/sdcard/3ds/ctruLua/examples/audio/audio.lua @@ -3,7 +3,7 @@ local hid = require("ctr.hid") local gfx = require("ctr.gfx") local audio = require("ctr.audio") -local test = assert(audio.load("test.wav")) +local test = assert(audio.load("test.wav")) -- Load audio file local channel = -1 local speed = 1 @@ -15,32 +15,33 @@ while true do if keys.down.start then break end if keys.down.a then - channel = test:play() + channel = test:play() -- Play audio (an audio can be played multiple times at the same time) end if keys.down.up then speed = speed + 0.01 - test:speed(speed) - audio.speed(nil, speed) + test:speed(speed) -- Set the audio object default speed, will affect the next time it is played + audio.speed(nil, speed) -- Set the speed of all the currently playing channels end if keys.down.down then speed = speed - 0.01 - test:speed(speed) + test:speed(speed) -- See above audio.speed(nil, speed) end if keys.down.left then leftBalance = leftBalance + 0.1 - test:mix(1-leftBalance, leftBalance) - audio.mix(nil, leftBalance, 1-leftBalance) + test:mix(1-leftBalance, leftBalance) -- Set the audio mix: left speaker will be at leftBalance% and right speaker 1-leftBalance%. + -- Like with test:speed(), it won't affect the currently playing audios but the nexts test:play() will have theses mix paramters. + audio.mix(nil, leftBalance, 1-leftBalance) -- Set the mix of all currently playing channels end if keys.down.right then leftBalance = leftBalance - 0.1 - test:mix(1-leftBalance, leftBalance) + test:mix(1-leftBalance, leftBalance) -- See above audio.mix(nil, leftBalance, 1-leftBalance) end - gfx.start(gfx.GFX_TOP) + gfx.start(gfx.TOP) gfx.text(5, 5, "Audio test! "..tostring(test:time()).."/"..tostring(test:duration()).."s") gfx.text(5, 25, "Last audio played on channel "..tostring(channel)) gfx.text(5, 65, "Speed: "..(speed*100).."% - Left balance: "..(leftBalance*100).."%") @@ -49,4 +50,4 @@ while true do gfx.render() end -test:unload() \ No newline at end of file +test:unload() -- Unload audio file \ No newline at end of file diff --git a/sdcard/3ds/ctruLua/tests/audio/test.wav b/sdcard/3ds/ctruLua/examples/audio/test.wav similarity index 100% rename from sdcard/3ds/ctruLua/tests/audio/test.wav rename to sdcard/3ds/ctruLua/examples/audio/test.wav diff --git a/sdcard/3ds/ctruLua/examples/example.lua b/sdcard/3ds/ctruLua/examples/example.lua index b9f0089..b85c187 100644 --- a/sdcard/3ds/ctruLua/examples/example.lua +++ b/sdcard/3ds/ctruLua/examples/example.lua @@ -47,19 +47,19 @@ while ctr.run() do dMul = hid.pos3d() - gfx.start(gfx.GFX_TOP, gfx.GFX_LEFT) + gfx.start(gfx.TOP, gfx.LEFT) drawStuffIn3D(-1) gfx.stop() - gfx.start(gfx.GFX_TOP, gfx.GFX_RIGHT) + gfx.start(gfx.TOP, gfx.RIGHT) drawStuffIn3D(1) gfx.stop() - gfx.start(gfx.GFX_BOTTOM) + gfx.start(gfx.BOTTOM) gfx.color.setDefault(gfx.color.RGBA8(0, 0, 0)) gfx.text(5, 5, "FPS: "..math.ceil(gfx.getFPS())) diff --git a/sdcard/3ds/ctruLua/libs/openfile.lua b/sdcard/3ds/ctruLua/libs/openfile.lua index 8954558..503af39 100644 --- a/sdcard/3ds/ctruLua/libs/openfile.lua +++ b/sdcard/3ds/ctruLua/libs/openfile.lua @@ -98,7 +98,7 @@ return function(title, curdir, exts, type) end -- Draw - gfx.start(gfx.GFX_TOP) + gfx.start(gfx.TOP) gfx.rectangle(0, 10+(sel-scroll)*15, gfx.TOP_WIDTH, 15, 0, gfx.color.RGBA8(0, 0, 200)) @@ -114,7 +114,7 @@ return function(title, curdir, exts, type) gfx.stop() - gfx.start(gfx.GFX_BOTTOM) + gfx.start(gfx.BOTTOM) gfx.text(5, 5, title) gfx.text(5, 20, "Accepted file extensions: "..(exts or "all")) diff --git a/sdcard/3ds/ctruLua/tests/httpc.lua b/sdcard/3ds/ctruLua/tests/httpc.lua index 791bed8..bb1b0a7 100644 --- a/sdcard/3ds/ctruLua/tests/httpc.lua +++ b/sdcard/3ds/ctruLua/tests/httpc.lua @@ -25,7 +25,7 @@ while ctr.run() do data = (data.."!") end - gfx.start(gfx.GFX_TOP) + gfx.start(gfx.TOP) gfx.text(0, 0, data) gfx.stop() diff --git a/source/gfx.c b/source/gfx.c index c60686e..9bae7ca 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -53,8 +53,8 @@ void load_map_lib(lua_State *L); Start drawing to a screen. Must be called before any draw operation. @function start -@tparam number screen the screen to draw to (`GFX_TOP` or `GFX_BOTTOM`) -@tparam[opt=GFX_LEFT] number eye the eye to draw to (`GFX_LEFT` or `GFX_RIGHT`) +@tparam number screen the screen to draw to (`gfx.TOP` or `gfx.BOTTOM`) +@tparam[opt=gfx.LEFT] number eye the eye to draw to (`gfx.LEFT` or `gfx.RIGHT`) */ static int gfx_start(lua_State *L) { u8 screen = luaL_checkinteger(L, 1); @@ -382,27 +382,27 @@ struct { char *name; int value; } gfx_constants[] = { /*** Constant used to select the top screen. It is equal to `0`. - @field GFX_TOP + @field TOP */ - { "GFX_TOP", GFX_TOP }, + { "TOP", GFX_TOP }, /*** Constant used to select the bottom screen. It is equal to `1`. - @field GFX_BOTTOM + @field BOTTOM */ - { "GFX_BOTTOM", GFX_BOTTOM }, + { "BOTTOM", GFX_BOTTOM }, /*** Constant used to select the left eye. It is equal to `0`. - @field GFX_LEFT + @field LEFT */ - { "GFX_LEFT", GFX_LEFT }, + { "LEFT", GFX_LEFT }, /*** Constant used to select the right eye. It is equal to `1`. - @field GFX_RIGHT + @field RIGHT */ - { "GFX_RIGHT", GFX_RIGHT }, + { "RIGHT", GFX_RIGHT }, /*** The top screen height, in pixels. It is equal to `240`. From e10a101a4a9c9b1dfd98b0dc12edc56ada5f9087 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Thu, 31 Dec 2015 15:41:09 +0100 Subject: [PATCH 052/101] Added some examples, fixed a typo --- sdcard/3ds/ctruLua/examples/cfgu/cfgu.lua | 56 ++++++++++++++++++++ sdcard/3ds/ctruLua/examples/httpc/httpc.lua | 43 +++++++++++++++ sdcard/3ds/ctruLua/examples/map/map.csv | 6 +++ sdcard/3ds/ctruLua/examples/map/map.lua | 49 +++++++++++++++++ sdcard/3ds/ctruLua/examples/map/tileset.png | Bin 0 -> 185 bytes sdcard/3ds/ctruLua/examples/ptm/ptm.lua | 21 ++++++++ sdcard/3ds/ctruLua/examples/qtm/qtm.lua | 48 +++++++++++++++++ source/cfgu.c | 4 +- 8 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 sdcard/3ds/ctruLua/examples/cfgu/cfgu.lua create mode 100644 sdcard/3ds/ctruLua/examples/httpc/httpc.lua create mode 100644 sdcard/3ds/ctruLua/examples/map/map.csv create mode 100644 sdcard/3ds/ctruLua/examples/map/map.lua create mode 100644 sdcard/3ds/ctruLua/examples/map/tileset.png create mode 100644 sdcard/3ds/ctruLua/examples/ptm/ptm.lua create mode 100644 sdcard/3ds/ctruLua/examples/qtm/qtm.lua diff --git a/sdcard/3ds/ctruLua/examples/cfgu/cfgu.lua b/sdcard/3ds/ctruLua/examples/cfgu/cfgu.lua new file mode 100644 index 0000000..90db095 --- /dev/null +++ b/sdcard/3ds/ctruLua/examples/cfgu/cfgu.lua @@ -0,0 +1,56 @@ +local ctr = require("ctr") +local gfx = require("ctr.gfx") +local hid = require("ctr.hid") +local cfgu = require("ctr.cfgu") + +local regions = { + [cfgu.REGION_JPN] = "Japan", + [cfgu.REGION_USA] = "America", + [cfgu.REGION_EUR] = "Europe", + [cfgu.REGION_AUS] = "Australia", + [cfgu.REGION_CHN] = "China", + [cfgu.REGION_KOR] = "Korea", + [cfgu.REGION_TWN] = "Taiwan" +} + +local languages = { + [cfgu.LANGUAGE_JP] = "Japanese", + [cfgu.LANGUAGE_EN] = "English", + [cfgu.LANGUAGE_FR] = "French", + [cfgu.LANGUAGE_DE] = "Deutch", + [cfgu.LANGUAGE_IT] = "Italian", + [cfgu.LANGUAGE_ES] = "Spanish", + [cfgu.LANGUAGE_ZH] = "Chinese", + [cfgu.LANGUAGE_KO] = "Korean", + [cfgu.LANGUAGE_NL] = "Dutch", + [cfgu.LANGUAGE_PT] = "Portuguese", + [cfgu.LANGUAGE_RU] = "Russian", + [cfgu.LANGUAGE_TW] = "Taiwanese" +} + +local models = { + [cfgu.MODEL_3DS] = "3DS", + [cfgu.MODEL_3DSXL] = "3DS XL", + [cfgu.MODEL_N3DS] = "New 3DS", + [cfgu.MODEL_2DS] = "2DS", + [cfgu.MODEL_N3DSXL] = "New 3DS XL" +} + +while ctr.run() do + hid.read() + keys = hid.keys() + if keys.down.start then break end + + gfx.start(gfx.BOTTOM) + gfx.text(2, 2, "CFGU example") + gfx.text(2, 20, "Region: "..regions[cfgu.getRegion()]) + gfx.text(2, 30, "Model: "..models[cfgu.getModel()]) + gfx.text(2, 40, "Language: "..models[cfgu.getLanguage()]) + gfx.text(2, 50, "Username: "..cfgu.getUsername()) + local m,d = cfgu.getBirthday() + gfx.text(2, 60, "Birthday: "..d.."/"..m) + gfx.text(2, 70, "0 Hash: "..cfgu.genHash(0)) + gfx.stop() + + gfx.render() +end diff --git a/sdcard/3ds/ctruLua/examples/httpc/httpc.lua b/sdcard/3ds/ctruLua/examples/httpc/httpc.lua new file mode 100644 index 0000000..7ff1ef5 --- /dev/null +++ b/sdcard/3ds/ctruLua/examples/httpc/httpc.lua @@ -0,0 +1,43 @@ +local ctr = require("ctr") +local gfx = require("ctr.gfx") +local hid = require("ctr.hid") +local httpc = require("ctr.httpc") + +local err = 0 +local addr = "https://wtfismyip.com/text" +local dls = 0 + +local context = assert(httpc.context()) + +assert(context:open(addr)) +assert(context:beginRequest()) + +local data = assert(context:downloadData()) + +while ctr.run() do + hid.read() + keys = hid.keys() + if keys.held.start then break end + if keys.down.b then + assert(context:open(addr)) + assert(context:beginRequest()) + data = assert(context:downloadData()) + dls = dls + 1 + end + + gfx.startFrame(gfx.TOP) + gfx.text(0, 0, data) + gfx.text(0, 20, "Downloaded "..dls.." times.") + gfx.endFrame() + + gfx.startFrame(gfx.BOTTOM) + gfx.text(2, 2, "HTTP Contexts example") + gfx.text(2, 20, "The data is downloaded from '"..addr.."'.") + gfx.text(2, 30, "Press [B] to redownload.") + gfx.endFrame() + + gfx.render() +end + + +context:close() diff --git a/sdcard/3ds/ctruLua/examples/map/map.csv b/sdcard/3ds/ctruLua/examples/map/map.csv new file mode 100644 index 0000000..6035e4c --- /dev/null +++ b/sdcard/3ds/ctruLua/examples/map/map.csv @@ -0,0 +1,6 @@ +0,0,0,0,0,0 +0,1,1,1,1,0 +0,1,3,2,1,0 +0,1,2,3,1,0 +0,1,1,1,1,0 +0,0,0,0,0,0 diff --git a/sdcard/3ds/ctruLua/examples/map/map.lua b/sdcard/3ds/ctruLua/examples/map/map.lua new file mode 100644 index 0000000..835c6d6 --- /dev/null +++ b/sdcard/3ds/ctruLua/examples/map/map.lua @@ -0,0 +1,49 @@ +local ctr = require("ctr") +local gfx = require("ctr.gfx") +local map = require("ctr.gfx.map") +local texture = require("ctr.gfx.texture") +local hid = require("ctr.hid") + +local tileset = assert(texture.load("tileset.png")) +local map = assert(map.load("map.csv", tileset, 16, 16)) + +local x,y = 0,0 +local s = 0 + +while ctr.run() do + hid.read() + local keys = hid.keys() + if keys.down.start then break end + if keys.held.up then + y = y - 1 + elseif keys.held.down then + y = y + 1 + end + if keys.held.left then + x = x - 1 + elseif keys.held.right then + x = x + 1 + end + if keys.held.r then + s = s + 1 + map:setSpace(s,s) + elseif keys.held.l then + s = s - 1 + map:setSpace(s,s) + end + + gfx.start(gfx.TOP) + map:draw(x,y) + gfx.stop() + + gfx.start(gfx.BOTTOM) + gfx.text(2, 2, "Map example") + gfx.text(2, 20, "Press L (-) and R (+) to change the space between the tiles") + gfx.text(2, 30, "Move the map with the D-pad or the C-pad") + gfx.stop() + + gfx.render() +end + +map:unload() +tileset:unload() diff --git a/sdcard/3ds/ctruLua/examples/map/tileset.png b/sdcard/3ds/ctruLua/examples/map/tileset.png new file mode 100644 index 0000000000000000000000000000000000000000..65c40e5594c57ac12a7000a69ec56764dbed8989 GIT binary patch literal 185 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={WI14-?iy0WWg+Z8+Vb&Z8pdfpR zr>`sfeI9vkChOd|Q&)jPk|nMYCC>S|xv6<249-QVi6yBi3gww484B*6z5(HleBwYw z#-1*YAs)xyUfsycz`()eSn_{vA)8R{nR@?qOHy1dWw!J&1648HSTTq12Z+_se}KW` V1*^478p~IZgr}>Y%Q~loCIFoPF{uCm literal 0 HcmV?d00001 diff --git a/sdcard/3ds/ctruLua/examples/ptm/ptm.lua b/sdcard/3ds/ctruLua/examples/ptm/ptm.lua new file mode 100644 index 0000000..4420c3c --- /dev/null +++ b/sdcard/3ds/ctruLua/examples/ptm/ptm.lua @@ -0,0 +1,21 @@ +local ctr = require("ctr") +local gfx = require("ctr.gfx") +local hid = require("ctr.hid") +local ptm = require("ctr.ptm") + +while ctr.run() do + hid.read() + local keys = hid.keys() + if keys.down.start then break end + + gfx.start(gfx.TOP) + gfx.text(2, 2, "PTM example") + local level = ptm.getBatteryLevel() + gfx.text(2, 20, "Battery level: ["..string.rep("|", level)..string.rep(" ", 5-level).."]") + gfx.text(2, 30, "Charging: "..((ptm.getBatteryChargeState() and "Yes") or "No")) + gfx.text(2, 40, "You walked: "..ptm.getTotalStepCount().." steps") + gfx.text(2, 50, "Counting: "..((ptm.getPedometerState() and "Yes") or "No")) + gfx.stop() + + gfx.render() +end diff --git a/sdcard/3ds/ctruLua/examples/qtm/qtm.lua b/sdcard/3ds/ctruLua/examples/qtm/qtm.lua new file mode 100644 index 0000000..25533c2 --- /dev/null +++ b/sdcard/3ds/ctruLua/examples/qtm/qtm.lua @@ -0,0 +1,48 @@ +local ctr = require("ctr") +local hid = require("ctr.hid") +local gfx = require("ctr.gfx") +local qtm = require("ctr.qtm") + +qtm.init() + +if not qtm.checkInitialized() then + while ctr.run() do + hid.read() + if hid.keys().down.start then + break + end + gfx.start(gfx.TOP) + gfx.text(2, 2, "Couldn't initialize the QTM module.") + gfx.text(2, 12, "You need a New 3DS in order to use this.") + gfx.stop() + gfx.render() + end + return +end + +while ctr.run() do + hid.read() + local keys = hid.keys() + if keys.down.start then break end + + local infos = qtm.getHeadTrackingInfo() + + gfx.start(gfx.TOP) + if infos:checkHeadFullyDetected() then + for i=1, 4 do + gfx.point(infos:convertCoordToScreen(1, 400, 240)) + end + end + gfx.stop() + + gfx.start(gfx.BOTTOM) + gfx.text(0, 0, "QTM example") + for i=1, 4 do + local x,y = infos[i] + gfx.text(0, 10*i, i..": "..x..";"..y) + end + gfx.stop() + + gfx.render() +end + diff --git a/source/cfgu.c b/source/cfgu.c index f74abae..1700e47 100644 --- a/source/cfgu.c +++ b/source/cfgu.c @@ -215,7 +215,7 @@ struct { char *name; int value; } cfgu_constants[] = { /*** Constant returned by `getLanguage` if the language is Italian. It is equal to `4`. - @field LANGUAGE_JP + @field LANGUAGE_IT */ {"LANGUAGE_IT", CFG_LANGUAGE_IT}, /*** @@ -276,7 +276,7 @@ struct { char *name; int value; } cfgu_constants[] = { /*** Constant returned by `getModel` if the console is a New 3DS. It is equal to `2`. - @field MODEL_3DSXL + @field MODEL_N3DS */ {"MODEL_N3DS", 2}, /*** From e7c9a60d6193ec1874f1ddb43bc382d95a67aedf Mon Sep 17 00:00:00 2001 From: Reuh Date: Thu, 31 Dec 2015 15:54:10 +0100 Subject: [PATCH 053/101] Alphabetical sorting in openfile.lua --- sdcard/3ds/ctruLua/libs/openfile.lua | 17 +++++++++++++++-- source/fs.c | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/sdcard/3ds/ctruLua/libs/openfile.lua b/sdcard/3ds/ctruLua/libs/openfile.lua index 503af39..0137005 100644 --- a/sdcard/3ds/ctruLua/libs/openfile.lua +++ b/sdcard/3ds/ctruLua/libs/openfile.lua @@ -1,3 +1,15 @@ +-- Sort ctr.fs.list returns (directories first and alphabetical sorting) +local function sort(files) + table.sort(files, function(i, j) + if i.isDirectory and not j.isDirectory then + return true + elseif i.isDirectory == j.isDirectory then + return string.lower(i.name) < string.lower(j.name) + end + end) + return files +end + --- Open a file explorer to select a file. -- string title: title of the file explorer. -- string curdir: the directory to initially open the file explorer in, or nil for the current directory. @@ -20,7 +32,8 @@ return function(title, curdir, exts, type) -- Variables local sel = 1 local scroll = 0 - local files = ctr.fs.list(curdir) + local files = sort(ctr.fs.list(curdir)) + if curdir ~= "/" then table.insert(files, 1, { name = "..", isDirectory = true }) end local newFileName = "" @@ -59,7 +72,7 @@ return function(title, curdir, exts, type) sel = 1 scroll = 0 - files = ctr.fs.list(curdir) + files = sort(ctr.fs.list(curdir)) if curdir ~= "/" then table.insert(files, 1, { name = "..", isDirectory = true }) diff --git a/source/fs.c b/source/fs.c index 6d367cb..f442999 100644 --- a/source/fs.c +++ b/source/fs.c @@ -48,7 +48,7 @@ const char* prefix_path(const char* path) { } /*** -Lists a directory contents. +Lists a directory contents (unsorted). @function list @tparam string path the directory we wants to list the content @treturn table the item list. Each item is a table like: From ca22cf15580293e223acebe8eaa1aee9373ea35a Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Tue, 5 Jan 2016 19:41:25 +0100 Subject: [PATCH 054/101] =?UTF-8?q?Added=20ctr.mic,=20Added=20a=20function?= =?UTF-8?q?=20to=20get=20a=20=C2=B5s=20counter=20value.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Didn't test ctr.mic. --- source/audio.c | 50 ++++++++ source/ctr.c | 25 +++- source/mic.c | 304 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 377 insertions(+), 2 deletions(-) create mode 100644 source/mic.c diff --git a/source/audio.c b/source/audio.c index 6aa75b4..1f72900 100644 --- a/source/audio.c +++ b/source/audio.c @@ -242,6 +242,55 @@ static int audio_load(lua_State *L) { return 0; } +/*** +Load raw audio data from a string. +@function loadRaw +@tparam string data raw audio data +@tparam number rate sampling rate +@tparam string encoding audio encoding, can be `"PCM8"`, `"PCM16"` or `"ADPCM"` +@tparam[opt=1] number channels audio channels count +@treturn[1] audio the loaded audio object +@treturn[2] nil if a error happened +@treturn[2] string error message +*/ +static int audio_loadRaw(lua_State *L) { + size_t dataSize; + char* data = (char*)luaL_checklstring(L, 1, &dataSize); + float rate = luaL_checkinteger(L, 2); + const char* argEncoding = luaL_checkstring(L, 3); + u32 channels = luaL_optinteger(L, 4, 1); + + audio_userdata *audio = lua_newuserdata(L, sizeof(*audio)); + luaL_getmetatable(L, "LAudio"); + lua_setmetatable(L, -2); + + audio->type = TYPE_WAV; + audio->rate = rate; + audio->channels = channels; + + u8 sampleSize = 2; // default to 2 + if (strcmp(argEncoding, "PCM8")) { + audio->encoding = NDSP_ENCODING_PCM8; + sampleSize = 1; + } else if (strcmp(argEncoding, "PCM16")) { + audio->encoding = NDSP_ENCODING_PCM16; + } else if (strcmp(argEncoding, "ADPCM")) { + audio->encoding = NDSP_ENCODING_ADPCM; + } else { + lua_pushnil(L); + lua_pushstring(L, "Wrong format"); + return 2; + } + + audio->nsamples = dataSize/sampleSize; + audio->size = dataSize; + audio->data = data; + + audio->speed = 1.0; + + return 1; +} + /*** Check if audio is currently playing on a channel. @function playing @@ -817,6 +866,7 @@ static const struct luaL_Reg audio_object_methods[] = { // Library functions static const struct luaL_Reg audio_lib[] = { { "load", audio_load }, + { "loadRaw", audio_loadRaw }, { "playing", audio_playing }, { "mix", audio_mix }, { "interpolation", audio_interpolation }, diff --git a/source/ctr.c b/source/ctr.c index 43f15dc..06f7292 100644 --- a/source/ctr.c +++ b/source/ctr.c @@ -6,6 +6,7 @@ The `ctr` module. #include <3ds/types.h> #include <3ds/services/apt.h> #include <3ds/os.h> +#include <3ds/svc.h> #include #include @@ -106,6 +107,13 @@ The `ctr.apt` module. */ void load_apt_lib(lua_State *L); +/*** +The `ctr.mic` module. +@table mic +@see ctr.mic +*/ +void load_mic_lib(lua_State *L); + /*** Return whether or not the program should continue. @function run @@ -128,10 +136,22 @@ static int ctr_time(lua_State *L) { return 1; } +/*** +Return a number of microseconds based on the system ticks. +@function utime +@treturn number microseconds +*/ +static int ctr_utime(lua_State *L) { + lua_pushinteger(L, svcGetSystemTick()/268.123480); + + return 1; +} + // Functions static const struct luaL_Reg ctr_lib[] = { - { "run", ctr_run }, - { "time", ctr_time}, + { "run", ctr_run }, + { "time", ctr_time }, + { "utime", ctr_utime}, { NULL, NULL } }; @@ -150,6 +170,7 @@ struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } { "cam", load_cam_lib, NULL }, { "audio", load_audio_lib, unload_audio_lib }, { "apt", load_apt_lib, NULL }, + { "mic", load_mic_lib, NULL }, { NULL, NULL } }; diff --git a/source/mic.c b/source/mic.c new file mode 100644 index 0000000..ed9d308 --- /dev/null +++ b/source/mic.c @@ -0,0 +1,304 @@ +/*** +The `mic` module. +@module ctr.mic +@usage local mic = require("ctr.mic") +*/ + +#include <3ds/types.h> +#include <3ds/services/mic.h> + +#include +#include + +#include +#include + +u8* buff; +u32 bufferSize = 0; + +/*** +Initialize the mic module. +@function init +@tparam[opt=0x50000] number bufferSize size of the buffer (must be a multiple of 0x1000) +*/ +static int mic_init(lua_State *L) { + bufferSize = luaL_optinteger(L, 1, 0x50000); + + buff = memalign(0x1000, bufferSize); + if (buff == NULL) { + lua_pushnil(L); + lua_pushstring(L, "Couldn't allocate buffer"); + return 2; + } + Result ret = micInit(buff, bufferSize); + if (ret) { + free(buff); + lua_pushnil(L); + lua_pushinteger(L, ret); + return 2; + } + + lua_pushboolean(L, true); + return 1; +} + +/*** +Shutdown the mic module. +@function shutdown +*/ +static int mic_shutdown(lua_State *L) { + micExit(); + free(buff); + return 0; +} + +/*** +Start sampling from the mic. +@function startSampling +@tparam[opt="PCM8"] encoding encoding encoding of the data to record, can be `"PCM8"` or `"PCM16"` +@tparam[opt=8180] number rate sampling rate, can be `8180`, `10910`, `16360` or `32730` +@tparam[opt=false] boolean loop if true, loop back to the beginning of the buffer when the end is reached +@tparam[opt=bufferFreeSize-4] number size size of audio data to write to the buffer, can be reduced to fit in the buffer +@tparam[opt=false] boolean restart if `true`, start at position 0 in the buffer; if `false`, start after the last sample +*/ +static int mic_startSampling(lua_State *L) { + const char *encodingArg = luaL_optstring(L, 1, "PCM8"); + MICU_Encoding encoding = MICU_ENCODING_PCM8; + if (strcmp(encodingArg, "PCM16")) { + encoding = MICU_ENCODING_PCM16; + } + + u16 rateArg = luaL_optinteger(L, 2, 8180); + MICU_SampleRate rate = MICU_SAMPLE_RATE_8180; + switch (rateArg) { + case 10910: + rate = MICU_SAMPLE_RATE_10910; + case 16360: + rate = MICU_SAMPLE_RATE_16360; + case 32730: + rate = MICU_SAMPLE_RATE_32730; + } + + bool loop = false; + if (lua_isboolean(L, 3)) + loop = lua_toboolean(L, 3); + + + u32 currentSampleSize = micGetSampleDataSize(); + u32 size = luaL_optinteger(L, 4, bufferSize-currentSampleSize-4); + if (size > (bufferSize-currentSampleSize-4)) { + size = bufferSize-currentSampleSize-4; + } + + u32 offset = currentSampleSize; + if (lua_isboolean(L, 5) && lua_toboolean(L, 5)) // restart to 0 + offset = 0; + + MICU_StartSampling(encoding, rate, offset, size, loop); + + return 0; +} + +/*** +Stop sampling from the mic. +@function stopSampling +*/ +static int mic_stopSampling(lua_State *L) { + MICU_StopSampling(); + + return 0; +} + +/*** +Adjust the sampling rate. +@function adjustSampling +@tparam number rate sampling rate, can be `8180`, `10910`, `16360` or `32730` +*/ +static int mic_adjustSampling(lua_State *L) { + u16 rateArg = luaL_checkinteger(L, 1); + MICU_SampleRate rate = MICU_SAMPLE_RATE_8180; + switch (rateArg) { + case 10910: + rate = MICU_SAMPLE_RATE_10910; + case 16360: + rate = MICU_SAMPLE_RATE_16360; + case 32730: + rate = MICU_SAMPLE_RATE_32730; + } + + MICU_AdjustSampling(rate); + + return 0; +} + +/*** +Check whether the mic is sampling. +@function isSampling +@treturn boolean `true` if sampling +*/ +static int mic_isSampling(lua_State *L) { + bool sampling; + MICU_IsSampling(&sampling); + + lua_pushboolean(L, sampling); + return 1; +} + +/*** +Return a string containing the raw sampled audio data. +@function getData +@tparam[opt=true] boolean lastSampleOnly set to `true` to only get the last sample, and to `false` to get everything +@treturn string raw audio data +*/ +static int mic_getData(lua_State *L) { + bool last = false; + if (lua_isboolean(L, 1)) + last = lua_toboolean(L, 1); + + u32 offset = 0; + if (last) { + offset = micGetLastSampleOffset(); + } + u32 size = micGetSampleDataSize(); + + char* data = malloc(size-offset); + for (int i=offset;i Date: Sun, 17 Jan 2016 12:18:39 +0100 Subject: [PATCH 055/101] ADDED THREAD ! Minor changes on other libs. To communicate with a thread, use sockets on localhost, this should work. Or use the return code, or the "last error", but that's crappy. For the return code, just `return `; only works with integers. --- source/audio.c | 4 +- source/ctr.c | 8 +++ source/fs.c | 25 +++++--- source/gfx.c | 6 +- source/httpc.c | 7 ++- source/thread.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 196 insertions(+), 14 deletions(-) create mode 100644 source/thread.c diff --git a/source/audio.c b/source/audio.c index 1f72900..2222999 100644 --- a/source/audio.c +++ b/source/audio.c @@ -887,7 +887,9 @@ int luaopen_audio_lib(lua_State *L) { } void load_audio_lib(lua_State *L) { - isAudioInitialized = !ndspInit(); // ndspInit returns 0 in case of success + if (!isAudioInitialized) { + isAudioInitialized = !ndspInit(); // ndspInit returns 0 in case of success + } luaL_requiref(L, "ctr.audio", luaopen_audio_lib, false); } diff --git a/source/ctr.c b/source/ctr.c index 06f7292..64f55e5 100644 --- a/source/ctr.c +++ b/source/ctr.c @@ -114,6 +114,13 @@ The `ctr.mic` module. */ void load_mic_lib(lua_State *L); +/*** +The `ctr.thread` module. +@table thread +@see ctr.thread +*/ +void load_thread_lib(lua_State *L); + /*** Return whether or not the program should continue. @function run @@ -171,6 +178,7 @@ struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } { "audio", load_audio_lib, unload_audio_lib }, { "apt", load_apt_lib, NULL }, { "mic", load_mic_lib, NULL }, + { "thread", load_thread_lib, NULL }, { NULL, NULL } }; diff --git a/source/fs.c b/source/fs.c index f442999..68e13e3 100644 --- a/source/fs.c +++ b/source/fs.c @@ -14,6 +14,8 @@ The `fs` module. #include #include +bool isFsInitialized = false; + Handle *fsuHandle; FS_Archive sdmcArchive; #ifdef ROMFS @@ -211,18 +213,21 @@ int luaopen_fs_lib(lua_State *L) { } void load_fs_lib(lua_State *L) { - fsInit(); + if (!isFsInitialized) { + fsInit(); - fsuHandle = fsGetSessionHandle(); - FSUSER_Initialize(*fsuHandle); - - sdmcArchive = (FS_Archive){ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")}; - FSUSER_OpenArchive(&sdmcArchive); - #ifdef ROMFS - romfsArchive = (FS_Archive){ARCHIVE_ROMFS, fsMakePath(PATH_EMPTY, "")}; - FSUSER_OpenArchive(&romfsArchive); - #endif + fsuHandle = fsGetSessionHandle(); + FSUSER_Initialize(*fsuHandle); + sdmcArchive = (FS_Archive){ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")}; + FSUSER_OpenArchive(&sdmcArchive); + #ifdef ROMFS + romfsArchive = (FS_Archive){ARCHIVE_ROMFS, fsMakePath(PATH_EMPTY, "")}; + FSUSER_OpenArchive(&romfsArchive); + #endif + isFsInitialized = true; + } + luaL_requiref(L, "ctr.fs", luaopen_fs_lib, false); } diff --git a/source/gfx.c b/source/gfx.c index 9bae7ca..18a110b 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -456,8 +456,10 @@ int luaopen_gfx_lib(lua_State *L) { } void load_gfx_lib(lua_State *L) { - sf2d_init(); - sftd_init(); + if (!isGfxInitialized) { + sf2d_init(); + sftd_init(); + } isGfxInitialized = true; diff --git a/source/httpc.c b/source/httpc.c index e104e18..eed4590 100644 --- a/source/httpc.c +++ b/source/httpc.c @@ -13,6 +13,8 @@ The `httpc` module. #include #include +bool isHttpcInitialized = false; + /*** Create a HTTP Context. @function context @@ -205,7 +207,10 @@ int luaopen_httpc_lib(lua_State *L) { } void load_httpc_lib(lua_State *L) { - httpcInit(); + if (!isHttpcInitialized) { + httpcInit(); + isHttpcInitialized = true; + } luaL_requiref(L, "ctr.httpc", luaopen_httpc_lib, false); } diff --git a/source/thread.c b/source/thread.c new file mode 100644 index 0000000..4fc8836 --- /dev/null +++ b/source/thread.c @@ -0,0 +1,160 @@ +/*** +The `thread` module. +@module ctr.thread +@usage local thread = require("ctr.thread") +*/ + +#include +#include +#include + +#include <3ds/types.h> +#include <3ds/thread.h> +#include <3ds/services/apt.h> + +#include +#include + +void load_ctr_lib(lua_State *L); + +typedef struct { + Thread thread; + const char *code; + char *error; +} thread_userdata; + +void entryPoint(void *thread) { + lua_State *T = luaL_newstate(); + luaL_openlibs(T); + load_ctr_lib(T); + + if (luaL_dostring(T, ((thread_userdata*)thread)->code)) { + const char* lerror = luaL_checkstring(T, -1); + ((thread_userdata*)thread)->error = malloc(strlen(lerror)+1); + strcpy(((thread_userdata*)thread)->error, lerror); + } + + int exitCode = 0; + if (lua_isinteger(T, -1)) { + exitCode = lua_tointeger(T, -1); + } + lua_close(T); + threadExit(exitCode); +} + +// module +/*** +Set the maximum CPU time allocated to threads on CPU #1. +@function setCpuLimit +@tparam number time in percents. +*/ +static int thread_setCpuLimit(lua_State *L) { + u32 percent = luaL_checkinteger(L, 1); + APT_SetAppCpuTimeLimit(percent); + + return 0; +} + +/*** +Start a new thread. +@function start +@tparam string code Lua code to load in the new thread. May not work with dumped functions. +@tparam[opt=0] number cpu must be >= 0 and < 2 for 3ds or < 4 for new3ds +@tparam[opt=0x27] number priority must be > 0x18 and < 0x3f; the lower is higher. +@tparam[opt=0x100000] number stacksize size of the stack, increase it in case of OoM +@treturn thread a new thread object +*/ +static int thread_start(lua_State *L) { + const char* code = luaL_checkstring(L, 1); + s32 processor = luaL_optinteger(L, 2, 0); + s32 priority = luaL_optinteger(L, 3, 0x27); + size_t stackSize = luaL_optinteger(L, 4, 0x100000); + + thread_userdata *thread = lua_newuserdata(L, sizeof(thread_userdata*)); + luaL_getmetatable(L, "LThread"); + lua_setmetatable(L, -2); + + thread->code = code; + thread->error = NULL; + thread->thread = threadCreate(entryPoint, thread, stackSize, priority, processor, true); + + return 1; +} + +/*** +Wait for a thread to finish. +@function :join +@tparam[opt=2^32-1] number timeout in ns +@treturn number exit code +@treturn string last error, or nil +*/ +static int thread_join(lua_State *L) { + thread_userdata *thread = luaL_checkudata(L, 1, "LThread"); + u64 timeout = luaL_optinteger(L, 2, 4294967295); + + threadJoin(thread->thread, timeout); + + lua_pushinteger(L, threadGetExitCode(thread->thread)); + if (thread->error != NULL) { + lua_pushstring(L, thread->error); + return 2; + } + return 1; +} + +/*** +Return the last error of a thread. +@function :lastError +@treturn string last error, or nil +*/ +static int thread_lastError(lua_State *L) { + thread_userdata *thread = luaL_checkudata(L, 1, "LThread"); + + if (thread->error == NULL) { + lua_pushnil(L); + } else { + lua_pushstring(L, thread->error); + } + return 1; +} + +/*** +Destroy a finished thread. +@function :destroy +*/ +static int thread_destroy(lua_State *L) { + thread_userdata *thread = luaL_checkudata(L, 1, "LThread"); + + threadFree(thread->thread); + free(thread->error); + + return 0; +} + +static const struct luaL_Reg thread_lib[] = { + {"start", thread_start}, + {"setCpuLimit", thread_setCpuLimit}, + {NULL, NULL} +}; + +static const struct luaL_Reg thread_methods[] = { + {"join", thread_join}, + {"lastError", thread_lastError}, + {"destroy", thread_destroy}, + {"__gc", thread_destroy}, + {NULL, NULL} +}; + +int luaopen_thread_lib(lua_State *L) { + luaL_newmetatable(L, "LThread"); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + luaL_setfuncs(L, thread_methods, 0); + + luaL_newlib(L, thread_lib); + return 1; +} + +void load_thread_lib(lua_State *L) { + luaL_requiref(L, "ctr.thread", luaopen_thread_lib, 0); +} From 116575fb5fab2effcb645d2dcd730d956aa677f8 Mon Sep 17 00:00:00 2001 From: Neil Zeke Cecchini Date: Wed, 24 Feb 2016 14:19:16 +0100 Subject: [PATCH 056/101] Replaced the old shell with LSH --- sdcard/3ds/ctruLua/libs/filepicker.lua | 199 +++++++++++++++++++++++++ sdcard/3ds/ctruLua/main.lua | 71 +++++---- 2 files changed, 243 insertions(+), 27 deletions(-) create mode 100644 sdcard/3ds/ctruLua/libs/filepicker.lua diff --git a/sdcard/3ds/ctruLua/libs/filepicker.lua b/sdcard/3ds/ctruLua/libs/filepicker.lua new file mode 100644 index 0000000..61a3dfb --- /dev/null +++ b/sdcard/3ds/ctruLua/libs/filepicker.lua @@ -0,0 +1,199 @@ +-- LSH version 0.1 +-- ctrµLua official shell + +local ctr = require("ctr") +local gfx = require("ctr.gfx") + +local function saveGraphicsState() + local old = {gfx.get3D(), gfx.color.getDefault(), gfx.color.getBackground(), + gfx.font.getDefault()} + + local mono = gfx.font.load(ctruLua.root .. "resources/VeraMono.ttf") + + gfx.set3D(false) + gfx.color.setDefault(0xFFFDFDFD) + gfx.color.setBackground(0xFF333333) + gfx.font.setDefault(mono) + + return old +end + +local function restoreGraphicsState(state) + gfx.set3D(state[1]) + gfx.color.setDefault(state[2]) + gfx.color.setBackground(state[3]) + gfx.font.setDefault(state[4]) +end + +local function getExtension(sel, bindings) + for _, ext in ipairs(bindings) do + if ext.ext == sel:match("%..+$") then + return ext + end + end +end + +local function getFilelist(cur) + local files = ctr.fs.list(cur) + + if cur ~= "/" and cur ~= "sdmc:/" then + table.insert(files, {name = "..", isDirectory = true}) + end + + -- Stealy stealing code from original openfile.lua + table.sort(files, function(i, j) + if i.isDirectory and not j.isDirectory then + return true + elseif i.isDirectory == j.isDirectory then + return string.lower(i.name) < string.lower(j.name) + end + end) + + return files +end + +local function drawBottom(cur, selFile, bindings) + local ext = getExtension(selFile.name, bindings) + + gfx.start(gfx.BOTTOM) + gfx.rectangle(0, 0, gfx.BOTTOM_WIDTH, 16, 0, 0xFF0000B3) + gfx.text(1, 0, cur, 12) + gfx.text(1, 15, selFile.name, 12) + if not selFile.isDirectory then + gfx.text(1, 45, selFile.fileSize, 12) + end + + local keys = {"X: Quit/Cancel"} + if selFile.isDirectory then + gfx.text(1, 30, "Directory", 12, 0xFF727272) + gfx.text(1, gfx.BOTTOM_HEIGHT - 30, "A: Open", 12) + gfx.text(1, gfx.BOTTOM_HEIGHT - 15, keys[1], 12) + elseif ext then + local lines = 1 + + -- Keys + if ext.y then + lines = lines + 1 + table.insert(keys, "Y: " .. ext.y) + end + if ext.a then + lines = lines + 1 + table.insert(keys, "A: " .. ext.a) + end + + -- Drawing + for i=lines, 1, -1 do + gfx.text(1, gfx.BOTTOM_HEIGHT - 15*i, keys[i], 12) + end + gfx.text(1, 30, ext.name, 12, 0xFF727272) + gfx.text(1, 45, tostring(selFile.fileSize) .. "B", 12, 0xFF727272) + else + gfx.text(1, 30, "File", 12, 0xFF727272) + gfx.text(1, 45, tostring(selFile.fileSize) .. "B", 12, 0xFF727272) + gfx.text(1, gfx.BOTTOM_HEIGHT - 15, keys[1], 12) + end + gfx.stop() +end + +local function drawTop(files, sel, scr) + gfx.start(gfx.TOP) + gfx.rectangle(0, (sel-scr-1)*15, gfx.TOP_WIDTH, 16, 0, 0xFF0000B3) + local over = #files - scr >= 16 and 16 or #files - scr + for i=scr+1, scr+over do + local color = files[i].isDirectory and 0xFF727272 or 0xFFFDFDFD + gfx.text(1, (i-scr-1)*15+1, files[i].name or "", 12, color) + end + gfx.stop() +end + +local function runA(cur, selFile, bindings) + if not selFile.isDirectory then + local ext = getExtension(selFile.name, bindings) + if not ext then return end + if ext.a then return cur .. selFile.name, ext.ext end + end +end + +local function runY(cur, selFile, bindings) + if not selFile.isDirectory then + local ext = getExtension(selFile.name, bindings) + if not ext then return end + if ext.y then return cur .. selFile.name, ext.ext end + end +end + +--- Open a file browser to allow the user to select a file. +-- It will save current graphical settings and set them back after ending. Press up or down to move one element at a time, and press left or right to go at the beginning or at the end of the list. This function is the return result of requiring filepicker.lua +-- @name filePicker +-- @param bindings A table of the extensions the user can select in the format {{name, ext, a, y},...}, name will show up instead of "File" or "Directory" on the bottom screen, a and y set the action names for those keys on the bottom screen (and also enable them, so if there's neither a or y, the file will have a custom type name but won't be effectively selectable), and ext is the extension to search for, dot included. Everything must be strings. +-- @param workdir Optional, current working directory will be used if not specified, otherwise, sets the path at which the file browser first shows up, a string. +-- @returns The absolute path to the file, nil in case no file was picked. +-- @returns The extension of the file, this might be helpful in cases were multiple file types could be expected, nil in case no file was picked. +-- @returns The "mode", which indicates which key was used to select the file, "A" or "Y". "X" in case no file was picked. +return function(bindings, workdir) + -- Initialization + local old = saveGraphicsState() + local cur = workdir or ctr.fs.getDirectory() + if cur:sub(-1) ~= "/" then + cur = cur .. "/" + end + local bindings = bindings or {} + + local files = getFilelist(cur) or {{name = "- Empty -"}} + local sel = 1 + local scr = 0 + + while ctr.run() do + drawBottom(cur, files[sel], bindings) + drawTop(files, sel, scr) + gfx.render() + + ctr.hid.read() + local state = ctr.hid.keys() + if (state.down.dDown or state.down.cpadDown) and sel < #files then + sel = sel + 1 + if sel - scr >= 16 then + scr = scr + 1 + end + elseif (state.down.dUp or state.down.cpadUp) and sel > 1 then + sel = sel - 1 + if sel == scr then + scr = scr - 1 + end + elseif state.down.dLeft or state.down.cpadLeft then + sel = 1 + scr = 0 + elseif state.down.dRight or state.down.cpadRight then + sel = #files + if #files > 15 then + scr = #files - 16 + end + + elseif state.down.a then + local selFile = files[sel] + if selFile.isDirectory then + if selFile.name == ".." then + cur = cur:gsub("[^/]+/$", "") + else + cur = cur .. selFile.name .. "/" + end + files, sel, scr = getFilelist(cur), 1, 0 + else + local file, ext = runA(cur, selFile, bindings) + if file then + restoreGraphicsState(old) + return file, ext, "A" + end + end + elseif state.down.y then + local file, ext = runY(cur, files[sel], bindings) + if file then + restoreGraphicsState(old) + return file, ext, "Y" + end + elseif state.down.x then + restoreGraphicsState(old) + return nil, nil, "X" + end + end +end \ No newline at end of file diff --git a/sdcard/3ds/ctruLua/main.lua b/sdcard/3ds/ctruLua/main.lua index 4b9540d..66ef48d 100644 --- a/sdcard/3ds/ctruLua/main.lua +++ b/sdcard/3ds/ctruLua/main.lua @@ -1,33 +1,50 @@ +local ctr = require("ctr") +local fs = require("ctr.fs") local gfx = require("ctr.gfx") -local fs = require("ctr.fs") + +-- Initializing "constants" +ctruLua = {} +ctruLua.root = fs.getDirectory() -- Set up path local ldir = fs.getDirectory().."libs/" package.path = package.path..";".. ldir.."?.lua;".. ldir.."?/init.lua" -repeat - gfx.set3D(false) - gfx.color.setDefault(0xFFFFFFFF) - gfx.color.setBackground(0xFF000000) - gfx.font.setDefault() - local file = require("openfile")("Choose a Lua file to execute", nil, ".lua", "exist") - if file then - fs.setDirectory(file:match("^(.-)[^/]*$")) - local success, err = pcall(dofile, file) - if not success then - local hid = require("ctr.hid") - gfx.set3D(false) - gfx.color.setDefault(0xFFFFFFFF) - gfx.color.setBackground(0xFF000000) - gfx.font.setDefault() - while true do - hid.read() - if hid.keys().down.start then break end - gfx.start(gfx.TOP) - gfx.wrappedText(0, 0, err, gfx.TOP_WIDTH) - gfx.stop() - gfx.render() - end - end - end -until not file \ No newline at end of file +-- Erroring +local function displayError(err) + gfx.color.setBackground(0xFF0000B3) + gfx.color.setDefault(0xFFFDFDFD) + gfx.font.setDefault(gfx.font.load(ctruLua.root .. "resources/VeraMono.ttf")) + + while ctr.run() do + gfx.start(gfx.TOP) + gfx.text(1, 1, "An error has occured.", 12) + gfx.wrappedText(1, 30, err, gfx.TOP_WIDTH-2, 12) + gfx.text(1, gfx.TOP_HEIGHT-15, "Press Start to continue.", 12) + gfx.stop() + gfx.start(gfx.BOTTOM) + gfx.stop() + + gfx.render() + ctr.hid.read() + if ctr.hid.keys().down.start then break end + end +end + +-- Main loop +while ctr.run() do + gfx.set3D(false) + gfx.font.setDefault() + gfx.color.setDefault(0xFFFDFDFD) + gfx.color.setBackground(0xFF333333) + local file, ext, mode = require("filepicker")({{name="Lua Script", ext=".lua", a="Execute"}}) + if file and mode == "A" then + fs.setDirectory(file:match("^(.-)[^/]*$")) + local ok, err = pcall(dofile, file) + if not ok then displayError(err) end + else + break + end +end + +error("Main process has exited.\nPlease reboot.\nPressing Start does not work yet.") \ No newline at end of file From 3eb41b5062d2b204d4e73f6c6613ed269eb9caf1 Mon Sep 17 00:00:00 2001 From: Neil Zeke Cecchini Date: Wed, 24 Feb 2016 14:23:36 +0100 Subject: [PATCH 057/101] Tidbits of documentation --- sdcard/3ds/ctruLua/main.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdcard/3ds/ctruLua/main.lua b/sdcard/3ds/ctruLua/main.lua index 66ef48d..196a8c9 100644 --- a/sdcard/3ds/ctruLua/main.lua +++ b/sdcard/3ds/ctruLua/main.lua @@ -4,6 +4,8 @@ local gfx = require("ctr.gfx") -- Initializing "constants" ctruLua = {} + +--- The ctruLua root directory's absolute path ctruLua.root = fs.getDirectory() -- Set up path From a380f09a348f81a6da17535da10c8c82822af031 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Wed, 24 Feb 2016 14:46:11 +0100 Subject: [PATCH 058/101] Updated httpc.c to the latest ctrulib, fixed the cfgu example. The name is still buggy, can't find why. --- sdcard/3ds/ctruLua/examples/cfgu/cfgu.lua | 3 +- source/httpc.c | 59 ++++++++++++++++++++--- 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/sdcard/3ds/ctruLua/examples/cfgu/cfgu.lua b/sdcard/3ds/ctruLua/examples/cfgu/cfgu.lua index 90db095..3ca851a 100644 --- a/sdcard/3ds/ctruLua/examples/cfgu/cfgu.lua +++ b/sdcard/3ds/ctruLua/examples/cfgu/cfgu.lua @@ -36,6 +36,7 @@ local models = { [cfgu.MODEL_N3DSXL] = "New 3DS XL" } +cfgu.init() while ctr.run() do hid.read() keys = hid.keys() @@ -45,7 +46,7 @@ while ctr.run() do gfx.text(2, 2, "CFGU example") gfx.text(2, 20, "Region: "..regions[cfgu.getRegion()]) gfx.text(2, 30, "Model: "..models[cfgu.getModel()]) - gfx.text(2, 40, "Language: "..models[cfgu.getLanguage()]) + gfx.text(2, 40, "Language: "..languages[cfgu.getLanguage()]) gfx.text(2, 50, "Username: "..cfgu.getUsername()) local m,d = cfgu.getBirthday() gfx.text(2, 60, "Birthday: "..d.."/"..m) diff --git a/source/httpc.c b/source/httpc.c index eed4590..1386880 100644 --- a/source/httpc.c +++ b/source/httpc.c @@ -22,12 +22,6 @@ Create a HTTP Context. */ static int httpc_context(lua_State *L) { httpcContext context; - Result ret = httpcOpenContext(&context, "http://google.com/", 0); // Initialization only. - if (ret != 0) { - lua_pushnil(L); - lua_pushinteger(L, ret); - return 2; - } lua_newuserdata(L, sizeof(&context)); luaL_getmetatable(L, "LHTTPC"); lua_setmetatable(L, -2); @@ -44,13 +38,25 @@ context object Open an url in the context. @function :open @tparam string url the url to open +@tparam[opt="GET"] string method method to use; can be `"GET"`, `"POST"`, `"HEAD"`, `"PUT"` or `"DELETE"` */ static int httpc_open(lua_State *L) { httpcContext *context = lua_touserdata(L, 1); char *url = (char*)luaL_checkstring(L, 2); + char *smethod = (char*)luaL_optstring(L, 3, "GET"); + HTTPC_RequestMethod method = HTTPC_METHOD_GET; // default to GET + if (strcmp(smethod, "POST")) { + method = HTTPC_METHOD_POST; + } else if (strcmp(smethod, "HEAD")) { + method = HTTPC_METHOD_HEAD; + } else if (strcmp(smethod, "PUT")) { + method = HTTPC_METHOD_PUT; + } else if (strcmp(smethod, "DELETE")) { + method = HTTPC_METHOD_DELETE; + } Result ret = 0; - ret = httpcOpenContext(context, url, 0); + ret = httpcOpenContext(context, method, url, 0); if (ret != 0) { lua_pushnil(L); lua_pushinteger(L, ret); @@ -177,6 +183,41 @@ static int httpc_close(lua_State *L) { return 0; } +/*** +Add a POST form field to a HTTP context. +@function :addPostData +@tparam string name name of the field +@tparam string value value of the field +*/ +static int httpc_addPostData(lua_State *L) { + httpcContext *context = lua_touserdata(L, 1); + char *name = (char*)luaL_checkstring(L, 2); + char *value = (char*)luaL_checkstring(L, 3); + + httpcAddPostDataAscii(context, name, value); + + return 0; +} + +/*** +Get a header field from a response. +@function :getResponseHeader +@tparam string name name of the header field to get +@tparam[opt=2048] number maximum size of the value to get +@treturn string field value +*/ +static int httpc_getResponseHeader(lua_State *L) { + httpcContext *context = lua_touserdata(L, 1); + char *name = (char*)luaL_checkstring(L, 2); + u32 maxSize = luaL_checkinteger(L, 3); + char* value = 0; + + httpcGetResponseHeader(context, name, value, maxSize); + + lua_pushstring(L, value); + return 1; +} + // object static const struct luaL_Reg httpc_methods[] = { {"open", httpc_open }, @@ -186,6 +227,8 @@ static const struct luaL_Reg httpc_methods[] = { {"getDownloadSize", httpc_getDownloadSize }, {"downloadData", httpc_downloadData }, {"close", httpc_close }, + {"addPostData", httpc_addPostData }, + {"getResponseHeader", httpc_getResponseHeader }, {NULL, NULL} }; @@ -208,7 +251,7 @@ int luaopen_httpc_lib(lua_State *L) { void load_httpc_lib(lua_State *L) { if (!isHttpcInitialized) { - httpcInit(); + httpcInit(0x1000); isHttpcInitialized = true; } From b6beaddf66c71362136714057e087b478d966db9 Mon Sep 17 00:00:00 2001 From: Neil Zeke Cecchini Date: Wed, 24 Feb 2016 17:55:12 +0100 Subject: [PATCH 059/101] Tabulation correction --- sdcard/3ds/ctruLua/libs/filepicker.lua | 290 ++++++++++++------------- sdcard/3ds/ctruLua/main.lua | 56 ++--- 2 files changed, 173 insertions(+), 173 deletions(-) diff --git a/sdcard/3ds/ctruLua/libs/filepicker.lua b/sdcard/3ds/ctruLua/libs/filepicker.lua index 61a3dfb..7a017cf 100644 --- a/sdcard/3ds/ctruLua/libs/filepicker.lua +++ b/sdcard/3ds/ctruLua/libs/filepicker.lua @@ -5,121 +5,121 @@ local ctr = require("ctr") local gfx = require("ctr.gfx") local function saveGraphicsState() - local old = {gfx.get3D(), gfx.color.getDefault(), gfx.color.getBackground(), - gfx.font.getDefault()} + local old = {gfx.get3D(), gfx.color.getDefault(), gfx.color.getBackground(), + gfx.font.getDefault()} - local mono = gfx.font.load(ctruLua.root .. "resources/VeraMono.ttf") + local mono = gfx.font.load(ctruLua.root .. "resources/VeraMono.ttf") - gfx.set3D(false) - gfx.color.setDefault(0xFFFDFDFD) - gfx.color.setBackground(0xFF333333) - gfx.font.setDefault(mono) + gfx.set3D(false) + gfx.color.setDefault(0xFFFDFDFD) + gfx.color.setBackground(0xFF333333) + gfx.font.setDefault(mono) - return old + return old end local function restoreGraphicsState(state) - gfx.set3D(state[1]) - gfx.color.setDefault(state[2]) - gfx.color.setBackground(state[3]) - gfx.font.setDefault(state[4]) + gfx.set3D(state[1]) + gfx.color.setDefault(state[2]) + gfx.color.setBackground(state[3]) + gfx.font.setDefault(state[4]) end local function getExtension(sel, bindings) - for _, ext in ipairs(bindings) do - if ext.ext == sel:match("%..+$") then - return ext - end - end + for _, ext in ipairs(bindings) do + if ext.ext == sel:match("%..+$") then + return ext + end + end end local function getFilelist(cur) - local files = ctr.fs.list(cur) + local files = ctr.fs.list(cur) - if cur ~= "/" and cur ~= "sdmc:/" then - table.insert(files, {name = "..", isDirectory = true}) - end + if cur ~= "/" and cur ~= "sdmc:/" then + table.insert(files, {name = "..", isDirectory = true}) + end - -- Stealy stealing code from original openfile.lua - table.sort(files, function(i, j) - if i.isDirectory and not j.isDirectory then - return true - elseif i.isDirectory == j.isDirectory then - return string.lower(i.name) < string.lower(j.name) - end - end) + -- Stealy stealing code from original openfile.lua + table.sort(files, function(i, j) + if i.isDirectory and not j.isDirectory then + return true + elseif i.isDirectory == j.isDirectory then + return string.lower(i.name) < string.lower(j.name) + end + end) - return files + return files end local function drawBottom(cur, selFile, bindings) - local ext = getExtension(selFile.name, bindings) + local ext = getExtension(selFile.name, bindings) - gfx.start(gfx.BOTTOM) - gfx.rectangle(0, 0, gfx.BOTTOM_WIDTH, 16, 0, 0xFF0000B3) - gfx.text(1, 0, cur, 12) - gfx.text(1, 15, selFile.name, 12) - if not selFile.isDirectory then - gfx.text(1, 45, selFile.fileSize, 12) - end + gfx.start(gfx.BOTTOM) + gfx.rectangle(0, 0, gfx.BOTTOM_WIDTH, 16, 0, 0xFF0000B3) + gfx.text(1, 0, cur, 12) + gfx.text(1, 15, selFile.name, 12) + if not selFile.isDirectory then + gfx.text(1, 45, selFile.fileSize, 12) + end - local keys = {"X: Quit/Cancel"} - if selFile.isDirectory then - gfx.text(1, 30, "Directory", 12, 0xFF727272) - gfx.text(1, gfx.BOTTOM_HEIGHT - 30, "A: Open", 12) - gfx.text(1, gfx.BOTTOM_HEIGHT - 15, keys[1], 12) - elseif ext then - local lines = 1 + local keys = {"X: Quit/Cancel"} + if selFile.isDirectory then + gfx.text(1, 30, "Directory", 12, 0xFF727272) + gfx.text(1, gfx.BOTTOM_HEIGHT - 30, "A: Open", 12) + gfx.text(1, gfx.BOTTOM_HEIGHT - 15, keys[1], 12) + elseif ext then + local lines = 1 - -- Keys - if ext.y then - lines = lines + 1 - table.insert(keys, "Y: " .. ext.y) - end - if ext.a then - lines = lines + 1 - table.insert(keys, "A: " .. ext.a) - end + -- Keys + if ext.y then + lines = lines + 1 + table.insert(keys, "Y: " .. ext.y) + end + if ext.a then + lines = lines + 1 + table.insert(keys, "A: " .. ext.a) + end - -- Drawing - for i=lines, 1, -1 do - gfx.text(1, gfx.BOTTOM_HEIGHT - 15*i, keys[i], 12) - end - gfx.text(1, 30, ext.name, 12, 0xFF727272) - gfx.text(1, 45, tostring(selFile.fileSize) .. "B", 12, 0xFF727272) - else - gfx.text(1, 30, "File", 12, 0xFF727272) - gfx.text(1, 45, tostring(selFile.fileSize) .. "B", 12, 0xFF727272) - gfx.text(1, gfx.BOTTOM_HEIGHT - 15, keys[1], 12) - end - gfx.stop() + -- Drawing + for i=lines, 1, -1 do + gfx.text(1, gfx.BOTTOM_HEIGHT - 15*i, keys[i], 12) + end + gfx.text(1, 30, ext.name, 12, 0xFF727272) + gfx.text(1, 45, tostring(selFile.fileSize) .. "B", 12, 0xFF727272) + else + gfx.text(1, 30, "File", 12, 0xFF727272) + gfx.text(1, 45, tostring(selFile.fileSize) .. "B", 12, 0xFF727272) + gfx.text(1, gfx.BOTTOM_HEIGHT - 15, keys[1], 12) + end + gfx.stop() end local function drawTop(files, sel, scr) - gfx.start(gfx.TOP) - gfx.rectangle(0, (sel-scr-1)*15, gfx.TOP_WIDTH, 16, 0, 0xFF0000B3) - local over = #files - scr >= 16 and 16 or #files - scr - for i=scr+1, scr+over do - local color = files[i].isDirectory and 0xFF727272 or 0xFFFDFDFD - gfx.text(1, (i-scr-1)*15+1, files[i].name or "", 12, color) - end - gfx.stop() + gfx.start(gfx.TOP) + gfx.rectangle(0, (sel-scr-1)*15, gfx.TOP_WIDTH, 16, 0, 0xFF0000B3) + local over = #files - scr >= 16 and 16 or #files - scr + for i=scr+1, scr+over do + local color = files[i].isDirectory and 0xFF727272 or 0xFFFDFDFD + gfx.text(1, (i-scr-1)*15+1, files[i].name or "", 12, color) + end + gfx.stop() end local function runA(cur, selFile, bindings) - if not selFile.isDirectory then - local ext = getExtension(selFile.name, bindings) - if not ext then return end - if ext.a then return cur .. selFile.name, ext.ext end - end + if not selFile.isDirectory then + local ext = getExtension(selFile.name, bindings) + if not ext then return end + if ext.a then return cur .. selFile.name, ext.ext end + end end local function runY(cur, selFile, bindings) - if not selFile.isDirectory then - local ext = getExtension(selFile.name, bindings) - if not ext then return end - if ext.y then return cur .. selFile.name, ext.ext end - end + if not selFile.isDirectory then + local ext = getExtension(selFile.name, bindings) + if not ext then return end + if ext.y then return cur .. selFile.name, ext.ext end + end end --- Open a file browser to allow the user to select a file. @@ -131,69 +131,69 @@ end -- @returns The extension of the file, this might be helpful in cases were multiple file types could be expected, nil in case no file was picked. -- @returns The "mode", which indicates which key was used to select the file, "A" or "Y". "X" in case no file was picked. return function(bindings, workdir) - -- Initialization - local old = saveGraphicsState() - local cur = workdir or ctr.fs.getDirectory() - if cur:sub(-1) ~= "/" then - cur = cur .. "/" - end - local bindings = bindings or {} + -- Initialization + local old = saveGraphicsState() + local cur = workdir or ctr.fs.getDirectory() + if cur:sub(-1) ~= "/" then + cur = cur .. "/" + end + local bindings = bindings or {} - local files = getFilelist(cur) or {{name = "- Empty -"}} - local sel = 1 - local scr = 0 + local files = getFilelist(cur) or {{name = "- Empty -"}} + local sel = 1 + local scr = 0 - while ctr.run() do - drawBottom(cur, files[sel], bindings) - drawTop(files, sel, scr) - gfx.render() + while ctr.run() do + drawBottom(cur, files[sel], bindings) + drawTop(files, sel, scr) + gfx.render() - ctr.hid.read() - local state = ctr.hid.keys() - if (state.down.dDown or state.down.cpadDown) and sel < #files then - sel = sel + 1 - if sel - scr >= 16 then - scr = scr + 1 - end - elseif (state.down.dUp or state.down.cpadUp) and sel > 1 then - sel = sel - 1 - if sel == scr then - scr = scr - 1 - end - elseif state.down.dLeft or state.down.cpadLeft then - sel = 1 - scr = 0 - elseif state.down.dRight or state.down.cpadRight then - sel = #files - if #files > 15 then - scr = #files - 16 - end + ctr.hid.read() + local state = ctr.hid.keys() + if (state.down.dDown or state.down.cpadDown) and sel < #files then + sel = sel + 1 + if sel - scr >= 16 then + scr = scr + 1 + end + elseif (state.down.dUp or state.down.cpadUp) and sel > 1 then + sel = sel - 1 + if sel == scr then + scr = scr - 1 + end + elseif state.down.dLeft or state.down.cpadLeft then + sel = 1 + scr = 0 + elseif state.down.dRight or state.down.cpadRight then + sel = #files + if #files > 15 then + scr = #files - 16 + end - elseif state.down.a then - local selFile = files[sel] - if selFile.isDirectory then - if selFile.name == ".." then - cur = cur:gsub("[^/]+/$", "") - else - cur = cur .. selFile.name .. "/" - end - files, sel, scr = getFilelist(cur), 1, 0 - else - local file, ext = runA(cur, selFile, bindings) - if file then - restoreGraphicsState(old) - return file, ext, "A" - end - end - elseif state.down.y then - local file, ext = runY(cur, files[sel], bindings) - if file then - restoreGraphicsState(old) - return file, ext, "Y" - end - elseif state.down.x then - restoreGraphicsState(old) - return nil, nil, "X" - end - end + elseif state.down.a then + local selFile = files[sel] + if selFile.isDirectory then + if selFile.name == ".." then + cur = cur:gsub("[^/]+/$", "") + else + cur = cur .. selFile.name .. "/" + end + files, sel, scr = getFilelist(cur), 1, 0 + else + local file, ext = runA(cur, selFile, bindings) + if file then + restoreGraphicsState(old) + return file, ext, "A" + end + end + elseif state.down.y then + local file, ext = runY(cur, files[sel], bindings) + if file then + restoreGraphicsState(old) + return file, ext, "Y" + end + elseif state.down.x then + restoreGraphicsState(old) + return nil, nil, "X" + end + end end \ No newline at end of file diff --git a/sdcard/3ds/ctruLua/main.lua b/sdcard/3ds/ctruLua/main.lua index 196a8c9..16b214c 100644 --- a/sdcard/3ds/ctruLua/main.lua +++ b/sdcard/3ds/ctruLua/main.lua @@ -1,4 +1,4 @@ -local ctr = require("ctr") +²local ctr = require("ctr") local fs = require("ctr.fs") local gfx = require("ctr.gfx") @@ -14,39 +14,39 @@ package.path = package.path..";".. ldir.."?.lua;".. ldir.."?/init.lua" -- Erroring local function displayError(err) - gfx.color.setBackground(0xFF0000B3) - gfx.color.setDefault(0xFFFDFDFD) - gfx.font.setDefault(gfx.font.load(ctruLua.root .. "resources/VeraMono.ttf")) + gfx.color.setBackground(0xFF0000B3) + gfx.color.setDefault(0xFFFDFDFD) + gfx.font.setDefault(gfx.font.load(ctruLua.root .. "resources/VeraMono.ttf")) - while ctr.run() do - gfx.start(gfx.TOP) - gfx.text(1, 1, "An error has occured.", 12) - gfx.wrappedText(1, 30, err, gfx.TOP_WIDTH-2, 12) - gfx.text(1, gfx.TOP_HEIGHT-15, "Press Start to continue.", 12) - gfx.stop() - gfx.start(gfx.BOTTOM) - gfx.stop() + while ctr.run() do + gfx.start(gfx.TOP) + gfx.text(1, 1, "An error has occured.", 12) + gfx.wrappedText(1, 30, err, gfx.TOP_WIDTH-2, 12) + gfx.text(1, gfx.TOP_HEIGHT-15, "Press Start to continue.", 12) + gfx.stop() + gfx.start(gfx.BOTTOM) + gfx.stop() - gfx.render() - ctr.hid.read() - if ctr.hid.keys().down.start then break end - end + gfx.render() + ctr.hid.read() + if ctr.hid.keys().down.start then break end + end end -- Main loop while ctr.run() do - gfx.set3D(false) - gfx.font.setDefault() - gfx.color.setDefault(0xFFFDFDFD) - gfx.color.setBackground(0xFF333333) - local file, ext, mode = require("filepicker")({{name="Lua Script", ext=".lua", a="Execute"}}) - if file and mode == "A" then - fs.setDirectory(file:match("^(.-)[^/]*$")) - local ok, err = pcall(dofile, file) - if not ok then displayError(err) end - else - break - end + gfx.set3D(false) + gfx.font.setDefault() + gfx.color.setDefault(0xFFFDFDFD) + gfx.color.setBackground(0xFF333333) + local file, ext, mode = require("filepicker")({{name="Lua Script", ext=".lua", a="Execute"}}) + if file and mode == "A" then + fs.setDirectory(file:match("^(.-)[^/]*$")) + local ok, err = pcall(dofile, file) + if not ok then displayError(err) end + else + break + end end error("Main process has exited.\nPlease reboot.\nPressing Start does not work yet.") \ No newline at end of file From cbfc7bfaca9fd349b84d839aa0f936ba2a261aa2 Mon Sep 17 00:00:00 2001 From: Neil Zeke Cecchini Date: Wed, 24 Feb 2016 18:01:37 +0100 Subject: [PATCH 060/101] Oopsie. --- sdcard/3ds/ctruLua/main.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdcard/3ds/ctruLua/main.lua b/sdcard/3ds/ctruLua/main.lua index 16b214c..53bd3c1 100644 --- a/sdcard/3ds/ctruLua/main.lua +++ b/sdcard/3ds/ctruLua/main.lua @@ -1,4 +1,4 @@ -²local ctr = require("ctr") +local ctr = require("ctr") local fs = require("ctr.fs") local gfx = require("ctr.gfx") From 60b304b2d31ade4ce5c90e28e83f00375ee1f4ea Mon Sep 17 00:00:00 2001 From: Neil Zeke Cecchini Date: Wed, 24 Feb 2016 19:00:47 +0100 Subject: [PATCH 061/101] Forgot the font --- sdcard/3ds/ctruLua/main.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/sdcard/3ds/ctruLua/main.lua b/sdcard/3ds/ctruLua/main.lua index 53bd3c1..705397a 100644 --- a/sdcard/3ds/ctruLua/main.lua +++ b/sdcard/3ds/ctruLua/main.lua @@ -14,6 +14,7 @@ package.path = package.path..";".. ldir.."?.lua;".. ldir.."?/init.lua" -- Erroring local function displayError(err) + gfx.set3D(false) gfx.color.setBackground(0xFF0000B3) gfx.color.setDefault(0xFFFDFDFD) gfx.font.setDefault(gfx.font.load(ctruLua.root .. "resources/VeraMono.ttf")) From 34d12eae0f73370550e2a218a7e641b54121e2fa Mon Sep 17 00:00:00 2001 From: Neil Zeke Cecchini Date: Wed, 24 Feb 2016 19:04:17 +0100 Subject: [PATCH 062/101] Added VeraMono.ttf, for real this time --- sdcard/3ds/ctruLua/resources/VeraMono.ttf | Bin 0 -> 49224 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 sdcard/3ds/ctruLua/resources/VeraMono.ttf diff --git a/sdcard/3ds/ctruLua/resources/VeraMono.ttf b/sdcard/3ds/ctruLua/resources/VeraMono.ttf new file mode 100644 index 0000000000000000000000000000000000000000..139f0b4311ad2e0369a347b3be6c46e6c2b730d5 GIT binary patch literal 49224 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 literal 0 HcmV?d00001 From e84ab0e3b2ca041d29dbfde6766bcb81a885a9a6 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Wed, 24 Feb 2016 21:48:28 +0100 Subject: [PATCH 063/101] Added functions to set/get the default text size, closes #4 --- source/gfx.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/source/gfx.c b/source/gfx.c index 18a110b..a7e1345 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -18,6 +18,7 @@ The `gfx` module. bool isGfxInitialized = false; bool is3DEnabled = false; //TODO: add a function for this in the ctrulib/sf2dlib. +u32 textSize = 9; /*** The `ctr.gfx.color` module. @@ -255,7 +256,7 @@ Draw a text on the current screen. @tparam integer x text drawing origin horizontal coordinate, in pixels @tparam integer y text drawing origin vertical coordinate, in pixels @tparam string text the text to draw -@tparam[opt=9] integer size drawing size, in pixels +@tparam[opt=default size] integer size drawing size, in pixels @tparam[opt=default color] integer color drawing color @tparam[opt=default font] font font to use */ @@ -265,7 +266,7 @@ static int gfx_text(lua_State *L) { size_t len; const char *text = luaL_checklstring(L, 3, &len); - int size = luaL_optinteger(L, 4, 9); + int size = luaL_optinteger(L, 4, textSize); u32 color = luaL_optinteger(L, 5, color_default); font_userdata *font = luaL_testudata(L, 6, "LFont"); if (font == NULL) { @@ -293,7 +294,7 @@ Warning: No UTF32 support. @tparam integer y text drawing origin vertical coordinate, in pixels @tparam string text the text to draw @tparam integer width width of a line, in pixels -@tparam[opt=9] integer size drawing size, in pixels +@tparam[opt=default Size] integer size drawing size, in pixels @tparam[opt=default color] integer color drawing color @tparam[opt=default font] font font to use */ @@ -304,7 +305,7 @@ static int gfx_wrappedText(lua_State *L) { const char *text = luaL_checklstring(L, 3, &len); unsigned int lineWidth = luaL_checkinteger(L, 4); - int size = luaL_optinteger(L, 5, 9); + int size = luaL_optinteger(L, 5, textSize); u32 color = luaL_optinteger(L, 6, color_default); font_userdata *font = luaL_testudata(L, 7, "LFont"); if (font == NULL) { @@ -330,7 +331,7 @@ Calculate the size of a text draw with `wrappedText`. @function calcBoundingBox @tparam string text The text to check @tparam integer lineWidth width of a line, in pixels -@tparam[opt=9] integer size drawing size, in pixels +@tparam[opt=default size] integer size drawing size, in pixels @tparam[opt=default font] font font to use @treturn integer width of the text, in pixels @treturn integer height of the text, in pixels @@ -339,7 +340,7 @@ static int gfx_calcBoundingBox(lua_State *L) { size_t len; const char *text = luaL_checklstring(L, 1, &len); unsigned int lineWidth = luaL_checkinteger(L, 2); - int size = luaL_optinteger(L, 3, 9); + int size = luaL_optinteger(L, 3, textSize); font_userdata *font = luaL_testudata(L, 4, "LFont"); if (font == NULL) { lua_getfield(L, LUA_REGISTRYINDEX, "LFontDefault"); @@ -356,6 +357,26 @@ static int gfx_calcBoundingBox(lua_State *L) { return 2; } +/*** +Set the default text size. +@function setTextSize +@tparam number size new default text size +*/ +static int gfx_setTextSize(lua_State *L) { + textSize = luaL_checkinteger(L, 1); + return 0; +} + +/*** +Return the default text size. +@function getTextSize +@treturn number the default text size +*/ +static int gfx_getTextSize(lua_State *L) { + lua_pushinteger(L, textSize); + return 1; +} + // Functions static const struct luaL_Reg gfx_lib[] = { { "start", gfx_start }, @@ -374,6 +395,8 @@ static const struct luaL_Reg gfx_lib[] = { { "text", gfx_text }, { "wrappedText", gfx_wrappedText }, { "calcBoundingBox", gfx_calcBoundingBox }, + { "setTextSize", gfx_setTextSize }, + { "getTextSize", gfx_getTextSize }, { NULL, NULL } }; From 34c48f360e8d3413caed829727fc3bbc36448744 Mon Sep 17 00:00:00 2001 From: Reuh Date: Mon, 29 Feb 2016 19:42:52 +0100 Subject: [PATCH 064/101] Added a way to build the documentation as .sublime-completions files (make build-doc-st) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also replaced make build-doc with make build-doc-html so we can easily add new documentation formats in the future. To add ctrµLua API autocompletion to Sublime Text, simply copy the output directory (doc/sublimetext) to your ST's package directory. --- .gitignore | 3 +- Makefile | 18 ++++++++ doc/ldoc.ltp | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 doc/ldoc.ltp diff --git a/.gitignore b/.gitignore index 9208658..59e812b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /build/* /ctruLua.* -/doc/html/* \ No newline at end of file +/doc/html/* +/doc/sublimetext/* diff --git a/Makefile b/Makefile index 671e455..fede3ea 100644 --- a/Makefile +++ b/Makefile @@ -166,8 +166,17 @@ build-all: @make build build-doc: + @echo Building HTML documentation... + @make build-doc-html + @echo Building SublimeText documentation... + @make build-doc-st + +build-doc-html: @cd doc/ && ldoc . && cd .. +build-doc-st: + @cd doc/ && ldoc . --template ./ --ext sublime-completions --dir ./sublimetext/ && cd .. + #--------------------------------------------------------------------------------- clean: @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf @@ -197,8 +206,17 @@ clean-all: @make clean clean-doc: + @echo Cleaning HTML documentation... + @make clean-doc-html + @echo Cleaning SublimeText documentation... + @make clean-doc-st + +clean-doc-html: @rm -rf doc/html +clean-doc-st: + @rm -rf doc/sublimetext + #--------------------------------------------------------------------------------- else diff --git a/doc/ldoc.ltp b/doc/ldoc.ltp new file mode 100644 index 0000000..600da3e --- /dev/null +++ b/doc/ldoc.ltp @@ -0,0 +1,118 @@ +# -- LDoc template by Reuh. +# -- Generates sublime-completions files which can be used in Sublime Text 3 for autocompletion. +# -- Based on the HTML template, so generated files will contain lots of comments with additionnal data not handled by ST. +# -- I tried to make the generated files human-readable, so they may be used instead of the HTML documentation. +# -- Typical usage: ldoc . --template ./ --ext sublime-completions --dir ./sublimetext/ +# +# local scope = "source.lua" +# local function e(str) return str:gsub("\"", "\\\"") end -- escape json string ("str") +# local function indent(indentation, str) -- indent str (except first line) with indentation +# return str:gsub("(.-)\n", indentation.."%1\n"):gsub("\n([^\n]*)$", "\n"..indentation.."%1"):gsub("^"..indentation, "") +# end +# local function displayName(item) return item.type == "function" and item.name..item.args or item.name end -- nice name +# local function autocompleteName(item) -- ST-autocomplete name +# if item.type == "function" then +# local i = 1 +# local args = "(" +# for arg in (item.args:match("^%((.*)%)$")..","):gmatch("%s*([^,]+)%,") do +# args = args.."${"..i..":"..arg.."}, " +# i = i +1 +# end +# return item.name..args:gsub("%, $", "")..")" +# else return item.name end +# end +/* +Title: $(ldoc.title) +Project: $(ldoc.project) +Description: $(ldoc.description) +# if ldoc.single then +(Single module-project) +# end +# if not module then + +Project contents: +# for kind, mods in ldoc.kinds() do + $(kind) +# for m in mods() do + $(m.name): $(m.summary) +# end +# end +*/ +# else -- if not module +*/ + +/* +Module: $(module.name) +Summary: $(module.summary) +Description: $(module.description) + +Module contents: +# for kind, items in module.kinds() do + $(kind) +# for item in items() do + $(item.type) $(displayName(item)) +# end +# end +*/ + +/* Completions */ +{ + "scope": "$(e(scope))", + + "completions": [ +# for kind, items in module.kinds() do + /* $(kind) */ +# for item in items() do + /* + $(item.type) $(displayName(item)) + Summary: $(item.summary) + Description: $(indent("\t\t\t", item.description)) +# if item.type == "function" then + Parameters: +# for p in item.params:iter() do +# local default = item:default_of_param(p) +# if default == true then default = "(optional)" +# elseif default then default = "(defaults to "..default..")" end + ($(item:type_of_param(p))) $(item:display_name_of(p)):$(item.params.map[p]) $(default) +# end +# local retgroups = item.retgroups or {} + Returns: +# for i, group in ldoc.ipairs(retgroups) do +# for ret in group:iter() do + ($(ret.type)) $(indent("\t\t\t", ret.text)) +# end +# if i < #retgroups then + ---or--- +# end +# end +# end +# if item.usage then + Usage: +# for i, usage in ldoc.ipairs(item.usage) do + $(sep)$(indent("\t\t\t", usage:gsub("^\n", ""))) +# if i < #item.usage then + -------- +# end +# end +# end +# if item.see then + See also: +# for see in item.see:iter() do + $(see.mod.name): $(see.name) +# end +# end + */ +# for pos in (module.name.."."):gmatch("()[^.]+%.") do +# local prefix = e(module.name:sub(pos) .. (item.name:match("^[%.%:]") and "" or ".")) + { + "trigger": "$(prefix)$(e(item.name))\t$(e(item.summary))", + "contents": "$(prefix)$(e(autocompleteName(item)))" + }, +# end +# end +# end + ] +} +# end -- if not module + +/* Generated by LDoc; sublime-completions template by Reuh. Last updated $(ldoc.updatetime).*/ From acd41db80546404e004c192aa42c7e8771eeb025 Mon Sep 17 00:00:00 2001 From: Reuh Date: Wed, 9 Mar 2016 13:30:15 +0100 Subject: [PATCH 065/101] Update README.md links --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6232241..c8e96c4 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@ Everything is in the Wiki. Warning: the 'u' in the repo's name is a 'µ', not a 'u'. -#### Builds ![build status](http://thomas99.no-ip.org:3000/ctrulua.png) +#### Builds ![build status](http://ci.reuh.tk/ctrulua.png) -* Most recent working build: [here](http://thomas99.no-ip.org:3000/ctrulua/builds/latest/artifacts/ctruLua.3dsx) (warning: only tested with Citra, sometimes on real hardware). -* See http://thomas99.no-ip.org:3000/ctrulua for all the builds. +* Most recent working build: [here](http://ci.reuh.tk/ctrulua/builds/latest/artifacts/ctruLua.3dsx) (warning: only tested with Citra, sometimes on real hardware). +* See http://ci.reuh.tk/ctrulua for all the builds. #### Build instructions @@ -19,7 +19,7 @@ May not work under Windows. #### Lua API Documentation -* An online version of the documentation can be found here : http://thomas99.no-ip.org/ctrulua +* An online version of the documentation can be found here : http://reuh.tk/ctrulua * To build the documentation, run `make build-doc` (requires [LDoc](https://github.com/stevedonovan/LDoc)). #### Based on ctrulib by smealum: [https://github.com/smealum/ctrulib](https://github.com/smealum/ctrulib) From 9db21c7831b421105ddcb9bc6bbf8e094f058ed1 Mon Sep 17 00:00:00 2001 From: Reuh Date: Wed, 9 Mar 2016 16:13:06 +0100 Subject: [PATCH 066/101] Added gfx.scissor Note: doesn't work on citra but it does on real hardware. --- source/gfx.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/source/gfx.c b/source/gfx.c index a7e1345..9ecd1a6 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -377,6 +377,33 @@ static int gfx_getTextSize(lua_State *L) { return 1; } +/*** +Enables or disable the scissor test. +When the scissor test is enabled, the drawing area will be limited to a specific rectangle, every pixel drawn outside will be discarded. +Calls this function without argument to disable the scissor test. +@function scissor +@tparam integer x scissor rectangle origin horizontal coordinate, in pixels +@tparam integer y scissor rectangle origin vertical coordinate, in pixels +@tparam integer width scissor rectangle width, in pixels +@tparam integer height scissor rectangle height, in pixels +@tparam[opt=false] boolean invert if true the scissor will be inverted (will draw only outside of the rectangle) +*/ +static int gfx_scissor(lua_State *L) { + if (lua_gettop(L) == 0) { + sf2d_set_scissor_test(GPU_SCISSOR_DISABLE, 0, 0, 0, 0); + } else { + int x = luaL_checkinteger(L, 1); + int y = luaL_checkinteger(L, 2); + int width = luaL_checkinteger(L, 3); + int height = luaL_checkinteger(L, 4); + bool invert = lua_toboolean(L, 5); + + sf2d_set_scissor_test(invert ? GPU_SCISSOR_INVERT : GPU_SCISSOR_NORMAL, x, y, width, height); + } + + return 0; +} + // Functions static const struct luaL_Reg gfx_lib[] = { { "start", gfx_start }, @@ -397,6 +424,7 @@ static const struct luaL_Reg gfx_lib[] = { { "calcBoundingBox", gfx_calcBoundingBox }, { "setTextSize", gfx_setTextSize }, { "getTextSize", gfx_getTextSize }, + { "scissor", gfx_scissor }, { NULL, NULL } }; From 6a9fbbb13357ecd4296902532231e906c4e85c0c Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Sat, 12 Mar 2016 19:57:59 +0100 Subject: [PATCH 067/101] Added ":addTrustedRootCA()" to the httpc contexts, close #8, Added some values in ctr. I didn't test :addTrustedRootCA(), but it's just a simple string-to-char+size function. --- Makefile | 4 +++- sdcard/3ds/ctruLua/examples/httpc/httpc.lua | 9 ++++----- source/ctr.c | 13 +++++++++++++ source/gfx.c | 2 +- source/httpc.c | 16 ++++++++++++++++ source/ir.c | 2 +- 6 files changed, 38 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index fede3ea..a766a60 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,8 @@ APP_TITLE := ctruLua APP_DESCRIPTION := Lua for the 3DS. Yes, it works. APP_AUTHOR := Reuh, Firew0lf and NegiAD ICON := icon.png +APP_VERSION := $(shell git describe --abbrev=0 --tags) +LASTCOMMIT := $(shell git rev-parse HEAD) #--------------------------------------------------------------------------------- # options for code generation @@ -48,7 +50,7 @@ CFLAGS := -g -Wall -O2 -mword-relocations -std=gnu11 \ -fomit-frame-pointer -ffast-math \ $(ARCH) -CFLAGS += $(INCLUDE) -DARM11 -D_3DS +CFLAGS += $(INCLUDE) -DARM11 -D_3DS -DCTR_VERSION=\"$(APP_VERSION)\" -DCTR_BUILD=\"$(LASTCOMMIT)\" CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 diff --git a/sdcard/3ds/ctruLua/examples/httpc/httpc.lua b/sdcard/3ds/ctruLua/examples/httpc/httpc.lua index 7ff1ef5..2f45f22 100644 --- a/sdcard/3ds/ctruLua/examples/httpc/httpc.lua +++ b/sdcard/3ds/ctruLua/examples/httpc/httpc.lua @@ -25,19 +25,18 @@ while ctr.run() do dls = dls + 1 end - gfx.startFrame(gfx.TOP) + gfx.start(gfx.TOP) gfx.text(0, 0, data) gfx.text(0, 20, "Downloaded "..dls.." times.") - gfx.endFrame() + gfx.stop() - gfx.startFrame(gfx.BOTTOM) + gfx.start(gfx.BOTTOM) gfx.text(2, 2, "HTTP Contexts example") gfx.text(2, 20, "The data is downloaded from '"..addr.."'.") gfx.text(2, 30, "Press [B] to redownload.") - gfx.endFrame() + gfx.stop() gfx.render() end - context:close() diff --git a/source/ctr.c b/source/ctr.c index 64f55e5..c7adad4 100644 --- a/source/ctr.c +++ b/source/ctr.c @@ -189,6 +189,19 @@ int luaopen_ctr_lib(lua_State *L) { ctr_libs[i].load(L); lua_setfield(L, -2, ctr_libs[i].name); } + + /*** + Running version of ctrµLua. This string contains the exact name of the last (pre-)release tag. + @field version + */ + lua_pushstring(L, CTR_VERSION); + lua_setfield(L, -2, "version"); + /*** + Running build of ctrµLua. This string contains the last commit hash. + @field build + */ + lua_pushstring(L, CTR_BUILD); + lua_setfield(L, -2, "build"); return 1; } diff --git a/source/gfx.c b/source/gfx.c index 9ecd1a6..f356eaa 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -8,7 +8,7 @@ The `gfx` module. #include #include -#include <3ds/vram.h> +//#include <3ds/vram.h> //#include <3ds/services/gsp.h> #include diff --git a/source/httpc.c b/source/httpc.c index 1386880..5f15af7 100644 --- a/source/httpc.c +++ b/source/httpc.c @@ -218,6 +218,21 @@ static int httpc_getResponseHeader(lua_State *L) { return 1; } +/*** +Add a trusted RootCA cert to a context. +@function :addTrustedRootCA +@tparam string DER certificate +*/ +static int httpc_addTrustedRootCA(lua_State *L) { + httpcContext *context = lua_touserdata(L, 1); + u32 certsize; + u8* cert = (u8*)luaL_checklstring(L, 2, (size_t*)&certsize); + + httpcAddTrustedRootCA(context, cert, certsize); + + return 0; +} + // object static const struct luaL_Reg httpc_methods[] = { {"open", httpc_open }, @@ -229,6 +244,7 @@ static const struct luaL_Reg httpc_methods[] = { {"close", httpc_close }, {"addPostData", httpc_addPostData }, {"getResponseHeader", httpc_getResponseHeader }, + {"addTrustedRootCA", httpc_addTrustedRootCA }, {NULL, NULL} }; diff --git a/source/ir.c b/source/ir.c index 5c5864e..86d85fe 100644 --- a/source/ir.c +++ b/source/ir.c @@ -5,7 +5,7 @@ The `ir` module. */ #include <3ds/types.h> #include <3ds/services/ir.h> -#include <3ds/linear.h> +//#include <3ds/linear.h> #include #include From e5467b663d13126870598b7bd3bd2bb3648339e1 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Sat, 12 Mar 2016 23:14:42 +0100 Subject: [PATCH 068/101] Added ctr.root, close #7, changed the max path length to 1024 ctr.root is not exactly the path to ctruLua.3dsx minus the file name, but is equivalent with HBL and $ (other launchers may work too, and it works perfectly with Citra), and is "romfs:/" if romfs is enabl$ --- source/ctr.c | 16 ++++++++++++++++ source/fs.c | 6 +++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/source/ctr.c b/source/ctr.c index c7adad4..db919dc 100644 --- a/source/ctr.c +++ b/source/ctr.c @@ -3,6 +3,9 @@ The `ctr` module. @module ctr @usage local ctr = require("ctr") */ +#include +#include + #include <3ds/types.h> #include <3ds/services/apt.h> #include <3ds/os.h> @@ -202,6 +205,19 @@ int luaopen_ctr_lib(lua_State *L) { */ lua_pushstring(L, CTR_BUILD); lua_setfield(L, -2, "build"); + + /*** + Root directory of ctrµLua. Contains the working directory where ctrµLua has been launched OR the romfs root if romfs has been enabled. + @field root + */ + #ifdef ROMFS + char* buff = "romfs:"; + #else + char* buff = malloc(1024); + getcwd(buff, 1024); + #endif + lua_pushstring(L, buff); + lua_setfield(L, -2, "root"); return 1; } diff --git a/source/fs.c b/source/fs.c index 68e13e3..b3e2235 100644 --- a/source/fs.c +++ b/source/fs.c @@ -40,7 +40,7 @@ const char* prefix_path(const char* path) { char* prefix = "sdmc:"; #endif - char out[256]; + char out[1024]; strcpy(out, prefix); return strcat(out, path); @@ -156,9 +156,9 @@ Get the current working directory. @treturn string the current working directory */ static int fs_getDirectory(lua_State *L) { - char cwd[256]; + char cwd[1024]; - lua_pushstring(L, getcwd(cwd, 256)); + lua_pushstring(L, getcwd(cwd, 1024)); return 1; } From 694159f444e5bbf5ec46b7dba1b9d07d7e67170f Mon Sep 17 00:00:00 2001 From: Neil Zeke Cecchini Date: Sun, 13 Mar 2016 21:30:14 +0100 Subject: [PATCH 069/101] Updated LSH to use the new constants Doesn't change much anyway. --- sdcard/3ds/ctruLua/libs/filepicker.lua | 2 +- sdcard/3ds/ctruLua/main.lua | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/sdcard/3ds/ctruLua/libs/filepicker.lua b/sdcard/3ds/ctruLua/libs/filepicker.lua index 7a017cf..e2b954a 100644 --- a/sdcard/3ds/ctruLua/libs/filepicker.lua +++ b/sdcard/3ds/ctruLua/libs/filepicker.lua @@ -8,7 +8,7 @@ local function saveGraphicsState() local old = {gfx.get3D(), gfx.color.getDefault(), gfx.color.getBackground(), gfx.font.getDefault()} - local mono = gfx.font.load(ctruLua.root .. "resources/VeraMono.ttf") + local mono = gfx.font.load(ctr.root .. "resources/VeraMono.ttf") gfx.set3D(false) gfx.color.setDefault(0xFFFDFDFD) diff --git a/sdcard/3ds/ctruLua/main.lua b/sdcard/3ds/ctruLua/main.lua index 705397a..44c840e 100644 --- a/sdcard/3ds/ctruLua/main.lua +++ b/sdcard/3ds/ctruLua/main.lua @@ -2,14 +2,8 @@ local ctr = require("ctr") local fs = require("ctr.fs") local gfx = require("ctr.gfx") --- Initializing "constants" -ctruLua = {} - ---- The ctruLua root directory's absolute path -ctruLua.root = fs.getDirectory() - -- Set up path -local ldir = fs.getDirectory().."libs/" +local ldir = ctr.root.."libs/" package.path = package.path..";".. ldir.."?.lua;".. ldir.."?/init.lua" -- Erroring @@ -17,7 +11,7 @@ local function displayError(err) gfx.set3D(false) gfx.color.setBackground(0xFF0000B3) gfx.color.setDefault(0xFFFDFDFD) - gfx.font.setDefault(gfx.font.load(ctruLua.root .. "resources/VeraMono.ttf")) + gfx.font.setDefault(gfx.font.load(ctr.root .. "resources/VeraMono.ttf")) while ctr.run() do gfx.start(gfx.TOP) From 5107f0277ce7754eaa97ae9d223704b9e177aebe Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Tue, 22 Mar 2016 21:48:52 +0100 Subject: [PATCH 070/101] Updated sf2dlib, added a "thickness" argument to lines (warning: break some old lines), Added WIP render targets, Added arguments (main file and root path) You'll need to update some of your codes if you used lines with colors, or you'll have some ... Nice line effects. --- libs/sf2dlib/.gitignore | 9 +- libs/sf2dlib/libsf2d/Makefile | 3 + libs/sf2dlib/libsf2d/include/sf2d.h | 67 ++++++++- libs/sf2dlib/libsf2d/source/sf2d.c | 92 ++++++++++--- libs/sf2dlib/libsf2d/source/sf2d_draw.c | 31 ++++- libs/sf2dlib/libsf2d/source/sf2d_private.c | 4 + libs/sf2dlib/libsf2d/source/sf2d_texture.c | 110 +++++++++++++++ sdcard/3ds/ctruLua/examples/example.lua | 4 +- source/ctr.c | 2 +- source/gfx.c | 149 ++++++++++++++++++++- source/httpc.c | 11 +- source/main.c | 36 ++++- 12 files changed, 477 insertions(+), 41 deletions(-) diff --git a/libs/sf2dlib/.gitignore b/libs/sf2dlib/.gitignore index e1071b6..a020760 100644 --- a/libs/sf2dlib/.gitignore +++ b/libs/sf2dlib/.gitignore @@ -1,2 +1,7 @@ -libsf2d/build/ -libsf2d/lib/ \ No newline at end of file +*.d +*.o +*.project +*.cproject +libsf2d/.settings/* +libsf2d/build/* +libsf2d/lib/* diff --git a/libs/sf2dlib/libsf2d/Makefile b/libs/sf2dlib/libsf2d/Makefile index 14e1112..28ccec1 100644 --- a/libs/sf2dlib/libsf2d/Makefile +++ b/libs/sf2dlib/libsf2d/Makefile @@ -30,6 +30,7 @@ CFLAGS := -g -Wall -O2\ $(ARCH) CFLAGS += $(INCLUDE) -DARM11 -D_3DS +#CFLAGS += -std=c11 #WILL HAVE TO BE REMOVED SOON CFLAGS += -DLIBCTRU_NO_DEPRECATION @@ -138,6 +139,8 @@ $(OUTPUT) : $(OFILES) @echo "extern const u8" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(notdir $<).shbin | tr . _)`.h @echo "extern const u32" `(echo $(notdir $<).shbin | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(notdir $<).shbin | tr . _)`.h +sf2d.c: shader.vsh + -include $(DEPENDS) #--------------------------------------------------------------------------------------- diff --git a/libs/sf2dlib/libsf2d/include/sf2d.h b/libs/sf2dlib/libsf2d/include/sf2d.h index 6385b83..f282d4f 100644 --- a/libs/sf2dlib/libsf2d/include/sf2d.h +++ b/libs/sf2dlib/libsf2d/include/sf2d.h @@ -4,7 +4,6 @@ * @date 22 March 2015 * @brief sf2dlib header */ - #ifndef SF2D_H #define SF2D_H @@ -136,6 +135,11 @@ typedef struct { void *data; /**< Pointer to the data */ } sf2d_texture; +typedef struct { + sf2d_texture texture; // "inherit"/extend standard texture + float projection[4*4]; /**< Orthographic projection matrix for this target */ +} sf2d_rendertarget; + // Basic functions /** @@ -171,6 +175,12 @@ void sf2d_set_3D(int enable); */ void sf2d_start_frame(gfxScreen_t screen, gfx3dSide_t side); +/** + * @brief Starts a frame bound to a rendertarget + * @param target rendertarget to draw to + */ +void sf2d_start_frame_target(sf2d_rendertarget *target); + /** * @brief Ends a frame, should be called on pair with sf2d_start_frame */ @@ -239,9 +249,10 @@ void sf2d_set_clear_color(u32 color); * @param y0 y coordinate of the first dot * @param x1 x coordinate of the second dot * @param y1 y coordinate of the sceond dot + * @param width thickness of the line * @param color the color to draw the line */ -void sf2d_draw_line(int x0, int y0, int x1, int y1, u32 color); + void sf2d_draw_line(float x0, float y0, float x1, float y1, float width, u32 color); /** * @brief Draws a rectangle @@ -292,12 +303,37 @@ void sf2d_draw_fill_circle(int x, int y, int radius, u32 color); */ sf2d_texture *sf2d_create_texture(int width, int height, sf2d_texfmt pixel_format, sf2d_place place); +/** + * @brief Creates an empty rendertarget. + * Functions similarly to sf2d_create_texture. + * @param width the width of the texture + * @param height the height of the texture + * @return a pointer to the newly created rendertarget + * @note Before drawing the texture, it needs to be tiled + * by calling sf2d_texture_tile32. + * The default texture params are both min and mag filters + * GPU_NEAREST, and both S and T wrappings GPU_CLAMP_TO_BORDER. + */ +sf2d_rendertarget *sf2d_create_rendertarget(int width, int height); + /** * @brief Frees a texture * @param texture pointer to the texture to freeze */ void sf2d_free_texture(sf2d_texture *texture); +/** + * @brief Frees a rendertarget + * @param target pointer to the rendertarget to free + */ +void sf2d_free_target(sf2d_rendertarget *target); + +/** + * @brief Clears a rendertarget to the specified color + * @param target pointer to the rendertarget to clear + */ +void sf2d_clear_target(sf2d_rendertarget *target, u32 color); + /** * @brief Fills an already allocated texture from a RGBA8 source * @param dst pointer to the destination texture to fill @@ -419,6 +455,33 @@ void sf2d_draw_texture_rotate(const sf2d_texture *texture, int x, int y, float r */ void sf2d_draw_texture_rotate_blend(const sf2d_texture *texture, int x, int y, float rad, u32 color); +/** + * @brief Draws a scaled texture with rotation around its hotspot + * @param texture the texture to draw + * @param x the x coordinate to draw the texture to + * @param y the y coordinate to draw the texture to + * @param rad rotation (in radians) to draw the texture + * @param x_scale the x scale + * @param y_scale the y scale + * @param center_x the x position of the hotspot + * @param center_y the y position of the hotspot + */ +void sf2d_draw_texture_rotate_scale_hotspot(const sf2d_texture *texture, int x, int y, float rad, float scale_x, float scale_y, float center_x, float center_y); + +/** + * @brief Draws a scaled texture with rotation around its hotspot with color + * @param texture the texture to draw + * @param x the x coordinate to draw the texture to + * @param y the y coordinate to draw the texture to + * @param rad rotation (in radians) to draw the texture + * @param x_scale the x scale + * @param y_scale the y scale + * @param center_x the x position of the hotspot + * @param center_y the y position of the hotspot + * @param color the color to blend with the texture + */ +void sf2d_draw_texture_rotate_scale_hotspot_blend(const sf2d_texture *texture, int x, int y, float rad, float scale_x, float scale_y, float center_x, float center_y, u32 color); + /** * @brief Draws a part of a texture * @param texture the texture to draw diff --git a/libs/sf2dlib/libsf2d/source/sf2d.c b/libs/sf2dlib/libsf2d/source/sf2d.c index be9e4aa..0b76e62 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d.c +++ b/libs/sf2dlib/libsf2d/source/sf2d.c @@ -1,3 +1,4 @@ +#include #include "sf2d.h" #include "sf2d_private.h" #include "shader_vsh_shbin.h" @@ -32,6 +33,10 @@ static u32 projection_desc = -1; //Matrix static float ortho_matrix_top[4*4]; static float ortho_matrix_bot[4*4]; +//Rendertarget things +static sf2d_rendertarget * currentRenderTarget = NULL; +static void * targetDepthBuffer; +static int targetDepthBufferLen = 0; //Apt hook cookie static aptHookCookie apt_hook_cookie; //Functions @@ -111,6 +116,7 @@ int sf2d_fini() linearFree(gpu_cmd); vramFree(gpu_fb_addr); vramFree(gpu_depth_fb_addr); + linearFree(targetDepthBuffer); sf2d_initialized = 0; @@ -173,6 +179,53 @@ void sf2d_start_frame(gfxScreen_t screen, gfx3dSide_t side) GPU_SetDummyTexEnv(5); } +void sf2d_start_frame_target(sf2d_rendertarget *target) +{ + sf2d_pool_reset(); + GPUCMD_SetBufferOffset(0); + + // Upload saved uniform + matrix_gpu_set_uniform(target->projection, projection_desc); + + int bufferLen = target->texture.width * target->texture.height * 4; // apparently depth buffer is (or can be) 32bit? + if (bufferLen > targetDepthBufferLen) { // expand depth buffer + if (targetDepthBufferLen > 0) linearFree(targetDepthBuffer); + targetDepthBuffer = linearAlloc(bufferLen); + memset(targetDepthBuffer, 0, bufferLen); + targetDepthBufferLen = bufferLen; + } + + GPU_SetViewport((u32 *)osConvertVirtToPhys(targetDepthBuffer), + (u32 *)osConvertVirtToPhys(target->texture.data), + 0, 0, target->texture.height, target->texture.width); + + currentRenderTarget = target; + + GPU_DepthMap(-1.0f, 0.0f); + GPU_SetFaceCulling(GPU_CULL_NONE); + GPU_SetStencilTest(false, GPU_ALWAYS, 0x00, 0xFF, 0x00); + GPU_SetStencilOp(GPU_STENCIL_KEEP, GPU_STENCIL_KEEP, GPU_STENCIL_KEEP); + GPU_SetBlendingColor(0,0,0,0); + GPU_SetDepthTestAndWriteMask(true, GPU_GEQUAL, GPU_WRITE_ALL); + GPUCMD_AddMaskedWrite(GPUREG_EARLYDEPTH_TEST1, 0x1, 0); + GPUCMD_AddWrite(GPUREG_EARLYDEPTH_TEST2, 0); + + GPU_SetAlphaBlending( + GPU_BLEND_ADD, + GPU_BLEND_ADD, + GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, + GPU_ONE, GPU_ZERO + ); + + GPU_SetAlphaTest(false, GPU_ALWAYS, 0x00); + + GPU_SetDummyTexEnv(1); + GPU_SetDummyTexEnv(2); + GPU_SetDummyTexEnv(3); + GPU_SetDummyTexEnv(4); + GPU_SetDummyTexEnv(5); +} + void sf2d_end_frame() { GPU_FinishDrawing(); @@ -180,23 +233,30 @@ void sf2d_end_frame() GPUCMD_FlushAndRun(); gspWaitForP3D(); - //Copy the GPU rendered FB to the screen FB - if (cur_screen == GFX_TOP) { - GX_DisplayTransfer(gpu_fb_addr, GX_BUFFER_DIM(240, 400), - (u32 *)gfxGetFramebuffer(GFX_TOP, cur_side, NULL, NULL), - GX_BUFFER_DIM(240, 400), 0x1000); - } else { - GX_DisplayTransfer(gpu_fb_addr, GX_BUFFER_DIM(240, 320), - (u32 *)gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL), - GX_BUFFER_DIM(240, 320), 0x1000); - } - gspWaitForPPF(); + if (!currentRenderTarget) { + //Copy the GPU rendered FB to the screen FB + if (cur_screen == GFX_TOP) { + GX_DisplayTransfer(gpu_fb_addr, GX_BUFFER_DIM(240, 400), + (u32 *)gfxGetFramebuffer(GFX_TOP, cur_side, NULL, NULL), + GX_BUFFER_DIM(240, 400), 0x1000); + } else { + GX_DisplayTransfer(gpu_fb_addr, GX_BUFFER_DIM(240, 320), + (u32 *)gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL), + GX_BUFFER_DIM(240, 320), 0x1000); + } + gspWaitForPPF(); - //Clear the screen - GX_MemoryFill( - gpu_fb_addr, clear_color, &gpu_fb_addr[240*400], GX_FILL_TRIGGER | GX_FILL_32BIT_DEPTH, - gpu_depth_fb_addr, 0, &gpu_depth_fb_addr[240*400], GX_FILL_TRIGGER | GX_FILL_32BIT_DEPTH); - gspWaitForPSC0(); + //Clear the screen + GX_MemoryFill( + gpu_fb_addr, clear_color, &gpu_fb_addr[240*400], GX_FILL_TRIGGER | GX_FILL_32BIT_DEPTH, + gpu_depth_fb_addr, 0, &gpu_depth_fb_addr[240*400], GX_FILL_TRIGGER | GX_FILL_32BIT_DEPTH); + gspWaitForPSC0(); + } else { + //gspWaitForPPF(); + //gspWaitForPSC0(); + sf2d_texture_tile32(&(currentRenderTarget->texture)); + } + currentRenderTarget = NULL; } void sf2d_swapbuffers() diff --git a/libs/sf2dlib/libsf2d/source/sf2d_draw.c b/libs/sf2dlib/libsf2d/source/sf2d_draw.c index 28ef34b..59a792e 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d_draw.c +++ b/libs/sf2dlib/libsf2d/source/sf2d_draw.c @@ -2,15 +2,36 @@ #include "sf2d_private.h" #include -void sf2d_draw_line(int x0, int y0, int x1, int y1, u32 color) +#ifndef M_PI +#define M_PI (3.14159265358979323846) +#endif + +void sf2d_draw_line(float x0, float y0, float x1, float y1, float width, u32 color) { sf2d_vertex_pos_col *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_col), 8); if (!vertices) return; - vertices[0].position = (sf2d_vector_3f){(float)x0+1.0f, (float)y0+1.0f, SF2D_DEFAULT_DEPTH}; - vertices[1].position = (sf2d_vector_3f){(float)x0-1.0f, (float)y0-1.0f, SF2D_DEFAULT_DEPTH}; - vertices[2].position = (sf2d_vector_3f){(float)x1+1.0f, (float)y1+1.0f, SF2D_DEFAULT_DEPTH}; - vertices[3].position = (sf2d_vector_3f){(float)x1-1.0f, (float)y1-1.0f, SF2D_DEFAULT_DEPTH}; + float dx = x1 - x0; + float dy = y1 - y0; + + float nx = -dy; + float ny = dx; + + float len = sqrt(nx * nx + ny * ny); + + if (len > 0 ){ + nx /= len; + ny /= len; + } + + nx *= width*0.5f; + ny *= width*0.5f; + + vertices[0].position = (sf2d_vector_3f){x0+nx, y0+ny, SF2D_DEFAULT_DEPTH}; + vertices[1].position = (sf2d_vector_3f){x0-nx, y0-ny, SF2D_DEFAULT_DEPTH}; + + vertices[2].position = (sf2d_vector_3f){x1+nx, y1+ny, SF2D_DEFAULT_DEPTH}; + vertices[3].position = (sf2d_vector_3f){x1-nx, y1-ny, SF2D_DEFAULT_DEPTH}; vertices[0].color = color; vertices[1].color = vertices[0].color; diff --git a/libs/sf2dlib/libsf2d/source/sf2d_private.c b/libs/sf2dlib/libsf2d/source/sf2d_private.c index 073c437..30b0ed8 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d_private.c +++ b/libs/sf2dlib/libsf2d/source/sf2d_private.c @@ -2,6 +2,10 @@ #include #include "sf2d_private.h" +#ifndef M_PI +#define M_PI (3.14159265358979323846) +#endif + //stolen from staplebutt void GPU_SetDummyTexEnv(u8 num) { diff --git a/libs/sf2dlib/libsf2d/source/sf2d_texture.c b/libs/sf2dlib/libsf2d/source/sf2d_texture.c index df314a9..13345e9 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d_texture.c +++ b/libs/sf2dlib/libsf2d/source/sf2d_texture.c @@ -4,6 +4,10 @@ #include "sf2d.h" #include "sf2d_private.h" +#ifndef M_PI +#define M_PI (3.14159265358979323846) +#endif + #define TEX_MIN_SIZE 8 static unsigned int nibbles_per_pixel(sf2d_texfmt format) @@ -93,6 +97,22 @@ sf2d_texture *sf2d_create_texture(int width, int height, sf2d_texfmt pixel_forma return texture; } +sf2d_rendertarget *sf2d_create_rendertarget(int width, int height) +{ + sf2d_texture *tx = sf2d_create_texture(width, height, TEXFMT_RGBA8, SF2D_PLACE_RAM); + sf2d_rendertarget *rt = malloc(sizeof(*rt)); + //memcpy(rt, tx, sizeof(*tx)); + rt->texture = *tx; + free(tx); + //tx = * rt->texture; + //rt->projection + + matrix_init_orthographic(rt->projection, 0.0f, width, height, 0.0f, 0.0f, 1.0f); + matrix_rotate_z(rt->projection, M_PI / 2.0f); + + return rt; +} + void sf2d_free_texture(sf2d_texture *texture) { if (texture) { @@ -105,6 +125,27 @@ void sf2d_free_texture(sf2d_texture *texture) } } +void sf2d_free_target(sf2d_rendertarget *target) +{ + sf2d_free_texture(&(target->texture)); + //free(target); // unnecessary since the texture is the start of the target struct +} + +void sf2d_clear_target(sf2d_rendertarget *target, u32 color) { + if (color == 0) { // if fully transparent, take a shortcut + memset(target->texture.data, 0, target->texture.width * target->texture.height * 4); + sf2d_texture_tile32(&(target->texture)); + return; + } + + color = ((color>>24)&0x000000FF) | ((color>>8)&0x0000FF00) | ((color<<8)&0x00FF0000) | ((color<<24)&0xFF000000); // reverse byte order + + int itarget = target->texture.width * target->texture.height; + for (int i = 0; i < itarget; i++) { memcpy(target->texture.data + i*4, &color, 4); } + + sf2d_texture_tile32(&(target->texture)); +} + void sf2d_fill_texture_from_RGBA8(sf2d_texture *dst, const void *rgba8, int source_w, int source_h) { // TODO: add support for non-RGBA8 textures @@ -344,6 +385,75 @@ void sf2d_draw_texture_rotate_blend(const sf2d_texture *texture, int x, int y, f color); } +static inline void sf2d_draw_texture_rotate_scale_hotspot_generic(const sf2d_texture *texture, int x, int y, float rad, float scale_x, float scale_y, float center_x, float center_y) +{ + sf2d_vertex_pos_tex *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_tex), 8); + if (!vertices) return; + + const float w = texture->width; + const float h = texture->height; + + vertices[0].position.x = -center_x * scale_x; + vertices[0].position.y = -center_y * scale_y; + vertices[0].position.z = SF2D_DEFAULT_DEPTH; + + vertices[1].position.x = (w - center_x) * scale_x; + vertices[1].position.y = -center_y * scale_y; + vertices[1].position.z = SF2D_DEFAULT_DEPTH; + + vertices[2].position.x = -center_x * scale_x; + vertices[2].position.y = (h - center_y) * scale_y; + vertices[2].position.z = SF2D_DEFAULT_DEPTH; + + vertices[3].position.x = (w - center_x) * scale_x; + vertices[3].position.y = h - center_y * scale_y; + vertices[3].position.z = SF2D_DEFAULT_DEPTH; + + float u = w/(float)texture->pow2_w; + float v = h/(float)texture->pow2_h; + + vertices[0].texcoord = (sf2d_vector_2f){0.0f, 0.0f}; + vertices[1].texcoord = (sf2d_vector_2f){u, 0.0f}; + vertices[2].texcoord = (sf2d_vector_2f){0.0f, v}; + vertices[3].texcoord = (sf2d_vector_2f){u, v}; + + const float c = cosf(rad); + const float s = sinf(rad); + int i; + for (i = 0; i < 4; ++i) { // Rotate and translate + float _x = vertices[i].position.x; + float _y = vertices[i].position.y; + vertices[i].position.x = _x*c - _y*s + x; + vertices[i].position.y = _x*s + _y*c + y; + } + + GPU_SetAttributeBuffers( + 2, // number of attributes + (u32*)osConvertVirtToPhys(vertices), + GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 2, GPU_FLOAT), + 0xFFFC, //0b1100 + 0x10, + 1, //number of buffers + (u32[]){0x0}, // buffer offsets (placeholders) + (u64[]){0x10}, // attribute permutations for each buffer + (u8[]){2} // number of attributes for each buffer + ); + + GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); +} + +void sf2d_draw_texture_rotate_scale_hotspot(const sf2d_texture *texture, int x, int y, float rad, float scale_x, float scale_y, float center_x, float center_y) +{ + sf2d_bind_texture(texture, GPU_TEXUNIT0); + sf2d_draw_texture_rotate_scale_hotspot_generic(texture, x, y, rad, scale_x, scale_y, center_x, center_y); +} + +void sf2d_draw_texture_rotate_scale_hotspot_blend(const sf2d_texture *texture, int x, int y, float rad, float scale_x, float scale_y, float center_x, float center_y, u32 color) +{ + sf2d_bind_texture_color(texture, GPU_TEXUNIT0, color); + sf2d_draw_texture_rotate_scale_hotspot_generic(texture, x, y, rad, scale_x, scale_y, center_x, center_y); +} + static inline void sf2d_draw_texture_part_generic(const sf2d_texture *texture, int x, int y, int tex_x, int tex_y, int tex_w, int tex_h) { sf2d_vertex_pos_tex *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_tex), 8); diff --git a/sdcard/3ds/ctruLua/examples/example.lua b/sdcard/3ds/ctruLua/examples/example.lua index b85c187..1c2a307 100644 --- a/sdcard/3ds/ctruLua/examples/example.lua +++ b/sdcard/3ds/ctruLua/examples/example.lua @@ -8,7 +8,7 @@ local dMul = 1 local angle = 0 -local texture1 = gfx.texture.load("sdmc:/3ds/ctruLua/icon.png"); +local texture1 = gfx.texture.load(ctr.root.."icon.png"); if not texture1 then error("Giants ducks came from another planet") end gfx.color.setBackground(gfx.color.RGBA8(200, 200, 200)) @@ -29,7 +29,7 @@ local function drawStuffIn3D(eye) gfx.color.setDefault(0xFF0000FF) gfx.rectangle(x + d(10*math.sin(ctr.time()/500)), y, 20, 20, angle) - gfx.line(50 + d(-6), 50, 75 + d(4), 96, gfx.color.RGBA8(52, 10, 65)) + gfx.line(50 + d(-6), 50, 75 + d(4), 96, 1, gfx.color.RGBA8(52, 10, 65)) gfx.circle(125 + d(-8), 125, 16) end diff --git a/source/ctr.c b/source/ctr.c index db919dc..49c93ba 100644 --- a/source/ctr.c +++ b/source/ctr.c @@ -182,7 +182,7 @@ struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } { "apt", load_apt_lib, NULL }, { "mic", load_mic_lib, NULL }, { "thread", load_thread_lib, NULL }, - { NULL, NULL } + { NULL, NULL, NULL } }; int luaopen_ctr_lib(lua_State *L) { diff --git a/source/gfx.c b/source/gfx.c index f356eaa..23a5faf 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -4,6 +4,8 @@ The `gfx` module. @usage local gfx = require("ctr.gfx") */ #include +#include +#include #include #include @@ -15,6 +17,11 @@ The `gfx` module. #include #include "font.h" +#include "texture.h" + +typedef struct { + sf2d_rendertarget *target; +} target_userdata; bool isGfxInitialized = false; bool is3DEnabled = false; //TODO: add a function for this in the ctrulib/sf2dlib. @@ -51,17 +58,23 @@ The `ctr.gfx.map` module. void load_map_lib(lua_State *L); /*** -Start drawing to a screen. +Start drawing to a screen/target. Must be called before any draw operation. @function start -@tparam number screen the screen to draw to (`gfx.TOP` or `gfx.BOTTOM`) +@tparam number/target screen the screen or target to draw to (`gfx.TOP`, `gfx.BOTTOM`, or render target) @tparam[opt=gfx.LEFT] number eye the eye to draw to (`gfx.LEFT` or `gfx.RIGHT`) */ static int gfx_start(lua_State *L) { - u8 screen = luaL_checkinteger(L, 1); - u8 eye = luaL_optinteger(L, 2, GFX_LEFT); + if (lua_isinteger(L, 1)) { + u8 screen = luaL_checkinteger(L, 1); + u8 eye = luaL_optinteger(L, 2, GFX_LEFT); - sf2d_start_frame(screen, eye); + sf2d_start_frame(screen, eye); + } else if (lua_isuserdata(L, 1)) { + target_userdata *target = luaL_checkudata(L, 1, "LTarget"); + + sf2d_start_frame_target(target->target); + } return 0; } @@ -170,6 +183,7 @@ Draw a line on the current screen. @tparam integer y1 line's starting point vertical coordinate, in pixels @tparam integer x2 line's endpoint horizontal coordinate, in pixels @tparam integer y2 line's endpoint vertical coordinate, in pixels +@tparam[opt=1] number width line's thickness, in pixels @tparam[opt=default color] integer color drawing color */ static int gfx_line(lua_State *L) { @@ -177,10 +191,11 @@ static int gfx_line(lua_State *L) { int y1 = luaL_checkinteger(L, 2); int x2 = luaL_checkinteger(L, 3); int y2 = luaL_checkinteger(L, 4); + float width = luaL_optnumber(L, 5, 1.0f); - u32 color = luaL_optinteger(L, 5, color_default); + u32 color = luaL_optinteger(L, 6, color_default); - sf2d_draw_line(x1, y1, x2, y2, color); + sf2d_draw_line(x1, y1, x2, y2, width, color); return 0; } @@ -404,6 +419,111 @@ static int gfx_scissor(lua_State *L) { return 0; } +/*** +__Work in progress__. Create a render target. Don't use it. +@function target +@tparam integer width +@tparam integer height +@treturn target +*/ +static int gfx_target(lua_State *L) { + int width = luaL_checkinteger(L, 1); + int height = luaL_checkinteger(L, 2); + int wpo2 = 0, hpo2 = 0; + for (;width>pow(2,wpo2);wpo2++); + width = pow(2,wpo2); + for (;height>pow(2,hpo2);hpo2++); + height = pow(2,hpo2); + + target_userdata *target; + target = (target_userdata*)lua_newuserdata(L, sizeof(*target)); + + luaL_getmetatable(L, "LTarget"); + lua_setmetatable(L, -2); + + target->target = sf2d_create_rendertarget(width, height); + + return 1; +} + +/*** +Render targets +@section target +*/ + +/*** +Clear a target to a specified color. +@function :clear +@tparam[opt=default color] integer color color to fill the target with +*/ +static int gfx_target_clear(lua_State *L) { + target_userdata *target = luaL_checkudata(L, 1, "LTarget"); + u32 color = luaL_optinteger(L, 2, color_default); + + sf2d_clear_target(target->target, color); + + return 0; +} + +/*** +Destroy a target. +@function :destroy +*/ +static int gfx_target_destroy(lua_State *L) { + target_userdata *target = luaL_checkudata(L, 1, "LTarget"); + + sf2d_free_target(target->target); + + return 0; +} + +static const struct luaL_Reg target_methods[]; +/*** + +*/ +static int gfx_target___index(lua_State *L) { + target_userdata *target = luaL_checkudata(L, 1, "LTarget"); + const char* name = luaL_checkstring(L, 2); + + if (strcmp(name, "texture") == 0) { + texture_userdata *texture; + texture = (texture_userdata*)lua_newuserdata(L, sizeof(*texture)); + luaL_getmetatable(L, "LTexture"); + lua_setmetatable(L, -2); + + texture->texture = &(target->target->texture); + texture->scaleX = 1.0f; + texture->scaleY = 1.0f; + texture->blendColor = 0xffffffff; + + return 1; + } else if (strcmp(name, "duck") == 0) { + sf2d_rendertarget *target = sf2d_create_rendertarget(64, 64); + for(int i=0;;i++) { + sf2d_clear_target(target, 0xff000000); + sf2d_start_frame_target(target); + sf2d_draw_fill_circle(i%380, i%200, 10, 0xff0000ff); + sf2d_end_frame(); + //sf2d_texture_tile32(&target->texture); + + sf2d_start_frame(GFX_TOP, GFX_LEFT); + sf2d_draw_texture(&target->texture, 10, 10); + sf2d_end_frame(); + sf2d_swapbuffers(); + } + } else { + for (u8 i=0;target_methods[i].name;i++) { + if (strcmp(target_methods[i].name, name) == 0) { + lua_pushcfunction(L, target_methods[i].func); + return 1; + } + } + } + + lua_pushnil(L); + return 1; +} + // Functions static const struct luaL_Reg gfx_lib[] = { { "start", gfx_start }, @@ -425,6 +545,16 @@ static const struct luaL_Reg gfx_lib[] = { { "setTextSize", gfx_setTextSize }, { "getTextSize", gfx_getTextSize }, { "scissor", gfx_scissor }, + { "target", gfx_target }, + { NULL, NULL } +}; + +// Render target +static const struct luaL_Reg target_methods[] = { + { "__index", gfx_target___index }, + {"clear", gfx_target_clear }, + {"destroy", gfx_target_destroy }, + {"__gc", gfx_target_destroy }, { NULL, NULL } }; @@ -491,6 +621,11 @@ struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } }; int luaopen_gfx_lib(lua_State *L) { + luaL_newmetatable(L, "LTarget"); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + luaL_setfuncs(L, target_methods, 0); + luaL_newlib(L, gfx_lib); for (int i = 0; gfx_constants[i].name; i++) { diff --git a/source/httpc.c b/source/httpc.c index 5f15af7..1df43bd 100644 --- a/source/httpc.c +++ b/source/httpc.c @@ -228,9 +228,15 @@ static int httpc_addTrustedRootCA(lua_State *L) { u32 certsize; u8* cert = (u8*)luaL_checklstring(L, 2, (size_t*)&certsize); - httpcAddTrustedRootCA(context, cert, certsize); + Result ret = httpcAddTrustedRootCA(context, cert, certsize); + if (ret != 0) { + lua_pushnil(L); + lua_pushinteger(L, ret); + return 2; + } - return 0; + lua_pushboolean(L, true); + return 1; } // object @@ -242,6 +248,7 @@ static const struct luaL_Reg httpc_methods[] = { {"getDownloadSize", httpc_getDownloadSize }, {"downloadData", httpc_downloadData }, {"close", httpc_close }, + {"__gc", httpc_close }, {"addPostData", httpc_addPostData }, {"getResponseHeader", httpc_getResponseHeader }, {"addTrustedRootCA", httpc_addTrustedRootCA }, diff --git a/source/main.c b/source/main.c index 270c655..d106898 100644 --- a/source/main.c +++ b/source/main.c @@ -1,3 +1,5 @@ +#include + #include <3ds.h> #include @@ -32,7 +34,32 @@ void error(const char *error) { } // Main loop -int main() { +int main(int argc, char** argv) { + // Default arguments + char* mainFile = "main.lua"; + + // Parse arguments + for (int i=0;i Date: Wed, 23 Mar 2016 19:50:10 +0100 Subject: [PATCH 071/101] Added streaming support to ctr.audio Currently runs in the main thread because I can't get it to work on another thread. You will need to call audio.update() each frame to make audio streaming work. --- source/audio.c | 272 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 249 insertions(+), 23 deletions(-) diff --git a/source/audio.c b/source/audio.c index 2222999..9308cfe 100644 --- a/source/audio.c +++ b/source/audio.c @@ -11,6 +11,7 @@ There are 24 audio channels available, numbered from 0 to 23. #include #include #include +#include #include #include @@ -22,30 +23,78 @@ There are 24 audio channels available, numbered from 0 to 23. typedef enum { TYPE_UNKNOWN = -1, TYPE_OGG = 0, - TYPE_WAV = 1 + TYPE_WAV = 1, + TYPE_RAW = 2 } filetype; // Audio object userdata typedef struct { filetype type; // file type - // OGG Vorbis specific - OggVorbis_File vf; // ogg vorbis file + // File type specific + union { + // OGG Vorbis + struct { + OggVorbis_File vf; + int currentSection; // section and position at the end of the initial data + long rawPosition; + }; + // WAV + struct { + FILE* file; + long fileSize; + long filePosition; // position at the end of the initial data + }; + }; // Needed for playback float rate; // sample rate (per channel) (Hz) u32 channels; // channel count u32 encoding; // data encoding (NDSP_ENCODING_*) + + // Initial data u32 nsamples; // numbers of samples in the audio (per channel, not the total) u32 size; // number of bytes in the audio (total, ie data size) char* data; // raw audio data + // Other useful data + u16 bytePerSample; // bytes per sample (warning: undefined for ADPCM (only for raw data)) + u32 chunkSize; // size per chunk (for streaming) + u32 chunkNsamples; // number of samples per chunk + // Playing parameters (type-independant) float mix[12]; // mix parameters ndspInterpType interp; // interpolation type double speed; // playing speed } audio_userdata; +// Audio stream instance struct (when an audio is played; only used when streaming) +typedef struct { + audio_userdata* audio; + + // Current position information + union { + // OGG + struct { + int currentSection; + long rawPosition; + }; + // WAV + long filePosition; + }; + + double prevStartTime; // audio time when last chunk started playing + bool eof; // if reached end of file + bool done; // if streaming ended and the stream will be skipped on the next update + // (the struct should be keept in memory until replaced or it will break audio:time()) + + char* nextData; // the next data to play + ndspWaveBuf* nextWaveBuf; + + char* prevData; // the data actually playing + ndspWaveBuf* prevWaveBuf; +} audio_stream; + // Indicate if NDSP was initialized or not. // NDSP doesn't work on citra yet. // Please only throw an error related to this when using a ndsp function, so other parts of the @@ -55,12 +104,24 @@ bool isAudioInitialized = false; // Array of the last audio_userdata sent to each channel; channels range from 0 to 23 audio_userdata* channels[24]; +// Array of the audio_instance that needs to be updated when calling audio.update (indexed per channel). +audio_stream* streaming[24]; + /*** Load an audio file. OGG Vorbis and PCM WAV file format are currently supported. (Most WAV files use the PCM encoding). +NOTE: audio streaming doesn't use threading for now, this means that the decoding will be done on the main thread. +It should work fine with WAVs, but with OGG files you may suffer slowdowns when a new chunk of data is decoded. +To avoid that, you can either reduce the chunkDuration or disable streaming, but be careful if you do so, audio files +can fill the memory really quickly. @function load -@tparam string path path to the file +@tparam string path path to the file or the data if type is raw +@tparam[opt=0.1] number chunkDuration if set to -1, streaming will be disabled (all data is loaded in memory at once) + Other values are the stream chunk duration in seconds (ctrµLua will load + the audio per chunk of x seconds). Note that you need to call audio.update() each + frame in order for ctµLua to load new data from audio streams. Two chunks of data + will be loaded at the same time at most (one playing, the other ready to be played). @tparam[opt=detect] string type file type, `"ogg"` or `"wav"`. If set to `"detect"`, will try to deduce the type from the filename. @treturn[1] audio the loaded audio object @@ -69,7 +130,8 @@ OGG Vorbis and PCM WAV file format are currently supported. */ static int audio_load(lua_State *L) { const char *path = luaL_checkstring(L, 1); - const char* argType = luaL_optstring(L, 2, "detect"); + double streamChunk = luaL_optnumber(L, 2, 0.1); + const char* argType = luaL_optstring(L, 3, "detect"); // Create userdata audio_userdata *audio = lua_newuserdata(L, sizeof(*audio)); @@ -91,6 +153,8 @@ static int audio_load(lua_State *L) { type = TYPE_OGG; } else if (strcmp(argType, "wav") == 0) { type = TYPE_WAV; + } else if (strcmp(argType, "raw") == 0) { + type = TYPE_RAW; } // Open and read file @@ -114,16 +178,26 @@ static int audio_load(lua_State *L) { audio->encoding = NDSP_ENCODING_PCM16; audio->nsamples = ov_pcm_total(&audio->vf, -1); audio->size = audio->nsamples * audio->channels * 2; // *2 because output is PCM16 (2 bytes/sample) + audio->bytePerSample = 2; - if (linearSpaceFree() < audio->size) luaL_error(L, "not enough linear memory available"); - audio->data = linearAlloc(audio->size); + // Streaming + if (streamChunk < 0) { + audio->chunkNsamples = audio->nsamples; + audio->chunkSize = audio->size; + } else { + audio->chunkNsamples = round(streamChunk * audio->rate); + audio->chunkSize = audio->chunkNsamples * audio->channels * 2; + } + + // Allocate + if (linearSpaceFree() < audio->chunkSize) luaL_error(L, "not enough linear memory available"); + audio->data = linearAlloc(audio->chunkSize); // Decoding loop int offset = 0; int eof = 0; - int current_section; - while (!eof) { - long ret = ov_read(&audio->vf, &audio->data[offset], 4096, ¤t_section); + while (!eof && offset < audio->chunkSize) { + long ret = ov_read(&audio->vf, &audio->data[offset], fmin(audio->chunkSize - offset, 4096), &audio->currentSection); if (ret == 0) { eof = 1; @@ -137,6 +211,7 @@ static int audio_load(lua_State *L) { offset += ret; } } + audio->rawPosition = ov_raw_tell(&audio->vf); return 1; @@ -222,13 +297,29 @@ static int audio_load(lua_State *L) { return 0; } + audio->bytePerSample = byte_per_sample / audio->channels; + + // Streaming + if (streamChunk < 0) { + audio->chunkNsamples = audio->nsamples; + audio->chunkSize = audio->size; + } else { + audio->chunkNsamples = round(streamChunk * audio->rate); + audio->chunkSize = audio->chunkNsamples * audio->channels * audio->bytePerSample; + } + // Read data - if (linearSpaceFree() < audio->size) luaL_error(L, "not enough linear memory available"); - audio->data = linearAlloc(audio->size); + if (linearSpaceFree() < audio->chunkSize) luaL_error(L, "not enough linear memory available"); + audio->data = linearAlloc(audio->chunkSize); - fread(audio->data, audio->size, 1, file); + fread(audio->data, audio->chunkSize, 1, file); + + audio->file = file; + audio->filePosition = ftell(file); + + fseek(file, 0, SEEK_END); + audio->fileSize = ftell(file); - fclose(file); return 1; } else { @@ -244,6 +335,7 @@ static int audio_load(lua_State *L) { /*** Load raw audio data from a string. +No streaming. @function loadRaw @tparam string data raw audio data @tparam number rate sampling rate @@ -264,16 +356,18 @@ static int audio_loadRaw(lua_State *L) { luaL_getmetatable(L, "LAudio"); lua_setmetatable(L, -2); - audio->type = TYPE_WAV; + audio->type = TYPE_RAW; audio->rate = rate; audio->channels = channels; u8 sampleSize = 2; // default to 2 if (strcmp(argEncoding, "PCM8")) { audio->encoding = NDSP_ENCODING_PCM8; + audio->bytePerSample = 1; sampleSize = 1; } else if (strcmp(argEncoding, "PCM16")) { audio->encoding = NDSP_ENCODING_PCM16; + audio->bytePerSample = 2; } else if (strcmp(argEncoding, "ADPCM")) { audio->encoding = NDSP_ENCODING_ADPCM; } else { @@ -285,6 +379,9 @@ static int audio_loadRaw(lua_State *L) { audio->nsamples = dataSize/sampleSize; audio->size = dataSize; audio->data = data; + + audio->chunkSize = audio->size; + audio->chunkNsamples = audio->nsamples; audio->speed = 1.0; @@ -463,6 +560,102 @@ static int audio_stop(lua_State *L) { return 1; } +/*** +Update all the currently playing audio streams. +Must be called every frame if you want to use audio with streaming. +@function update +*/ +static int audio_update(lua_State *L) { + if (!isAudioInitialized) luaL_error(L, "audio wasn't initialized correctly"); + + for (int i = 0; i <= 23; i++) { + if (streaming[i] == NULL) continue; + audio_stream* stream = streaming[i]; + if (stream->done) continue; + audio_userdata* audio = stream->audio; + + // If the next chunk started to play, load the next one + if (stream->nextWaveBuf != NULL && ndspChnGetWaveBufSeq(i) == stream->nextWaveBuf->sequence_id) { + if (stream->prevWaveBuf) stream->prevStartTime = stream->prevStartTime + (double)(audio->chunkNsamples) / audio->rate; + + if (!stream->eof) { + // Swap buffers + char* prevData = stream->prevData; // doesn't contain important data, can rewrite + char* nextData = stream->nextData; // contains the data that started playing + stream->prevData = nextData; // buffer in use + stream->nextData = prevData; // now contains an available buffer + stream->prevWaveBuf = stream->nextWaveBuf; + + // Decoding loop + u32 chunkNsamples = audio->chunkNsamples; // chunk nsamples and size may be lower than the defaults if reached EOF + u32 chunkSize = audio->chunkSize; + if (audio->type == TYPE_OGG) { + if (ov_seekable(&audio->vf) && ov_raw_tell(&audio->vf) != stream->rawPosition) + ov_raw_seek(&audio->vf, stream->rawPosition); // goto last read end (audio file may be played multiple times at one) + + int offset = 0; + while (!stream->eof && offset < audio->chunkSize) { + long ret = ov_read(&audio->vf, &stream->nextData[offset], fmin(audio->chunkSize - offset, 4096), &stream->currentSection); + if (ret == 0) { + stream->eof = 1; + } else if (ret < 0) { + luaL_error(L, "error in the ogg vorbis stream"); + return 0; + } else { + offset += ret; + } + } + stream->rawPosition = ov_raw_tell(&audio->vf); + chunkSize = offset; + chunkNsamples = chunkSize / audio->channels / audio->bytePerSample; + + } else if (audio->type == TYPE_WAV) { + chunkSize = fmin(audio->fileSize - stream->filePosition, audio->chunkSize); + chunkNsamples = chunkSize / audio->channels / audio->bytePerSample; + + fseek(audio->file, stream->filePosition, SEEK_SET); // goto last read end (audio file may be played multiple times at one) + fread(stream->nextData, chunkSize, 1, audio->file); + stream->filePosition = ftell(audio->file); + if (stream->filePosition == audio->fileSize) stream->eof = 1; + + } else luaL_error(L, "unknown audio type"); + + // Send & play audio data + ndspWaveBuf* waveBuf = calloc(1, sizeof(ndspWaveBuf)); + + waveBuf->data_vaddr = stream->nextData; + waveBuf->nsamples = chunkNsamples; + waveBuf->looping = false; + + DSP_FlushDataCache((u32*)stream->nextData, chunkSize); + + ndspChnWaveBufAdd(i, waveBuf); + + stream->nextWaveBuf = waveBuf; + } + } + + // Free the last chunk if it's no longer played + if (stream->prevWaveBuf != NULL && ndspChnGetWaveBufSeq(i) != stream->prevWaveBuf->sequence_id) { + free(stream->prevWaveBuf); + stream->prevWaveBuf = NULL; + } + + // We're done + if (stream->prevWaveBuf == NULL && stream->nextWaveBuf != NULL && ndspChnGetWaveBufSeq(i) != stream->nextWaveBuf->sequence_id && stream->eof) { + free(stream->nextWaveBuf); + stream->nextWaveBuf = NULL; + linearFree(stream->prevData); + stream->prevData = NULL; + linearFree(stream->nextData); + stream->nextData = NULL; + stream->done = true; + } + } + + return 0; +} + /*** audio object @section Methods @@ -505,8 +698,11 @@ static int audio_object_time(lua_State *L) { if (channel == -1 || channels[channel] != audio || !isAudioInitialized) // audio not playing lua_pushnumber(L, 0); - else - lua_pushnumber(L, (double)(ndspChnGetSamplePos(channel)) / audio->rate); + else { + double additionnalTime = 0; + if (streaming[channel] != NULL) additionnalTime = streaming[channel]->prevStartTime; + lua_pushnumber(L, (double)(ndspChnGetSamplePos(channel)) / audio->rate + additionnalTime); + } return 1; } @@ -650,20 +846,46 @@ static int audio_object_play(lua_State *L) { ndspChnSetRate(channel, audio->rate * audio->speed); // maybe hackish way to set a different speed, but it works ndspChnSetFormat(channel, NDSP_CHANNELS(audio->channels) | NDSP_ENCODING(audio->encoding)); - // Send & play audio data + // Send & play audio initial data ndspWaveBuf* waveBuf = calloc(1, sizeof(ndspWaveBuf)); waveBuf->data_vaddr = audio->data; - waveBuf->nsamples = audio->nsamples; + waveBuf->nsamples = audio->chunkNsamples; waveBuf->looping = loop; - DSP_FlushDataCache((u32*)audio->data, audio->size); + DSP_FlushDataCache((u32*)audio->data, audio->chunkSize); ndspChnWaveBufAdd(channel, waveBuf); channels[channel] = audio; lua_pushinteger(L, channel); + // Remove last audio stream + if (streaming[channel] != NULL) { + free(streaming[channel]); + streaming[channel] = NULL; + } + + // Stream the rest of the audio + if (audio->chunkSize < audio->size) { + audio_stream* stream = calloc(1, sizeof(audio_stream)); + stream->audio = audio; + stream->nextWaveBuf = waveBuf; + + // Allocate buffers + if (linearSpaceFree() < audio->chunkSize*2) luaL_error(L, "not enough linear memory available"); + stream->nextData = linearAlloc(audio->chunkSize); + stream->prevData = linearAlloc(audio->chunkSize); + + // Init stream values + if (audio->type == TYPE_OGG) { + stream->currentSection = audio->currentSection; + stream->rawPosition = audio->rawPosition; + } else if (audio->type == TYPE_WAV) stream->filePosition = audio->filePosition; + + streaming[channel] = stream; + } + return 1; } @@ -718,6 +940,10 @@ static int audio_object_type(lua_State *L) { lua_pushstring(L, "ogg"); else if (audio->type == TYPE_WAV) lua_pushstring(L, "wav"); + else if (audio->type == TYPE_RAW) + lua_pushstring(L, "raw"); + else + lua_pushstring(L, "unknown"); return 1; } @@ -739,6 +965,7 @@ static int audio_object_unload(lua_State *L) { } if (audio->type == TYPE_OGG) ov_clear(&audio->vf); + else if (audio->type == TYPE_WAV) fclose(audio->file); // Free memory linearFree(audio->data); @@ -872,6 +1099,7 @@ static const struct luaL_Reg audio_lib[] = { { "interpolation", audio_interpolation }, { "speed", audio_speed }, { "stop", audio_stop }, + { "update", audio_update }, { NULL, NULL } }; @@ -887,9 +1115,7 @@ int luaopen_audio_lib(lua_State *L) { } void load_audio_lib(lua_State *L) { - if (!isAudioInitialized) { - isAudioInitialized = !ndspInit(); // ndspInit returns 0 in case of success - } + if (!isAudioInitialized) isAudioInitialized = !ndspInit(); // ndspInit returns 0 in case of success luaL_requiref(L, "ctr.audio", luaopen_audio_lib, false); } From 04eb578892a1236c3fce35c5085071c5cd307828 Mon Sep 17 00:00:00 2001 From: Reuh Date: Wed, 23 Mar 2016 19:53:46 +0100 Subject: [PATCH 072/101] Updated audio example --- sdcard/3ds/ctruLua/examples/audio/audio.lua | 1 + source/audio.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sdcard/3ds/ctruLua/examples/audio/audio.lua b/sdcard/3ds/ctruLua/examples/audio/audio.lua index d794708..34dead2 100644 --- a/sdcard/3ds/ctruLua/examples/audio/audio.lua +++ b/sdcard/3ds/ctruLua/examples/audio/audio.lua @@ -47,6 +47,7 @@ while true do gfx.text(5, 65, "Speed: "..(speed*100).."% - Left balance: "..(leftBalance*100).."%") gfx.stop() + audio.update() gfx.render() end diff --git a/source/audio.c b/source/audio.c index 9308cfe..bc6aca5 100644 --- a/source/audio.c +++ b/source/audio.c @@ -931,7 +931,7 @@ static int audio_object_stop(lua_State *L) { /*** Returns the audio object type. @function :type -@treturn string "ogg" or "wav" +@treturn string "ogg", "wav" or "raw" */ static int audio_object_type(lua_State *L) { audio_userdata *audio = luaL_checkudata(L, 1, "LAudio"); From 4c9bdf75fae28c5dc630fb0770fe1e3206b1be83 Mon Sep 17 00:00:00 2001 From: Reuh Date: Wed, 23 Mar 2016 20:16:20 +0100 Subject: [PATCH 073/101] Unload streaming data when stopping audio Forgot some memory freeing, sorry. --- source/audio.c | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/source/audio.c b/source/audio.c index bc6aca5..812436a 100644 --- a/source/audio.c +++ b/source/audio.c @@ -86,7 +86,7 @@ typedef struct { double prevStartTime; // audio time when last chunk started playing bool eof; // if reached end of file bool done; // if streaming ended and the stream will be skipped on the next update - // (the struct should be keept in memory until replaced or it will break audio:time()) + // (the struct should be keept in memory until replaced or audio stopped or it will break audio:time()) char* nextData; // the next data to play ndspWaveBuf* nextWaveBuf; @@ -107,6 +107,34 @@ audio_userdata* channels[24]; // Array of the audio_instance that needs to be updated when calling audio.update (indexed per channel). audio_stream* streaming[24]; +// Stop playing audio on a channel, and stop streaming/free memory. +void stopAudio(int channel) { + ndspChnWaveBufClear(channel); + + // Stop streaming and free data + if (streaming[channel] != NULL) { + audio_stream* stream = streaming[channel]; + if (stream->nextWaveBuf != NULL) { + free(stream->nextWaveBuf); + stream->nextWaveBuf = NULL; + } + if (stream->nextData != NULL) { + linearFree(stream->nextData); + stream->nextData = NULL; + } + if (stream->prevWaveBuf != NULL) { + free(stream->prevWaveBuf); + stream->prevWaveBuf = NULL; + } + if (stream->prevData != NULL) { + linearFree(stream->prevData); + stream->prevData = NULL; + } + free(stream); + streaming[channel] = NULL; + } +} + /*** Load an audio file. OGG Vorbis and PCM WAV file format are currently supported. @@ -542,7 +570,7 @@ static int audio_stop(lua_State *L) { if (channel == -1) { for (int i = 0; i <= 23; i++) { if (ndspChnIsPlaying(i)) { - ndspChnWaveBufClear(i); + stopAudio(i); n++; } } @@ -550,7 +578,7 @@ static int audio_stop(lua_State *L) { luaL_error(L, "channel number must be between 0 and 23"); } else { if (ndspChnIsPlaying(channel)) { - ndspChnWaveBufClear(channel); + stopAudio(channel); n++; } } @@ -838,7 +866,7 @@ static int audio_object_play(lua_State *L) { if (channel < 0 || channel > 23) luaL_error(L, "channel number must be between 0 and 23"); // Set channel parameters - ndspChnWaveBufClear(channel); + stopAudio(channel); ndspChnReset(channel); ndspChnInitParams(channel); ndspChnSetMix(channel, audio->mix); @@ -910,7 +938,7 @@ static int audio_object_stop(lua_State *L) { if (channel == -1) { for (int i = 0; i <= 23; i++) { if (channels[i] == audio && ndspChnIsPlaying(i)) { - ndspChnWaveBufClear(i); + stopAudio(i); n++; } } @@ -918,7 +946,7 @@ static int audio_object_stop(lua_State *L) { luaL_error(L, "channel number must be between 0 and 23"); } else { if (channels[channel] == audio && ndspChnIsPlaying(channel)) { - ndspChnWaveBufClear(channel); + stopAudio(channel); n++; } } @@ -959,7 +987,7 @@ static int audio_object_unload(lua_State *L) { if (isAudioInitialized) { for (int i = 0; i <= 23; i++) { if (channels[i] == audio) { - ndspChnWaveBufClear(i); + stopAudio(i); } } } From 61433417604d161db400ec3b26846475195366f7 Mon Sep 17 00:00:00 2001 From: Reuh Date: Thu, 24 Mar 2016 18:07:41 +0100 Subject: [PATCH 074/101] Fixed audio looping when using streaming Also made sure the chunk size is lower or equal the total file size. --- source/audio.c | 45 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/source/audio.c b/source/audio.c index 812436a..fa56cd0 100644 --- a/source/audio.c +++ b/source/audio.c @@ -72,6 +72,8 @@ typedef struct { typedef struct { audio_userdata* audio; + bool loop; // loop audio? + // Current position information union { // OGG @@ -213,7 +215,7 @@ static int audio_load(lua_State *L) { audio->chunkNsamples = audio->nsamples; audio->chunkSize = audio->size; } else { - audio->chunkNsamples = round(streamChunk * audio->rate); + audio->chunkNsamples = fmin(round(streamChunk * audio->rate), audio->nsamples); audio->chunkSize = audio->chunkNsamples * audio->channels * 2; } @@ -332,7 +334,7 @@ static int audio_load(lua_State *L) { audio->chunkNsamples = audio->nsamples; audio->chunkSize = audio->size; } else { - audio->chunkNsamples = round(streamChunk * audio->rate); + audio->chunkNsamples = fmin(round(streamChunk * audio->rate), audio->nsamples); audio->chunkSize = audio->chunkNsamples * audio->channels * audio->bytePerSample; } @@ -673,11 +675,37 @@ static int audio_update(lua_State *L) { if (stream->prevWaveBuf == NULL && stream->nextWaveBuf != NULL && ndspChnGetWaveBufSeq(i) != stream->nextWaveBuf->sequence_id && stream->eof) { free(stream->nextWaveBuf); stream->nextWaveBuf = NULL; - linearFree(stream->prevData); - stream->prevData = NULL; - linearFree(stream->nextData); - stream->nextData = NULL; - stream->done = true; + + // Free memory + if (!stream->loop) { + linearFree(stream->prevData); + stream->prevData = NULL; + linearFree(stream->nextData); + stream->nextData = NULL; + stream->done = true; + // Loop: goto start + } else { + // Send & play audio initial data + ndspWaveBuf* waveBuf = calloc(1, sizeof(ndspWaveBuf)); + + waveBuf->data_vaddr = audio->data; + waveBuf->nsamples = audio->chunkNsamples; + waveBuf->looping = false; + + DSP_FlushDataCache((u32*)audio->data, audio->chunkSize); + + ndspChnWaveBufAdd(i, waveBuf); + + stream->nextWaveBuf = waveBuf; + + // Reset stream values + stream->prevStartTime = 0; + stream->eof = false; + if (audio->type == TYPE_OGG) { + stream->currentSection = audio->currentSection; + stream->rawPosition = audio->rawPosition; + } else if (audio->type == TYPE_WAV) stream->filePosition = audio->filePosition; + } } } @@ -879,7 +907,7 @@ static int audio_object_play(lua_State *L) { waveBuf->data_vaddr = audio->data; waveBuf->nsamples = audio->chunkNsamples; - waveBuf->looping = loop; + waveBuf->looping = (audio->chunkSize < audio->size) ? false : loop; // let ndsp loop the chunk if not streaming DSP_FlushDataCache((u32*)audio->data, audio->chunkSize); @@ -898,6 +926,7 @@ static int audio_object_play(lua_State *L) { if (audio->chunkSize < audio->size) { audio_stream* stream = calloc(1, sizeof(audio_stream)); stream->audio = audio; + stream->loop = loop; stream->nextWaveBuf = waveBuf; // Allocate buffers From eae356ce8090e1d9df85c46310da618f47772a2d Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Sat, 26 Mar 2016 16:49:23 +0100 Subject: [PATCH 075/101] Added the :save() method to textures, Added support for some image formats The new image formats are loaded with stb_image, and the textures are saved with stb_image_write. https://github.com/nothings/stb You can now load GIFs (not animated), PSD, TGA, HDR, PIC and PNM. The texture loading still uses sfil for JPEG, PNG, and BMP. You can force the use of stbi by loading with the "type" argument in texture.load() set to 242 (or anything 5 +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +typedef unsigned char stbi_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// NOTE: not clear do we actually need this for the 64-bit path? +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// (but compiling with -msse2 allows the compiler to use SSE2 everywhere; +// this is just broken and gcc are jerks for not fixing it properly +// http://www.virtualdub.org/blog/pivot/entry.php?id=363 ) +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && defined(STBI__X86_TARGET) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +static int stbi__sse2_available() +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +static int stbi__sse2_available() +{ +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later + // GCC 4.8+ has a nice way to do this + return __builtin_cpu_supports("sse2"); +#else + // portable way to do this, preferably without using GCC inline ASM? + // just bail for now. + return 0; +#endif +} +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +// assume GCC or Clang on ARM targets +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +// this is not threadsafe +static const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load = flag_true_if_should_flip; +} + +static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result = stbi__load_main(s, x, y, comp, req_comp); + + if (stbi__vertically_flip_on_load && result != NULL) { + int w = *x, h = *y; + int depth = req_comp ? req_comp : *comp; + int row,col,z; + stbi_uc temp; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < depth; z++) { + temp = result[(row * w + col) * depth + z]; + result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; + result[((h - row - 1) * w + col) * depth + z] = temp; + } + } + } + } + + return result; +} + +#ifndef STBI_NO_HDR +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int w = *x, h = *y; + int depth = req_comp ? req_comp : *comp; + int row,col,z; + float temp; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < depth; z++) { + temp = result[(row * w + col) * depth + z]; + result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; + result[((h - row - 1) * w + col) * depth + z] = temp; + } + } + } + } +} +#endif + +#ifndef STBI_NO_STDIO + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_flip(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} +#endif //!STBI_NO_STDIO + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_flip(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_flip(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_flip(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_file(&s,f); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +static void stbi__skip(stbi__context *s, int n) +{ + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} + +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + return z + (stbi__get16le(s) << 16); +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc(req_comp * x * y); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + default: STBI_ASSERT(0); + } + #undef CASE + } + + STBI_FREE(data); + return good; +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) stbi__malloc(x * y * comp * sizeof(float)); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) stbi__malloc(x * y * comp); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi_uc dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (-1 << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + k = stbi_lrot(j->code_buffer, n); + STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & ~sgn); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi_uc *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc << j->succ_low); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) << shift); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) << shift); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi_uc *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4; + int t = q & 15,i; + if (p != 0) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = stbi__get8(z->s); + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + stbi__skip(z->s, stbi__get16be(z->s)-2); + return 1; + } + return 0; +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + c = stbi__get8(s); + if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + for (i=0; i < s->img_n; ++i) { + z->img_comp[i].id = stbi__get8(s); + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! + return stbi__err("bad component ID","Corrupt JPEG"); + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + } + return stbi__err("outofmem", "Out of memory"); + } + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; + if (z->progressive) { + z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3; + z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3; + z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } else { + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } else if (x != 0) { + return stbi__err("junk before marker", "Corrupt JPEG"); + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#ifdef STBI_JPEG_OLD +// this is the same YCbCr-to-RGB calculation that stb_image has used +// historically before the algorithm changes in 1.49 +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#else +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* float2fixed(1.40200f); + g = y_fixed + (cr*-float2fixed(0.71414f)) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* float2fixed(1.40200f); + g = y_fixed + cr*-float2fixed(0.71414f) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + #ifndef STBI_JPEG_OLD + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + #endif + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + #ifndef STBI_JPEG_OLD + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + #endif + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + int i; + for (i=0; i < j->s->img_n; ++i) { + if (j->img_comp[i].raw_data) { + STBI_FREE(j->img_comp[i].raw_data); + j->img_comp[i].raw_data = NULL; + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].raw_coeff) { + STBI_FREE(j->img_comp[i].raw_coeff); + j->img_comp[i].raw_coeff = 0; + j->img_comp[i].coeff = 0; + } + if (j->img_comp[i].linebuf) { + STBI_FREE(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n; + + if (z->s->img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4]; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc(n * z->s->img_x * z->s->img_y + 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n; // report original components, not output + return output; + } +} + +static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__jpeg j; + j.s = s; + stbi__setup_jpeg(&j); + return load_jpeg_image(&j, x,y,comp,req_comp); +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg j; + j.s = s; + stbi__setup_jpeg(&j); + r = stbi__decode_jpeg_header(&j, STBI__SCAN_type); + stbi__rewind(s); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__jpeg j; + j.s = s; + return stbi__jpeg_info_raw(&j, x, y, comp); +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[288]; + stbi__uint16 value[288]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + STBI_ASSERT(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) stbi__fill_bits(a); + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = old_limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else if (c == 16) { + c = stbi__zreceive(a,2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncomperssed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + STBI_ASSERT(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +// @TODO: should statically initialize these for optimal thread safety +static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32]; +static void stbi__init_zdefaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncomperssed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zdefault_distance[31]) stbi__init_zdefaults(); + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc(x * y * out_n); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + if (s->img_x == x && s->img_y == y) { + if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } else { // interlaced: + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior = cur - stride; + int filter = *raw++; + int filter_bytes = img_n; + int width = x; + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + STBI_ASSERT(img_width_bytes <= x); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*img_n; + #define CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); break; + CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); break; + CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); break; + CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break; + CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); break; + } + #undef CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(STBI__F_none) cur[k] = raw[k]; break; + CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-out_n]); break; + CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-out_n])>>1)); break; + CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; + CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-out_n] >> 1)); break; + CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],0,0)); break; + } + #undef CASE + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_n + out_x*out_n, + a->out + (j*x+i)*out_n, out_n); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc(pixel_count * pal_img_n); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load = 0; +static int stbi__de_iphone_flag = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + p[0] = p[2] * 255 / a; + p[1] = p[1] * 255 / a; + p[2] = t * 255 / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, depth=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + depth = stbi__get8(s); if (depth != 1 && depth != 2 && depth != 4 && depth != 8) return stbi__err("1/2/4/8-bit only","PNG not supported: 1/2/4/8-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + for (k=0; k < s->img_n; ++k) + tc[k] = (stbi_uc) (stbi__get16be(s) & 255) * stbi__depth_scale_table[depth]; // non 8-bit images will be larger + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, depth, color, interlace)) return 0; + if (has_trans) + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } + STBI_FREE(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_out_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int stbi__shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; +} stbi__bmp_data; + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (info->bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + info->mr = info->mg = info->mb = 0; + if (compress == 0) { + if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - 14 - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - 14 - info.hsz) >> 2; + } + + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - 14 - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if(is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // else: fall-through + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fall-through + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (r * 255)/31; + out[1] = (g * 255)/31; + out[2] = (b * 255)/31; + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + tga_data = (unsigned char*)stbi__malloc( (size_t)tga_width * tga_height * tga_comp ); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_comp ); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int bitdepth; + int w,h; + stbi_uc *out; + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) stbi__malloc(4 * w*h); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out + channel; + if (channel >= channelCount) { + // Fill this channel with default data. + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } else { + // Read the data. + if (bitdepth == 16) { + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + + if (req_comp && req_comp != 4) { + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp) +{ + stbi_uc *result; + int i, x,y; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc(x*y*4); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out, *old_out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags, delay; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[4096]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif g; + if (!stbi__gif_header(s, &g, comp, 1)) { + stbi__rewind( s ); + return 0; + } + if (x) *x = g.w; + if (y) *y = g.h; + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + p = &g->out[g->cur_x + g->cur_y]; + c = &g->color_table[g->codes[code].suffix * 4]; + + if (c[3] >= 128) { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +static void stbi__fill_gif_background(stbi__gif *g, int x0, int y0, int x1, int y1) +{ + int x, y; + stbi_uc *c = g->pal[g->bgindex]; + for (y = y0; y < y1; y += 4 * g->w) { + for (x = x0; x < x1; x += 4) { + stbi_uc *p = &g->out[y + x]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = 0; + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) +{ + int i; + stbi_uc *prev_out = 0; + + if (g->out == 0 && !stbi__gif_header(s, g, comp,0)) + return 0; // stbi__g_failure_reason set by stbi__gif_header + + prev_out = g->out; + g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + + switch ((g->eflags & 0x1C) >> 2) { + case 0: // unspecified (also always used on 1st frame) + stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h); + break; + case 1: // do not dispose + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + g->old_out = prev_out; + break; + case 2: // dispose to background + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y); + break; + case 3: // dispose to previous + if (g->old_out) { + for (i = g->start_y; i < g->max_y; i += 4 * g->w) + memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x); + } + break; + } + + for (;;) { + switch (stbi__get8(s)) { + case 0x2C: /* Image Descriptor */ + { + int prev_trans = -1; + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + if (g->transparent >= 0 && (g->eflags & 0x01)) { + prev_trans = g->pal[g->transparent][3]; + g->pal[g->transparent][3] = 0; + } + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (o == NULL) return NULL; + + if (prev_trans != -1) + g->pal[g->transparent][3] = (stbi_uc) prev_trans; + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = stbi__get16le(s); + g->transparent = stbi__get8(s); + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) + stbi__skip(s, len); + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } + + STBI_NOTUSED(req_comp); +} + +static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + + u = stbi__gif_load_next(s, &g, comp, req_comp); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } + else if (g.out) + STBI_FREE(g.out); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s) +{ + const char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s); + stbi__rewind(s); + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) stbi__malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + stbi__rewind( s ); + if (p == NULL) + return 0; + *x = s->img_x; + *y = s->img_y; + *comp = info.ma ? 4 : 3; + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + if (stbi__get16be(s) != 8) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained; + stbi__pic_packet packets[10]; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) +// Does not support 16-bit-per-channel + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *out; + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + return 0; + *x = s->img_x; + *y = s->img_y; + *comp = s->img_n; + + out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv; + char c, p, t; + + stbi__rewind( s ); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + + if (maxv > 255) + return stbi__err("max value > 255", "PPM image not 8-bit"); + else + return 1; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + diff --git a/libs/stb/include/stb_image_write.h b/libs/stb/include/stb_image_write.h new file mode 100644 index 0000000..1bb99e1 --- /dev/null +++ b/libs/stb/include/stb_image_write.h @@ -0,0 +1,1045 @@ +/* stb_image_write - v1.01 - public domain - http://nothings.org/stb/stb_image_write.h + writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio. It could be + adapted to write to memory or a general streaming interface; let me know. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation. This library is designed + for source code compactness and simplicity, not optimal image file size + or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can define STBIW_MEMMOVE() to replace memmove() + +USAGE: + + There are four functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + + There are also four equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + +CREDITS: + + PNG/BMP/TGA + Sean Barrett + HDR + Baldur Karlsson + TGA monochrome: + Jean-Sebastien Guay + misc enhancements: + Tim Kelsey + TGA RLE + Alan Hickman + initial file IO callback implementation + Emmanuel Julien + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + +LICENSE + +This software is in the public domain. Where that dedication is not +recognized, you are granted a perpetual, irrevocable license to copy, +distribute, and modify this file as you see fit. + +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#define STBIWDEF extern +extern int stbi_write_tga_with_rle; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + +#ifdef __cplusplus +} +#endif + +#endif//INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO + +#include +#include +#include +#include + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,newsz) realloc(p,newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#endif + + +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT +#include +#define STBIW_ASSERT(x) assert(x) +#endif + +#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) + +typedef struct +{ + stbi_write_func *func; + void *context; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi_write_tga_with_rle = 1; +#else +int stbi_write_tga_with_rle = 1; +#endif + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=STBIW_UCHAR(x); + b[1]=STBIW_UCHAR(x>>8); + b[2]=STBIW_UCHAR(x>>16); + b[3]=STBIW_UCHAR(x>>24); + s->func(s->context,b,4); + break; } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) +{ + unsigned char arr[3]; + arr[0] = a, arr[1] = b, arr[2] = c; + s->func(s->context, arr, 3); +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + s->func(s->context, &d[comp - 1], 1); + + switch (comp) { + case 1: + s->func(s->context,d,1); + break; + case 2: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + s->func(s->context, d, 1); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + s->func(s->context, &d[comp - 1], 1); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ + stbiw_uint32 zero = 0; + int i,j, j_end; + + if (y <= 0) + return; + + if (vdir < 0) + j_end = -1, j = y-1; + else + j_end = y, j = 0; + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +{ + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) +{ + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header +} + +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) +{ + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + for (j = y - 1; j >= 0; --j) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + s->func(s->context, &header, 1); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + s->func(s->context, &header, 1); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + } + return 1; +} + +int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson +#ifndef STBI_WRITE_NO_STDIO + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32f) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +{ + unsigned char lengthbyte = STBIW_UCHAR(length+128); + STBIW_ASSERT(length+128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +{ + unsigned char lengthbyte = STBIW_UCHAR(length); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) +{ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + + len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); + s->func(s->context, buffer, len); + + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x); + STBIW_FREE(scratch); + return 1; + } +} + +int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char **hash_table[stbiw__ZHASH]; // 64KB on the stack! + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) best=d,bestloc=hlist[j]; + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + + { + // compute adler32 on input + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; + s1 %= 65521, s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); +} + +static unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + unsigned int crc = ~0u; + int i; + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int i,j,k,p,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = j ? mapping : firstmap; + int best = 0, bestval = 0x7fffffff; + for (p=0; p < 2; ++p) { + for (k= p?best:0; k < 5; ++k) { + int type = mymap[k],est=0; + unsigned char *z = pixels + stride_bytes*j; + for (i=0; i < n; ++i) + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + for (i=n; i < x*n; ++i) { + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i] - z[i-n]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; + case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; + case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } + } + if (p) break; + for (i=0; i < x*n; ++i) + est += abs((signed char) line_buffer[i]); + if (est < bestval) { bestval = est; best = k; } + } + } + // when we get here, best contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) best; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + f = fopen(filename, "wb"); + if (!f) { STBIW_FREE(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ + diff --git a/source/gfx.c b/source/gfx.c index 23a5faf..ed6bfe5 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -558,6 +558,10 @@ static const struct luaL_Reg target_methods[] = { { NULL, NULL } }; +/*** +Constants +@section constants +*/ // Constants struct { char *name; int value; } gfx_constants[] = { /*** diff --git a/source/texture.c b/source/texture.c index a369737..6468da6 100644 --- a/source/texture.c +++ b/source/texture.c @@ -12,6 +12,12 @@ The `gfx.texture` module. #include #include +#define STB_IMAGE_IMPLEMENTATION +#include +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include +#include + #include "texture.h" int getType(const char *name) { @@ -58,9 +64,15 @@ static int texture_load(lua_State *L) { } else if (type==2) { //BMP texture->texture = sfil_load_BMP_file(path, place); //appears to be broken right now. } else { - lua_pushnil(L); - lua_pushstring(L, "Bad type"); - return 2; + int w, h; + char* data = (char*)stbi_load(path, &w, &h, NULL, 4); + if (data == NULL) { + lua_pushnil(L); + lua_pushstring(L, "Can't open file"); + return 2; + } + texture->texture = sf2d_create_texture_mem_RGBA8(data, w, h, TEXFMT_RGBA8, place); + free(data); } if (texture->texture == NULL) { @@ -246,7 +258,7 @@ Set the blend color of the texture. @tparam number color new blend color */ static int texture_setBlendColor(lua_State *L) { - texture_userdata *texture = luaL_checkudata(L, 1, "LTexture"); + texture_userdata *texture = luaL_checkudata(L, 1, "LTexture"); u32 color = luaL_checkinteger(L, 2); texture->blendColor = color; @@ -254,6 +266,87 @@ static int texture_setBlendColor(lua_State *L) { return 0; } +/*** +Save a texture to a file. +@function :save +@tparam string filename path to the file to save the texture to +@tparam[opt=TYPE_PNG] number type type of the image to save. Can be TYPE_PNG or TYPE_BMP +@treturn[1] boolean true on success +@treturn[2] nil +@treturn[2] string error message +*/ +static int texture_save(lua_State *L) { + texture_userdata *texture = luaL_checkudata(L, 1, "LTexture"); + const char* path = luaL_checkstring(L, 2); + u8 type = luaL_optinteger(L, 3, 0); + + u32* buff = malloc(texture->texture->width * texture->texture->height * 4); + if (buff == NULL) { + lua_pushnil(L); + lua_pushstring(L, "Failed to allocate buffer"); + return 2; + } + for (int y=0;ytexture->height;y++) { + for (int x=0;xtexture->width;x++) { + buff[x+(y*texture->texture->width)] = __builtin_bswap32(sf2d_get_pixel(texture->texture, x, y)); + } + } + + int result = 0; + if (type == 0) { // PNG + FILE* file = fopen(path, "wb"); + if (file == NULL) { + free(buff); + lua_pushnil(L); + lua_pushstring(L, "Can open file"); + return 2; + } + png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_infop infos = png_create_info_struct(png); + setjmp(png_jmpbuf(png)); + png_init_io(png, file); + + png_set_IHDR(png, infos, texture->texture->width, texture->texture->height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_write_info(png, infos); + + png_bytep row = malloc(4 * texture->texture->width * sizeof(png_byte)); + + for(int y=0;ytexture->height;y++) { + for (int x=0;xtexture->width;x++) { + ((u32*)row)[x] = buff[x+(y*texture->texture->width)]; + } + png_write_row(png, row); + } + + png_write_end(png, NULL); + + fclose(file); + png_free_data(png, infos, PNG_FREE_ALL, -1); + png_destroy_write_struct(&png, &infos); + free(row); + + result = 1; + + } else if (type == 2) { // BMP + result = stbi_write_bmp(path, texture->texture->width, texture->texture->height, 4, buff); + } else { + free(buff); + lua_pushnil(L); + lua_pushstring(L, "Not a valid type"); + return 2; + } + free(buff); + + if (result == 0) { + lua_pushnil(L); + lua_pushstring(L, "Failed to save the texture"); + return 2; + } + + lua_pushboolean(L, true); + return 1; +} + // object static const struct luaL_Reg texture_methods[] = { { "draw", texture_draw }, @@ -264,6 +357,7 @@ static const struct luaL_Reg texture_methods[] = { { "getPixel", texture_getPixel }, { "setPixel", texture_setPixel }, { "setBlendColor", texture_setBlendColor }, + { "save", texture_save }, { "__gc", texture_unload }, {NULL, NULL} }; From c687efcfb48dd6c3a4ca9ce8c7d57e66979e214b Mon Sep 17 00:00:00 2001 From: Neil Zeke Cecchini Date: Sat, 2 Apr 2016 18:29:54 +0200 Subject: [PATCH 076/101] Made keyboard.lua use a configuration file Thus removing the need to edit it directly as an user --- sdcard/3ds/ctruLua/config/keyboard.cfg | 40 ++++++++++++++++ sdcard/3ds/ctruLua/libs/keyboard.lua | 66 +++++++------------------- 2 files changed, 57 insertions(+), 49 deletions(-) create mode 100644 sdcard/3ds/ctruLua/config/keyboard.cfg diff --git a/sdcard/3ds/ctruLua/config/keyboard.cfg b/sdcard/3ds/ctruLua/config/keyboard.cfg new file mode 100644 index 0000000..fda1efe --- /dev/null +++ b/sdcard/3ds/ctruLua/config/keyboard.cfg @@ -0,0 +1,40 @@ +keyWidth, keyHeight = 25, 25 + +layout = { + ["default"] = { + { "&", "é", "\"", "'", "(", "-", "è", "_", "ç", "à", ")", "=", "Bks" }, + { "a", "z", "e", "r", "t", "y", "u", "i", "o", "p", "^", "$", "Ent" }, + { "q", "s", "d", "f", "g", "h", "j", "k", "l", "m", "ù", "*", "Ent" }, + { "Shift", "<", "w", "x", "c", "v", "b", "n", ",", ";", ":", "!", "Tab" }, + { "SLck", ">", "+", "/", " ", " ", " ", " ", " ", "{", "}", ".", "Sym" } + }, + ["Shift"] = { + { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "°", "+", "Bks" }, + { "A", "Z", "E", "R", "T", "Y", "U", "I", "O", "P", "¨", "£", "Ent" }, + { "Q", "S", "D", "F", "G", "H", "J", "K", "L", "M", "%", "µ", "Ent" }, + { "Shift", ">", "W", "X", "C", "V", "B", "N", "?", ".", "/", "§", "Tab" }, + { "SLck", "~", "#", "[", " ", " ", " ", " ", " ", "]", "|", "@", "Sym" } + }, + ["Sym"] = { + { "²", "~", "#", "{", "[", "|", "`", "\\", "^", "@", "]", "}", "Bks" }, + { "a", "z", "€", "r", "t", "y", "u", "i", "o", "p", "", "¤", "Ent" }, + { "q", "s", "d", "f", "g", "h", "j", "k", "l", "m", "", "", "Ent" }, + { "Shift", "", "w", "x", "c", "v", "b", "n", "", "", "", "", "Tab" }, + { "SLck", "", "", "", " ", " ", " ", " ", " ", "", "", "", "Sym" } + }, +} + +alias = { + ["Tab"] = "\t", + ["Ent"] = "\n", + ["Bks"] = "\b" +} + +sticky = { + ["SLck"] = "Shift" +} + +keys = { + ["l"] = "Shift", + ["r"] = "Shift" +} \ No newline at end of file diff --git a/sdcard/3ds/ctruLua/libs/keyboard.lua b/sdcard/3ds/ctruLua/libs/keyboard.lua index af34714..d398194 100644 --- a/sdcard/3ds/ctruLua/libs/keyboard.lua +++ b/sdcard/3ds/ctruLua/libs/keyboard.lua @@ -1,44 +1,12 @@ +local ctr = require("ctr") local hid = require("ctr.hid") local gfx = require("ctr.gfx") local hex = gfx.color.hex -- Options -local keyWidth, keyHeight = 25, 25 -local layout = { - ["default"] = { - { "&", "é", "\"", "'", "(", "-", "è", "_", "ç", "à", ")", "=", "Back" }, - { "a", "z", "e", "r", "t", "y", "u", "i", "o", "p", "^", "$", "Enter" }, - { "q", "s", "d", "f", "g", "h", "j", "k", "l", "m", "ù", "*", "Enter" }, - { "Shift", "<", "w", "x", "c", "v", "b", "n", ",", ";", ":", "!", "Tab" }, - { "CpLck", ">", "+", "/", " ", " ", " ", " ", " ", "{", "}", ".", "AltGr" } - }, - ["Shift"] = { - { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "°", "+", "Back" }, - { "A", "Z", "E", "R", "T", "Y", "U", "I", "O", "P", "¨", "£", "Enter" }, - { "Q", "S", "D", "F", "G", "H", "J", "K", "L", "M", "%", "µ", "Enter" }, - { "Shift", ">", "W", "X", "C", "V", "B", "N", "?", ".", "/", "§", "Tab" }, - { "CpLck", "~", "#", "[", " ", " ", " ", " ", " ", "]", "|", "@", "AltGr" } - }, - ["AltGr"] = { - { "²", "~", "#", "{", "[", "|", "`", "\\", "^", "@", "]", "}", "Back" }, - { "a", "z", "€", "r", "t", "y", "u", "i", "o", "p", "", "¤", "Enter" }, - { "q", "s", "d", "f", "g", "h", "j", "k", "l", "m", "", "", "Enter" }, - { "Shift", "", "w", "x", "c", "v", "b", "n", "", "", "", "", "Tab" }, - { "CpLck", "", "", "", " ", " ", " ", " ", " ", "", "", "", "AltGr" } - }, -} -local alias = { - ["Tab"] = "\t", - ["Enter"] = "\n", - ["Back"] = "BACK" -} -local sticky = { - ["CpLck"] = "Shift" -} -local keys = { - ["l"] = "Shift", - ["r"] = "Shift" -} + +local config = {} +loadfile(ctr.root .. "config/keyboard.cfg", nil, config)() -- Variables local currentModifier = { "default", "sticky" } @@ -51,7 +19,7 @@ return { local xTouch, yTouch if hidKeys.down.touch then xTouch, yTouch = hid.touch() end - for key, modifier in pairs(keys) do + for key, modifier in pairs(config.keys) do if hidKeys.down[key] then currentModifier = { modifier, "key" } elseif hidKeys.up[key] and currentModifier[2] == "key" and currentModifier[1] == modifier then @@ -59,27 +27,27 @@ return { end end - for row, rowKeys in pairs(layout[currentModifier[1]]) do + for row, rowKeys in pairs(config.layout[currentModifier[1]]) do for column, key in pairs(rowKeys) do - local xKey, yKey = x + (column-1)*(keyWidth-1), y + (row-1)*(keyHeight-1) + local xKey, yKey = x + (column-1)*(config.keyWidth-1), y + (row-1)*(config.keyHeight-1) - gfx.rectangle(xKey, yKey, keyWidth, keyHeight, 0, hex(0xFFFFFFFF)) - gfx.rectangle(xKey + 1, yKey + 1, keyWidth - 2, keyHeight - 2, 0, hex(0x000000FF)) + gfx.rectangle(xKey, yKey, config.keyWidth, config.keyHeight, 0, hex(0xFFFFFFFF)) + gfx.rectangle(xKey + 1, yKey + 1, config.keyWidth - 2, config.keyHeight - 2, 0, hex(0x000000FF)) gfx.text(xKey + 2, yKey + 2, key) if xTouch then - if xTouch > xKey and xTouch < xKey + keyWidth then - if yTouch > yKey and yTouch < yKey + keyHeight then - gfx.rectangle(xKey, yKey, keyWidth, keyHeight, 0, hex(0xDDFFFFFF)) + if xTouch > xKey and xTouch < xKey + config.keyWidth then + if yTouch > yKey and yTouch < yKey + config.keyHeight then + gfx.rectangle(xKey, yKey, config.keyWidth, config.keyHeight, 0, hex(0xDDFFFFFF)) - local k = alias[key] or key - if sticky[k] and layout[sticky[k]] then - if currentModifier[1] == sticky[k] and currentModifier[2] == "sticky" then + local k = config.alias[key] or key + if config.sticky[k] and config.layout[config.sticky[k]] then + if currentModifier[1] == config.sticky[k] and currentModifier[2] == "sticky" then currentModifier = { "default", "sticky" } else - currentModifier = { sticky[k], "sticky" } + currentModifier = { config.sticky[k], "sticky" } end - elseif layout[k] then + elseif config.layout[k] then if currentModifier[1] == k and currentModifier[2] == "normal" then currentModifier = { "default", "sticky" } else From 4669a684027b2ac6f83f4c4d0a6c0419536abcfd Mon Sep 17 00:00:00 2001 From: Neil Zeke Cecchini Date: Sat, 2 Apr 2016 18:31:32 +0200 Subject: [PATCH 077/101] Updated filepicker.lua and adding documentation Effectively rendering openfile.lua completely obsolete, which is why it was deleted. filepicker.lua is now way smarter, handles file creation and has a documentation file in LDoc. --- doc/config.ld | 2 +- doc/filepicker.md | 93 ++++++ sdcard/3ds/ctruLua/libs/filepicker.lua | 415 ++++++++++++++++--------- sdcard/3ds/ctruLua/libs/openfile.lua | 156 ---------- 4 files changed, 361 insertions(+), 305 deletions(-) create mode 100644 doc/filepicker.md delete mode 100644 sdcard/3ds/ctruLua/libs/openfile.lua diff --git a/doc/config.ld b/doc/config.ld index 13c1958..cfecb59 100644 --- a/doc/config.ld +++ b/doc/config.ld @@ -11,7 +11,7 @@ format = "markdown" plain = true -- Input files -topics = "../README.md" +topics = {"../README.md", "filepicker.md"} file = "../source/" examples = "../sdcard/3ds/ctruLua/examples/" manual_url = "file://../libs/lua-5.3.1/doc/manual.html" diff --git a/doc/filepicker.md b/doc/filepicker.md new file mode 100644 index 0000000..7f89590 --- /dev/null +++ b/doc/filepicker.md @@ -0,0 +1,93 @@ +# filepicker +## filePicker([workingDirectory[, bindings[, callbacks[, ...]]]]) +### Argument: workingDirectory +The directory that shows up first in the file browser. +If this is nil, ctr.fs.getDirectory() is used. +The recommended form is sdmc:/path/ or romfs:/path/, but it can be a simple /path/ instead. +#### Possible values +- string +- nil + +### Argument: bindings +A table, list of filetypes and key bindings related to these filetypes. +#### Format +``` +{ + __default, __directory, [Lua regexp] = { + [keys from ctr.hid.keys()] = { + function + string + }... + __name = string + }... +} +``` + +The Lua regexp is matched against the filename to determine if it is of this type. +__directory is the "file type" for directories +__default is the "file type" for files that cannot be matched with any other. + Also, every other type inherits the values it doesn't define from __default. +A file type contains the human-readable name (__name) of the type, displayed on the bottom screen, + as well as an optional binding for each key. +The optional binding is formed of an anonymous function, followed by the key's label to be displayed on the bottom screen, + if the label is nil, the key isn't displayed but still bound. + +The function is defined as-is: +##### function(externalConfig, selected, bindingPattern, bindingKey) +externalConfig.workingDirectory is the active directory for filePicker, doesn't necessarily match ctrµLua's. +externalConfig.bindings, externalConfig.callbacks and externalConfig.additionalArguments all are the arguments passed to filePicker, + starting from position 2. +externalConfig.fileList is the list of files currently displayed by filePicker, in the same format as is returned by ctr.fs.listDirectory(). +selected.inList is the absolute position of the cursor in externalConfig.fileList +selected.offset is the number of items skipped for display from fileList +bindingPattern is the [Lua regexp] defined earlier, and bindingKey the [key] that triggered this event. +This function may return the same thing as filePicker itself (Defined later here), or nothing. If it returns nothing, +filePicker will keep running, if it returns the same returns as filePicker, filePicker will exit, returning these values. + +#### Notes +Sane defaults are set if you did not set them otherwise: +__default.x quits filePicker, returning the current directory, "__directory", nil and "x". See the returns to understand what this means. +__directory.a changes directories to that directory. + +### Argument: callbacks +A table defining the callbacks ran at the end of each of the equivalent phases. +#### Format +``` +{ + drawTop, drawBottom, eventHandler = function +} +``` +All of these take the following parameters: (externalConfig, selected) +They have the meaning defined earlier. + +#### Notes +Although drawTop and drawBottom are ran at the end of their respective functions, +eventHandler is not, as it cannot be without being run repeatedly, so it's run at the beginning of +the ACTUAL eventHandler instead of its end. + +### Argument: ... +Additional parameters. All of these are aggregated orderly in externalConfig.additionalArguments and passed around as explained earlier. +They have no specific meaning unless defined so by event handling functions. + +### Return 1: selectedPath +The path selected by the either, may or may not have the sdmc:/romfs: prefix, depending on your input. +A string. + +### Return 2: bindingPattern +The pattern the file matched to. You can use this to know exactly which kind of file you're dealing with +A string. + +### Return 3: mode +Included handlers may have it be "open", in case you're opening an existing file, "new" in case the user wants to create a new file, +Or nil. A "nil" is assumed to mean that the user didn't pick anything. +A string or nil. + +### Return 4: key +The key that triggered the event that made filePicker exit. +A string. + +## Included event handlers you have available are: +- filepicker.changeDirectory - The name is on the tin, change workingDirectory to the active element and refresh the file list for that path. +- filepicker.openFile - Quits and returns as described by this document based on the active element. +- filepicker.newFile - Prompts the user to input a file name manually, relative to the current working directory, and with FAT-incompatible characters excluded. Quits and returns that. +- filepicker.nothing - Do nothing and keep on running. Literally. This is used as a plug to enable an action for all (or a certain type) but files of a certain type (or more precise than the initial type). \ No newline at end of file diff --git a/sdcard/3ds/ctruLua/libs/filepicker.lua b/sdcard/3ds/ctruLua/libs/filepicker.lua index e2b954a..e4ebd42 100644 --- a/sdcard/3ds/ctruLua/libs/filepicker.lua +++ b/sdcard/3ds/ctruLua/libs/filepicker.lua @@ -1,12 +1,13 @@ --- LSH version 0.1 --- ctrµLua official shell +local ctr = require('ctr') +local keyboard = require('keyboard') -local ctr = require("ctr") -local gfx = require("ctr.gfx") +local gfx = ctr.gfx -local function saveGraphicsState() +local externalConfig + +local function gfxPrepare() local old = {gfx.get3D(), gfx.color.getDefault(), gfx.color.getBackground(), - gfx.font.getDefault()} + gfx.font.getDefault(), gfx.getTextSize()} local mono = gfx.font.load(ctr.root .. "resources/VeraMono.ttf") @@ -14,186 +15,304 @@ local function saveGraphicsState() gfx.color.setDefault(0xFFFDFDFD) gfx.color.setBackground(0xFF333333) gfx.font.setDefault(mono) + gfx.setTextSize(12) return old end -local function restoreGraphicsState(state) +local function gfxRestore(state) gfx.set3D(state[1]) gfx.color.setDefault(state[2]) gfx.color.setBackground(state[3]) gfx.font.setDefault(state[4]) + gfx.setTextSize(state[5]) end -local function getExtension(sel, bindings) - for _, ext in ipairs(bindings) do - if ext.ext == sel:match("%..+$") then - return ext +local function systemBindings(bindings) + bindings.__default.up = { + function(_, selected, ...) + if selected.inList > 1 then + selected.inList = selected.inList - 1 + if selected.inList == selected.offset then + selected.offset = selected.offset - 1 + end + end end - end -end + } -local function getFilelist(cur) - local files = ctr.fs.list(cur) - - if cur ~= "/" and cur ~= "sdmc:/" then - table.insert(files, {name = "..", isDirectory = true}) - end - - -- Stealy stealing code from original openfile.lua - table.sort(files, function(i, j) - if i.isDirectory and not j.isDirectory then - return true - elseif i.isDirectory == j.isDirectory then - return string.lower(i.name) < string.lower(j.name) + bindings.__default.down = { + function(externalConfig, selected, ...) + if selected.inList < #externalConfig.fileList then + selected.inList = selected.inList + 1 + if selected.inList - selected.offset >= 16 then + selected.offset = selected.offset + 1 + end + end end - end) + } - return files + bindings.__default.left = { + function(_, selected, ...) + selected.inList, selected.offset = 1, 0 + end + } + + bindings.__default.right = { + function(externalConfig, selected, ...) + selected.inList = #externalConfig.fileList + if #externalConfig.fileList > 15 then + selected.offset = #externalConfig.fileList - 16 + end + end + } end -local function drawBottom(cur, selFile, bindings) - local ext = getExtension(selFile.name, bindings) +local function getFileList(workingDirectory) + local fileList = ctr.fs.list(workingDirectory) + if workingDirectory ~= "/" and workingDirectory ~= "sdmc:/" then + table.insert(fileList, {name = "..", isDirectory = true}) + end + + -- Stealy stealing code from original openfile.lua + table.sort(fileList, function(i, j) + if i.isDirectory and not j.isDirectory then + return true + elseif i.isDirectory == j.isDirectory then + return string.lower(i.name) < string.lower(j.name) + end + end) + + return fileList +end + +local function getBinding(selectedFile, bindings) + if selectedFile.isDirectory then + return bindings.__directory, "__directory" + end + for pattern, values in pairs(bindings) do + if selectedFile.name:match(pattern) then + return values, pattern + end + end + return bindings.__default, "__default" +end + +local function drawBottom(externalConfig, workingDirectoryScroll, selected) + local workingDirectory = externalConfig.workingDirectory + local bindings = externalConfig.bindings + local selectedFile = externalConfig.fileList[selected.inList] gfx.start(gfx.BOTTOM) gfx.rectangle(0, 0, gfx.BOTTOM_WIDTH, 16, 0, 0xFF0000B3) - gfx.text(1, 0, cur, 12) - gfx.text(1, 15, selFile.name, 12) - if not selFile.isDirectory then - gfx.text(1, 45, selFile.fileSize, 12) + gfx.text(1 - workingDirectoryScroll.value, 0, workingDirectory) + if gfx.font.getDefault():width(workingDirectory) > gfx.BOTTOM_WIDTH - 2 then + workingDirectoryScroll.value = workingDirectoryScroll.value + workingDirectoryScroll.phase + if workingDirectoryScroll.value == (gfx.BOTTOM_WIDTH - 2) - gfx.font.getDefault():width(workingDirectory) or + workingDirectoryScroll.value == 0 then + workingDirectoryScroll.phase = - workingDirectoryScroll.phase + end end - local keys = {"X: Quit/Cancel"} - if selFile.isDirectory then - gfx.text(1, 30, "Directory", 12, 0xFF727272) - gfx.text(1, gfx.BOTTOM_HEIGHT - 30, "A: Open", 12) - gfx.text(1, gfx.BOTTOM_HEIGHT - 15, keys[1], 12) - elseif ext then - local lines = 1 + gfx.text(1, 15, selectedFile.name, 12) + if not selectedFile.isDirectory then + gfx.text(1, 45, tostring(selectedFile.fileSize) .. "B", 12, 0xFF727272) + end - -- Keys - if ext.y then - lines = lines + 1 - table.insert(keys, "Y: " .. ext.y) - end - if ext.a then - lines = lines + 1 - table.insert(keys, "A: " .. ext.a) - end - - -- Drawing - for i=lines, 1, -1 do - gfx.text(1, gfx.BOTTOM_HEIGHT - 15*i, keys[i], 12) - end - gfx.text(1, 30, ext.name, 12, 0xFF727272) - gfx.text(1, 45, tostring(selFile.fileSize) .. "B", 12, 0xFF727272) + local binding, pattern = getBinding(selectedFile, bindings) + if selectedFile.isDirectory then + gfx.text(1, 30, bindings.__directory.__name, 12, 0xFF727272) else - gfx.text(1, 30, "File", 12, 0xFF727272) - gfx.text(1, 45, tostring(selFile.fileSize) .. "B", 12, 0xFF727272) - gfx.text(1, gfx.BOTTOM_HEIGHT - 15, keys[1], 12) + gfx.text(1, 30, binding.__name, 12, 0xFF727272) end + + local bindingNames = { + {"start", "Start"}, {"select", "Select"}, + {"x", "X"}, {"y", "Y"}, + {"b", "B"}, {"a", "A"}, + {"r", "R"}, {"l", "L"}, + {"zr", "ZR"}, {"zl", "ZL"}, + {"cstickDown", "C Down"}, {"cstickUp", "C Up"}, + {"cstickRight", "C Right"}, {"cstickLeft", "C Left"} + } + + local j = 0 + + for i, v in ipairs(bindingNames) do + if binding[v[1]] and binding[v[1]][2] then + j = j + 1 + gfx.text(1, gfx.BOTTOM_HEIGHT - 15*j, v[2] .. ": " .. binding[v[1]][2]) + end + end + + externalConfig.callbacks.drawBottom(externalConfig, selected) gfx.stop() end -local function drawTop(files, sel, scr) - gfx.start(gfx.TOP) - gfx.rectangle(0, (sel-scr-1)*15, gfx.TOP_WIDTH, 16, 0, 0xFF0000B3) - local over = #files - scr >= 16 and 16 or #files - scr - for i=scr+1, scr+over do - local color = files[i].isDirectory and 0xFF727272 or 0xFFFDFDFD - gfx.text(1, (i-scr-1)*15+1, files[i].name or "", 12, color) +local function drawTop(externalConfig, selected) + gfx.start(gfx.TOP) + gfx.rectangle(0, (selected.inList-selected.offset-1)*15, gfx.TOP_WIDTH, 16, 0, 0xFF0000B9) + local over = #externalConfig.fileList - selected.offset >= 16 and 16 or #externalConfig.fileList - selected.offset + for i=selected.offset+1, selected.offset+over do + local color = externalConfig.fileList[i].isDirectory and 0xFF727272 or 0xFFFDFDFD + gfx.text(1, (i-selected.offset-1)*15+1, externalConfig.fileList[i].name or "", 12, color) + end + externalConfig.callbacks.drawTop(externalConfig, selected) + gfx.stop() +end + +local function eventHandler(externalConfig, selected) + externalConfig.callbacks.eventHandler(externalConfig, selected) + ctr.hid.read() + local state = ctr.hid.keys() + local binding, pattern = getBinding(externalConfig.fileList[selected.inList], externalConfig.bindings) + for k, v in pairs(binding) do + if k ~= "__name" and state.down[k] then + local a, b, c, key = v[1](externalConfig, selected, pattern, k) + if key then return a, b, c, key + else return end + end + end + for k, v in pairs(externalConfig.bindings.__default) do + if k ~= "__name" and state.down[k] then + local a, b, c, key = v[1](externalConfig, selected, pattern, k) + if key then return a, b, c, key + else break end end - gfx.stop() -end - -local function runA(cur, selFile, bindings) - if not selFile.isDirectory then - local ext = getExtension(selFile.name, bindings) - if not ext then return end - if ext.a then return cur .. selFile.name, ext.ext end end end -local function runY(cur, selFile, bindings) - if not selFile.isDirectory then - local ext = getExtension(selFile.name, bindings) - if not ext then return end - if ext.y then return cur .. selFile.name, ext.ext end +local function nothing(externalConfig, selected, bindingName, bindingKey) + -- externalConfig = {workingDirectory=string, bindings=table, callbacks=table, additionalArguments=table, fileList=table} + -- selected = {file=string, inList=number, offset=number} + -- bindings = {__default/__directory/[regex] = {__name, [keyName] = {(handlingFunction), (name)}}} + -- callbacks = {drawTop, drawBottom, eventHandler} +end + +local function changeDirectory(externalConfig, selected, bindingName, bindingKey) + if externalConfig.fileList[selected.inList].isDirectory then + if externalConfig.fileList[selected.inList].name == ".." then + externalConfig.workingDirectory = externalConfig.workingDirectory:gsub("[^/]+/$", "") + else + externalConfig.workingDirectory = externalConfig.workingDirectory .. externalConfig.fileList[selected.inList].name .. "/" + end + externalConfig.fileList = getFileList(externalConfig.workingDirectory) + selected.inList, selected.offset = 1, 0 end end ---- Open a file browser to allow the user to select a file. --- It will save current graphical settings and set them back after ending. Press up or down to move one element at a time, and press left or right to go at the beginning or at the end of the list. This function is the return result of requiring filepicker.lua --- @name filePicker --- @param bindings A table of the extensions the user can select in the format {{name, ext, a, y},...}, name will show up instead of "File" or "Directory" on the bottom screen, a and y set the action names for those keys on the bottom screen (and also enable them, so if there's neither a or y, the file will have a custom type name but won't be effectively selectable), and ext is the extension to search for, dot included. Everything must be strings. --- @param workdir Optional, current working directory will be used if not specified, otherwise, sets the path at which the file browser first shows up, a string. --- @returns The absolute path to the file, nil in case no file was picked. --- @returns The extension of the file, this might be helpful in cases were multiple file types could be expected, nil in case no file was picked. --- @returns The "mode", which indicates which key was used to select the file, "A" or "Y". "X" in case no file was picked. -return function(bindings, workdir) - -- Initialization - local old = saveGraphicsState() - local cur = workdir or ctr.fs.getDirectory() - if cur:sub(-1) ~= "/" then - cur = cur .. "/" - end - local bindings = bindings or {} - - local files = getFilelist(cur) or {{name = "- Empty -"}} - local sel = 1 - local scr = 0 - +local function newFile(externalConfig, selected, bindingName, bindingKey) + local name = "" while ctr.run() do - drawBottom(cur, files[sel], bindings) - drawTop(files, sel, scr) + gfx.start(gfx.BOTTOM) + gfx.rectangle(0, 0, gfx.BOTTOM_WIDTH, 16, 0, 0xFF0000B3) + gfx.text(1, 0, externalConfig.workingDirectory) + keyboard.draw(4, 115) + gfx.stop() + + gfx.start(gfx.TOP) + gfx.rectangle(0, 0, gfx.TOP_WIDTH, 16, 0, 0xFF0000B3) + gfx.text(1, 0, "Creating new file") + gfx.rectangle(4, gfx.TOP_HEIGHT // 2 - 15, gfx.TOP_WIDTH - 8, 30, 0, 0xFF727272) + gfx.text(5, gfx.TOP_HEIGHT // 2 - 6, name, 12) + gfx.stop() gfx.render() + local char = (keyboard.read() or ""):gsub("[\t%/%?%<%>%\\%:%*%|%”%^]", "") ctr.hid.read() - local state = ctr.hid.keys() - if (state.down.dDown or state.down.cpadDown) and sel < #files then - sel = sel + 1 - if sel - scr >= 16 then - scr = scr + 1 - end - elseif (state.down.dUp or state.down.cpadUp) and sel > 1 then - sel = sel - 1 - if sel == scr then - scr = scr - 1 - end - elseif state.down.dLeft or state.down.cpadLeft then - sel = 1 - scr = 0 - elseif state.down.dRight or state.down.cpadRight then - sel = #files - if #files > 15 then - scr = #files - 16 - end + local keys = ctr.hid.keys() - elseif state.down.a then - local selFile = files[sel] - if selFile.isDirectory then - if selFile.name == ".." then - cur = cur:gsub("[^/]+/$", "") - else - cur = cur .. selFile.name .. "/" - end - files, sel, scr = getFilelist(cur), 1, 0 - else - local file, ext = runA(cur, selFile, bindings) - if file then - restoreGraphicsState(old) - return file, ext, "A" - end - end - elseif state.down.y then - local file, ext = runY(cur, files[sel], bindings) - if file then - restoreGraphicsState(old) - return file, ext, "Y" - end - elseif state.down.x then - restoreGraphicsState(old) - return nil, nil, "X" + if char ~= "" and char ~= "\b" and char ~= "\n" then + name = name .. char + elseif char ~= "" and char == "\b" then + name = name:sub(1, (utf8.offset(name, -1) or 0)-1) + elseif (char ~= "" and char == "\n" or keys.down.a) and name ~= "" then + local b, p = getBinding({name=name}, externalConfig.bindings) + return externalConfig.workingDirectory .. name, p, "new", b + elseif keys.down.b then + break end end -end \ No newline at end of file +end + +local function openFile(externalConfig, selected, bindingName, bindingKey) + return externalConfig.workingDirectory .. externalConfig.fileList[selected.inList].name, + bindingName, "open", bindingKey +end + +local function filePicker(workingDirectory, bindings, callbacks, ...) + -- Argument sanitization + local additionalArguments = { ... } + workingDirectory = workingDirectory or ctr.fs.getDirectory() + bindings = bindings or {} + callbacks = callbacks or {} + for _, v in ipairs({"drawTop", "drawBottom", "eventHandler"}) do + if not callbacks[v] then + callbacks[v] = function(...) end + end + end + + if workingDirectory:sub(utf8.offset(workingDirectory, -1) or -1) ~= "/" then + workingDirectory = workingDirectory .. "/" + end + + -- Default Bindings + bindings.__default = bindings.__default or {} + bindings.__default.__name = bindings.__default.__name or "File" + bindings.__default.x = bindings.__default.x or { + function(externalConfig, ...) + return externalConfig.workingDirectory, "__directory", nil, "x" + end, "Quit" + } + + bindings.__directory = bindings.__directory or {} + bindings.__directory.__name = bindings.__directory.__name or "Directory" + bindings.__directory.a = bindings.__directory.a or { + changeDirectory, "Open" + } + + local movementKeys = { + "up" , "down" , "left" , "right" , + "cpadUp", "cpadDown", "cpadLeft", "cpadRight", + "dUp" , "dDown" , "dLeft" , "dRight" + } + + for k, v in pairs(bindings) do + if k ~= "__default" then + setmetatable(bindings[k], {__index = bindings.__default}) + end + + for _, w in ipairs(movementKeys) do + if v[w] then bindings[k][w] = nil end + end + end + + systemBindings(bindings) + + -- Other Initialization + local selected = {inList = 1, offset = 0} + local workingDirectoryScroll = { value = 0, phase = -1 } + local gfxState = gfxPrepare() + + -- Main Loop + externalConfig = {workingDirectory=workingDirectory, bindings=bindings, + callbacks=callbacks, additionalArguments=additionalArguments, + fileList=getFileList(workingDirectory)} + while ctr.run() do + drawBottom(externalConfig, workingDirectoryScroll, selected) + drawTop(externalConfig, selected) + gfx.render() + + local file, binding, mode, key = eventHandler(externalConfig, selected) + + if key then + gfxRestore(gfxState) + return file, binding, mode, key + end + end +end + +local returnTable = {filePicker = filePicker, openFile = openFile, + newFile = newFile, changeDirectory = changeDirectory} +setmetatable(returnTable, {__call = function(self, ...) return self.filePicker(...) end}) + +return returnTable \ No newline at end of file diff --git a/sdcard/3ds/ctruLua/libs/openfile.lua b/sdcard/3ds/ctruLua/libs/openfile.lua deleted file mode 100644 index 0137005..0000000 --- a/sdcard/3ds/ctruLua/libs/openfile.lua +++ /dev/null @@ -1,156 +0,0 @@ --- Sort ctr.fs.list returns (directories first and alphabetical sorting) -local function sort(files) - table.sort(files, function(i, j) - if i.isDirectory and not j.isDirectory then - return true - elseif i.isDirectory == j.isDirectory then - return string.lower(i.name) < string.lower(j.name) - end - end) - return files -end - ---- Open a file explorer to select a file. --- string title: title of the file explorer. --- string curdir: the directory to initially open the file explorer in, or nil for the current directory. --- string exts: the file extensions the user can select, separated by ";". If nil, all extensions are accepted. --- string type: "exist" to select an existing file, "new" to select an non-existing file or "any" to select a existing --- or non-existing file name. If nil, defaults to "exist". --- returns string: the file the user has selected, or nil if the explorer was closed without selecting a file. --- string: "exist" if the file exist or "new" if it doesn't -return function(title, curdir, exts, type) - -- Open libs - local ctr = require("ctr") - local gfx = require("ctr.gfx") - - local keyboard = require("keyboard") - - -- Arguments - local curdir = curdir or ctr.fs.getDirectory() - local type = type or "exist" - - -- Variables - local sel = 1 - local scroll = 0 - local files = sort(ctr.fs.list(curdir)) - - if curdir ~= "/" then table.insert(files, 1, { name = "..", isDirectory = true }) end - local newFileName = "" - - local ret = nil - - -- Remember and set defaults - local was3D = gfx.get3D() - local wasDefault = gfx.color.getDefault() - local wasBackground = gfx.color.getBackground() - local wasFont = gfx.font.getDefault() - gfx.set3D(false) - gfx.color.setDefault(0xFFFFFFFF) - gfx.color.setBackground(0xFF000000) - gfx.font.setDefault() - - while ctr.run() do - ctr.hid.read() - local keys = ctr.hid.keys() - if keys.down.start then break end - - -- Keys input - if keys.down.down and sel < #files then - sel = sel + 1 - if sel > scroll + 14 then scroll = scroll + 1 end - elseif keys.down.up and sel > 1 then - sel = sel - 1 - if sel <= scroll then scroll = scroll - 1 end - end - - if keys.down.a then - local f = files[sel] - - if f.isDirectory then - if f.name == ".." then curdir = curdir:gsub("[^/]+/$", "") - else curdir = curdir..f.name.."/" end - - sel = 1 - scroll = 0 - files = sort(ctr.fs.list(curdir)) - - if curdir ~= "/" then - table.insert(files, 1, { name = "..", isDirectory = true }) - end - elseif type == "exist" or type == "any" then - if exts then - for ext in (exts..";"):gmatch("[^;]+") do - if f.name:match("%..+$") == ext then - ret = { curdir..f.name, "exist" } - break - end - end - else - ret = { curdir..f.name, "exist" } - end - if ret then break end - end - end - - -- Keyboard input - if type == "new" or type == "any" then - local input = keyboard.read() - if input then - if input == "BACK" then - newFileName = newFileName:sub(1, (utf8.offset(newFileName, -1) or 0)-1) - elseif input == "\n" then - local fileStatus = "new" - local f = io.open(curdir..newFileName) - if f then fileStatus = "exist" f:close() end - ret = { curdir..newFileName, fileStatus } - break - else - newFileName = newFileName..input - end - end - end - - -- Draw - gfx.start(gfx.TOP) - - gfx.rectangle(0, 10+(sel-scroll)*15, gfx.TOP_WIDTH, 15, 0, gfx.color.RGBA8(0, 0, 200)) - - for i = scroll+1, scroll+14, 1 do - local f = files[i] - if not f then break end - local name = f.isDirectory and "["..f.name.."]" or f.name.." ("..f.fileSize.."b)" - if not f.isHidden then gfx.text(5, 12+(i-scroll)*15, name) end - end - - gfx.rectangle(0, 0, gfx.TOP_WIDTH, 25, 0, gfx.color.RGBA8(200, 200, 200)) - gfx.text(3, 3, curdir, 13, gfx.color.RGBA8(0, 0, 0)) - - gfx.stop() - - gfx.start(gfx.BOTTOM) - - gfx.text(5, 5, title) - gfx.text(5, 20, "Accepted file extensions: "..(exts or "all")) - - if type == "new" or type == "any" then - gfx.text(5, 90, newFileName) - keyboard.draw(5, 115) - end - - gfx.stop() - - gfx.render() - end - - -- Reset defaults - gfx.set3D(was3D) - gfx.color.setDefault(wasDefault) - gfx.color.setBackground(wasBackground) - gfx.font.setDefault(wasFont) - - if ret then - return table.unpack(ret) - else - return ret - end -end From 05c9adc2a07770772cbaa2478ca22606295970ce Mon Sep 17 00:00:00 2001 From: Neil Zeke Cecchini Date: Sat, 2 Apr 2016 18:36:27 +0200 Subject: [PATCH 078/101] Updated apps that rely on file selection The editor doesn't use openfile.lua anymore, the main shell is updated to use filepicker.filePicker's new format. Also, updated the editor to use resources/VeraMono.ttf instead of its own copy, which was deleted. --- sdcard/3ds/ctruLua/editor/VeraMono.ttf | Bin 49224 -> 0 bytes sdcard/3ds/ctruLua/editor/main.lua | 18 ++++++++++------- sdcard/3ds/ctruLua/main.lua | 27 ++++++++++++++++--------- 3 files changed, 28 insertions(+), 17 deletions(-) delete mode 100644 sdcard/3ds/ctruLua/editor/VeraMono.ttf diff --git a/sdcard/3ds/ctruLua/editor/VeraMono.ttf b/sdcard/3ds/ctruLua/editor/VeraMono.ttf deleted file mode 100644 index 139f0b4311ad2e0369a347b3be6c46e6c2b730d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49224 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 diff --git a/sdcard/3ds/ctruLua/editor/main.lua b/sdcard/3ds/ctruLua/editor/main.lua index 64c9145..1831568 100644 --- a/sdcard/3ds/ctruLua/editor/main.lua +++ b/sdcard/3ds/ctruLua/editor/main.lua @@ -4,19 +4,23 @@ local gfx = require("ctr.gfx") -- Open libs local keyboard = require("keyboard") -local openfile = require("openfile") +local filepicker = require("filepicker") local color = dofile("color.lua") local syntax = dofile("syntax.lua") -- Load data -local font = gfx.font.load("VeraMono.ttf") +local font = gfx.font.load(ctr.root .. "resources/VeraMono.ttf") -- Open file -local path, status = openfile("Choose a file to edit", nil, nil, "any") -if not path then return end +local path, binding, mode, key = filepicker(nil, {__default = { + a = {filepicker.openFile, "Open"}, + y = {filepicker.newFile, "New File"} + } +}) +if not mode then return end local lineEnding local lines = {} -if status == "exist" then +if mode == "open" then for line in io.lines(path, "L") do if not lineEnding then lineEnding = line:match("([\n\r]+)$") end table.insert(lines, line:match("^(.-)[\n\r]*$")) @@ -110,7 +114,7 @@ while ctr.run() do -- Keyboard input local input = keyboard.read() if input then - if input == "BACK" then + if input == "\b" then if cursorX > utf8.len(lines[cursorY])+1 then cursorX = utf8.len(lines[cursorY])+1 end if cursorX > 1 then lines[cursorY] = lines[cursorY]:sub(1, utf8.offset(lines[cursorY], cursorX-1)-1).. @@ -173,7 +177,7 @@ while ctr.run() do gfx.text(3, 3, "FPS: "..math.ceil(gfx.getFPS())) - keyboard.draw(5, 115) + keyboard.draw(4, 115) gfx.stop() diff --git a/sdcard/3ds/ctruLua/main.lua b/sdcard/3ds/ctruLua/main.lua index 44c840e..bea7791 100644 --- a/sdcard/3ds/ctruLua/main.lua +++ b/sdcard/3ds/ctruLua/main.lua @@ -5,21 +5,24 @@ local gfx = require("ctr.gfx") -- Set up path local ldir = ctr.root.."libs/" package.path = package.path..";".. ldir.."?.lua;".. ldir.."?/init.lua" +local filepicker = require("filepicker") -- Erroring -local function displayError(err) +local function displayError(err, trace) gfx.set3D(false) gfx.color.setBackground(0xFF0000B3) gfx.color.setDefault(0xFFFDFDFD) + gfx.setTextSize(12) gfx.font.setDefault(gfx.font.load(ctr.root .. "resources/VeraMono.ttf")) while ctr.run() do - gfx.start(gfx.TOP) - gfx.text(1, 1, "An error has occured.", 12) - gfx.wrappedText(1, 30, err, gfx.TOP_WIDTH-2, 12) - gfx.text(1, gfx.TOP_HEIGHT-15, "Press Start to continue.", 12) - gfx.stop() gfx.start(gfx.BOTTOM) + gfx.text(1, 1, "An error has occured.") + gfx.wrappedText(1, 30, err, gfx.BOTTOM_WIDTH-2) + gfx.text(1, gfx.BOTTOM_HEIGHT-15, "Press Start to continue.") + gfx.stop() + gfx.start(gfx.TOP) + gfx.wrappedText(2, 6, trace, gfx.TOP_WIDTH - 2) gfx.stop() gfx.render() @@ -34,11 +37,15 @@ while ctr.run() do gfx.font.setDefault() gfx.color.setDefault(0xFFFDFDFD) gfx.color.setBackground(0xFF333333) - local file, ext, mode = require("filepicker")({{name="Lua Script", ext=".lua", a="Execute"}}) - if file and mode == "A" then + local file, ext, mode, key = filepicker(ctr.root, { + ["%.lua$"] = { + a = {filepicker.openFile, "Run"}, + __name = "Lua Script" + } + }) + if mode then fs.setDirectory(file:match("^(.-)[^/]*$")) - local ok, err = pcall(dofile, file) - if not ok then displayError(err) end + xpcall(dofile, function(err) displayError(err, debug.traceback()) end, file) else break end From 1f23b863799bbfd6a2a45f946a09e7d8de4436e5 Mon Sep 17 00:00:00 2001 From: Neil Zeke Cecchini Date: Sat, 2 Apr 2016 18:40:16 +0200 Subject: [PATCH 079/101] Changed the icon, and deleted the test files. The new one is much nicer. As for the test files, they had nothing to do here from the start. --- icon.png | Bin 1221 -> 1080 bytes sdcard/3ds/ctruLua/tests/httpc.lua | 37 ----------------------------- 2 files changed, 37 deletions(-) delete mode 100644 sdcard/3ds/ctruLua/tests/httpc.lua diff --git a/icon.png b/icon.png index f035c569cd89ff4b8459816f3caca0d15c34a1c7..5c7a62bbacadbb208110faac588d587af6806e44 100644 GIT binary patch delta 1049 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 FV1n Date: Sat, 2 Apr 2016 19:03:24 +0200 Subject: [PATCH 080/101] Fixed sublime-completions doc building --- doc/ldoc.ltp | 4 ++-- sdcard/3ds/ctruLua/libs/keyboard.lua | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/ldoc.ltp b/doc/ldoc.ltp index 600da3e..ca2ab5d 100644 --- a/doc/ldoc.ltp +++ b/doc/ldoc.ltp @@ -5,9 +5,9 @@ # -- Typical usage: ldoc . --template ./ --ext sublime-completions --dir ./sublimetext/ # # local scope = "source.lua" -# local function e(str) return str:gsub("\"", "\\\"") end -- escape json string ("str") +# local function e(str) return (str or ""):gsub("\"", "\\\"") end -- escape json string ("str") # local function indent(indentation, str) -- indent str (except first line) with indentation -# return str:gsub("(.-)\n", indentation.."%1\n"):gsub("\n([^\n]*)$", "\n"..indentation.."%1"):gsub("^"..indentation, "") +# return (str or ""):gsub("(.-)\n", indentation.."%1\n"):gsub("\n([^\n]*)$", "\n"..indentation.."%1"):gsub("^"..indentation, "") # end # local function displayName(item) return item.type == "function" and item.name..item.args or item.name end -- nice name # local function autocompleteName(item) -- ST-autocomplete name diff --git a/sdcard/3ds/ctruLua/libs/keyboard.lua b/sdcard/3ds/ctruLua/libs/keyboard.lua index d398194..b6bba84 100644 --- a/sdcard/3ds/ctruLua/libs/keyboard.lua +++ b/sdcard/3ds/ctruLua/libs/keyboard.lua @@ -1,10 +1,8 @@ -local ctr = require("ctr") local hid = require("ctr.hid") local gfx = require("ctr.gfx") local hex = gfx.color.hex -- Options - local config = {} loadfile(ctr.root .. "config/keyboard.cfg", nil, config)() From e87651a4046b21e77ec79841c700d3086797ce84 Mon Sep 17 00:00:00 2001 From: Reuh Date: Sat, 2 Apr 2016 19:09:40 +0200 Subject: [PATCH 081/101] Readd require('ctr') in keyboard --- sdcard/3ds/ctruLua/libs/keyboard.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/sdcard/3ds/ctruLua/libs/keyboard.lua b/sdcard/3ds/ctruLua/libs/keyboard.lua index b6bba84..e220779 100644 --- a/sdcard/3ds/ctruLua/libs/keyboard.lua +++ b/sdcard/3ds/ctruLua/libs/keyboard.lua @@ -1,3 +1,4 @@ +local ctr = require("ctr") local hid = require("ctr.hid") local gfx = require("ctr.gfx") local hex = gfx.color.hex From e7ff54d58c6d10366b1a8bf34da1487197cbaf35 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Sun, 10 Apr 2016 01:47:52 +0200 Subject: [PATCH 082/101] Added SSL support to sockets, Added console/stdout support, Removed apt.init/apt.shutdown, Added some SSL options to httpc The console can __not__ be used with gfx.start() on a screen. You don't have to gfx.render() while print()ing, but you should do it when you are in the main loop. The SSL sockets don't work with Citra. --- source/apt.c | 32 ++------ source/cfgu.c | 21 ++++- source/ctr.c | 12 ++- source/gfx.c | 49 ++++++++++++ source/httpc.c | 21 +++++ source/news.c | 21 ++++- source/ptm.c | 25 +++++- source/socket.c | 197 +++++++++++++++++++++++++++++++++++++++++++---- source/texture.c | 30 ++++---- 9 files changed, 333 insertions(+), 75 deletions(-) diff --git a/source/apt.c b/source/apt.c index 99a1fd1..e860736 100644 --- a/source/apt.c +++ b/source/apt.c @@ -13,30 +13,6 @@ Used to manage the applets and application status. #include #include -/*** -Initialize the APT module. Useless. -@function init -*/ -static int apt_init(lua_State *L) { - Result ret = aptInit(); - if (ret!=0) { - lua_pushboolean(L, false); - lua_pushinteger(L, ret); - return 2; - } - lua_pushboolean(L, true); - return 1; -} - -/*** -Shutdown the APT module. Useless, don't use it. -@function shutdown -*/ -static int apt_shutdown(lua_State *L) { - aptExit(); - return 0; -} - /*** Open an APT session. Should only work if you don't use the homebrew menu. @function openSession @@ -132,8 +108,6 @@ static int apt_getMenuAppID(lua_State *L) { } static const struct luaL_Reg apt_lib[] = { - {"init", apt_init }, - {"shutdown", apt_shutdown }, {"openSession", apt_openSession }, {"closeSession", apt_closeSession }, {"setStatus", apt_setStatus }, @@ -327,6 +301,8 @@ struct { char *name; int value; } apt_constants[] = { }; int luaopen_apt_lib(lua_State *L) { + aptInit(); + luaL_newlib(L, apt_lib); for (int i = 0; apt_constants[i].name; i++) { @@ -340,3 +316,7 @@ int luaopen_apt_lib(lua_State *L) { void load_apt_lib(lua_State *L) { luaL_requiref(L, "ctr.apt", luaopen_apt_lib, false); } + +void unload_apt_lib(lua_State *L) { + aptExit(); +} diff --git a/source/cfgu.c b/source/cfgu.c index 1700e47..5b2e23b 100644 --- a/source/cfgu.c +++ b/source/cfgu.c @@ -14,13 +14,17 @@ Used to get some user config. #include #include +bool initStateCFGU = false; + /*** Initialize the CFGU module. @function init */ static int cfgu_init(lua_State *L) { - cfguInit(); - + if (!initStateCFGU) { + cfguInit(); + initStateCFGU = true; + } return 0; } @@ -29,8 +33,10 @@ Disable the CFGU module. @function shutdown */ static int cfgu_shutdown(lua_State *L) { - cfguExit(); - + if (initStateCFGU) { + cfguExit(); + initStateCFGU = false; + } return 0; } @@ -309,3 +315,10 @@ int luaopen_cfgu_lib(lua_State *L) { void load_cfgu_lib(lua_State *L) { luaL_requiref(L, "ctr.cfgu", luaopen_cfgu_lib, false); } + +void unload_cfgu_lib(lua_State *L) { + if (initStateCFGU) { + initStateCFGU = false; + cfguExit(); + } +} diff --git a/source/ctr.c b/source/ctr.c index 49c93ba..366fc10 100644 --- a/source/ctr.c +++ b/source/ctr.c @@ -28,6 +28,7 @@ The `ctr.news` module. @see ctr.news */ void load_news_lib(lua_State *L); +void unload_news_lib(lua_State *L); /*** The `ctr.ptm` module. @@ -35,6 +36,7 @@ The `ctr.ptm` module. @see ctr.ptm */ void load_ptm_lib(lua_State *L); +void unload_ptm_lib(lua_State *L); /*** The `ctr.hid` module. @@ -80,6 +82,7 @@ The `ctr.cfgu` module. @see ctr.cfgu */ void load_cfgu_lib(lua_State *L); +void unload_cfgu_lib(lua_State *L); /*** The `ctr.socket` module. @@ -109,6 +112,7 @@ The `ctr.apt` module. @see ctr.apt */ void load_apt_lib(lua_State *L); +void unload_apt_lib(lua_State *L); /*** The `ctr.mic` module. @@ -168,18 +172,18 @@ static const struct luaL_Reg ctr_lib[] = { // Subtables struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } ctr_libs[] = { { "gfx", load_gfx_lib, unload_gfx_lib }, - { "news", load_news_lib, NULL }, - { "ptm", load_ptm_lib, NULL }, + { "news", load_news_lib, unload_news_lib }, + { "ptm", load_ptm_lib, unload_ptm_lib }, { "hid", load_hid_lib, unload_hid_lib }, { "ir", load_ir_lib, NULL }, { "fs", load_fs_lib, unload_fs_lib }, { "httpc", load_httpc_lib, unload_httpc_lib }, { "qtm", load_qtm_lib, NULL }, - { "cfgu", load_cfgu_lib, NULL }, + { "cfgu", load_cfgu_lib, unload_cfgu_lib }, { "socket", load_socket_lib, NULL }, { "cam", load_cam_lib, NULL }, { "audio", load_audio_lib, unload_audio_lib }, - { "apt", load_apt_lib, NULL }, + { "apt", load_apt_lib, unload_apt_lib }, { "mic", load_mic_lib, NULL }, { "thread", load_thread_lib, NULL }, { NULL, NULL, NULL } diff --git a/source/gfx.c b/source/gfx.c index ed6bfe5..82342bb 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -12,6 +12,7 @@ The `gfx` module. //#include <3ds/vram.h> //#include <3ds/services/gsp.h> +#include <3ds/console.h> #include #include @@ -524,6 +525,51 @@ static int gfx_target___index(lua_State *L) { return 1; } +/*** +Initialize the console. You can print on it using print(), or any function that normally outputs to stdout. +Warning: you can't use a screen for both a console and drawing, you have to disable the console first. +@function console +@tparam[opt=gfx.TOP] number screen screen to draw the console on. +@tparam[opt=true] boolean debug enable stderr output on the console +*/ +u8 consoleScreen = GFX_TOP; +static int gfx_console(lua_State *L) { + consoleScreen = luaL_optinteger(L, 1, GFX_TOP); + bool err = false; + if (lua_isboolean(L, 2)) { + err = lua_toboolean(L, 2); + } + + consoleInit(consoleScreen, NULL); + if (err) + consoleDebugInit(debugDevice_CONSOLE); + + return 0; +} + +/*** +Clear the console. +@function clearConsole +*/ +static int gfx_clearConsole(lua_State *L) { + consoleClear(); + + return 0; +} + +/*** +Disable the console. +@function disableConsole +*/ +static int gfx_disableConsole(lua_State *L) { + gfxSetScreenFormat(consoleScreen, GSP_BGR8_OES); + gfxSetDoubleBuffering(consoleScreen, true); + gfxSwapBuffersGpu(); + gspWaitForVBlank(); + + return 0; +} + // Functions static const struct luaL_Reg gfx_lib[] = { { "start", gfx_start }, @@ -546,6 +592,9 @@ static const struct luaL_Reg gfx_lib[] = { { "getTextSize", gfx_getTextSize }, { "scissor", gfx_scissor }, { "target", gfx_target }, + { "console", gfx_console }, + { "clearConsole", gfx_clearConsole }, + { "disableConsole", gfx_disableConsole }, { NULL, NULL } }; diff --git a/source/httpc.c b/source/httpc.c index 1df43bd..ef70afc 100644 --- a/source/httpc.c +++ b/source/httpc.c @@ -9,6 +9,7 @@ The `httpc` module. #include <3ds.h> #include <3ds/types.h> #include <3ds/services/httpc.h> +#include <3ds/services/sslc.h> #include #include @@ -239,6 +240,25 @@ static int httpc_addTrustedRootCA(lua_State *L) { return 1; } +/*** +Set SSL options for a context. +@function :setSSLOptions +@tparam boolean disableVerify disable server certificate verification if `true` +@tparam[opt=false] boolean tlsv10 use TLS v1.0 if `true` +*/ +static int httpc_setSSLOptions(lua_State *L) { + httpcContext *context = lua_touserdata(L, 1); + + bool disVer = lua_toboolean(L, 2); + bool tsl10 = false; + if (lua_isboolean(L, 3)) + tsl10 = lua_toboolean(L, 3); + + httpcSetSSLOpt(context, (disVer?SSLCOPT_DisableVerify:0)|(tsl10?SSLCOPT_TLSv10:0)); + + return 0; +} + // object static const struct luaL_Reg httpc_methods[] = { {"open", httpc_open }, @@ -252,6 +272,7 @@ static const struct luaL_Reg httpc_methods[] = { {"addPostData", httpc_addPostData }, {"getResponseHeader", httpc_getResponseHeader }, {"addTrustedRootCA", httpc_addTrustedRootCA }, + {"setSSLOptions", httpc_setSSLOptions }, {NULL, NULL} }; diff --git a/source/news.c b/source/news.c index f97e6d9..5afdcbe 100644 --- a/source/news.c +++ b/source/news.c @@ -13,13 +13,17 @@ The `news` module. #include #include +bool initStateNews = false; + /*** Initialize the news module. @function init */ static int news_init(lua_State *L) { - newsInit(); - + if (!initStateNews) { + newsInit(); + initStateNews = true; + } return 0; } @@ -63,8 +67,10 @@ Disable the news module. @function shutdown */ static int news_shutdown(lua_State *L) { - newsExit(); - + if (initStateNews) { + newsExit(); + initStateNews = false; + } return 0; } @@ -83,3 +89,10 @@ int luaopen_news_lib(lua_State *L) { void load_news_lib(lua_State *L) { luaL_requiref(L, "ctr.news", luaopen_news_lib, 0); } + +void unload_news_lib(lua_State *L) { + if (initStateNews) { + newsExit(); + initStateNews = false; + } +} diff --git a/source/ptm.c b/source/ptm.c index 8a902e9..438acde 100644 --- a/source/ptm.c +++ b/source/ptm.c @@ -10,13 +10,19 @@ The `ptm` module. #include #include +bool initStatePTM = false; + /*** Initialize the PTM module. @function init */ static int ptm_init(lua_State *L) { - ptmuInit(); - ptmSysmInit(); + if (!initStatePTM) { + ptmuInit(); + ptmSysmInit(); + + initStatePTM = true; + } return 0; } @@ -26,8 +32,12 @@ Disable the PTM module. @function shutdown */ static int ptm_shutdown(lua_State *L) { - ptmuExit(); - ptmSysmExit(); + if (initStatePTM) { + ptmuExit(); + ptmSysmExit(); + + initStatePTM = false; + } return 0; } @@ -146,3 +156,10 @@ int luaopen_ptm_lib(lua_State *L) { void load_ptm_lib(lua_State *L) { luaL_requiref(L, "ctr.ptm", luaopen_ptm_lib, 0); } + +void unload_ptm_lib(lua_State *L) { + if (initStatePTM) { + ptmuExit(); + ptmSysmExit(); + } +} diff --git a/source/socket.c b/source/socket.c index c4eef76..fc2325d 100644 --- a/source/socket.c +++ b/source/socket.c @@ -8,6 +8,7 @@ The UDP part is only without connection. #include <3ds.h> #include <3ds/types.h> #include <3ds/services/soc.h> +#include <3ds/services/sslc.h> #include #include @@ -15,7 +16,9 @@ The UDP part is only without connection. #include #include #include +#include #include +#include #include #include #include @@ -26,21 +29,58 @@ typedef struct { int socket; struct sockaddr_in addr; struct hostent *host; // only used for client sockets + sslcContext sslContext; + bool isSSL; } socket_userdata; +bool initStateSocket = false; + +u32 rootCertChain = 0; + /*** Initialize the socket module @function init @tparam[opt=0x100000] number buffer size (in bytes), must be a multiple of 0x1000 */ static int socket_init(lua_State *L) { - u32 size = luaL_optinteger(L, 1, 0x100000); - Result ret = socInit((u32*)memalign(0x1000, size), size); + if (!initStateSocket) { + u32 size = luaL_optinteger(L, 1, 0x100000); + if (size%0x1000 != 0) { + lua_pushboolean(L, false); + lua_pushstring(L, "Not a multiple of 0x1000"); + return 2; + } + + u32* mem = (u32*)memalign(0x1000, size); + if (mem == NULL) { + lua_pushboolean(L, false); + lua_pushstring(L, "Failed to allocate memory"); + return 2; + } + + Result ret = socInit(mem, size); - if (ret) { - lua_pushboolean(L, false); - lua_pushinteger(L, ret); - return 2; + if (ret) { + lua_pushboolean(L, false); + lua_pushinteger(L, ret); + return 2; + } + + ret = sslcInit(0); + if (R_FAILED(ret)) { + lua_pushboolean(L, false); + lua_pushinteger(L, ret); + return 2; + } + + sslcCreateRootCertChain(&rootCertChain); + sslcRootCertChainAddDefaultCert(rootCertChain, SSLC_DefaultRootCert_CyberTrust, NULL); + sslcRootCertChainAddDefaultCert(rootCertChain, SSLC_DefaultRootCert_AddTrust_External_CA, NULL); + sslcRootCertChainAddDefaultCert(rootCertChain, SSLC_DefaultRootCert_COMODO, NULL); + sslcRootCertChainAddDefaultCert(rootCertChain, SSLC_DefaultRootCert_USERTrust, NULL); + sslcRootCertChainAddDefaultCert(rootCertChain, SSLC_DefaultRootCert_DigiCert_EV, NULL); + + initStateSocket = true; } lua_pushboolean(L, true); @@ -52,8 +92,11 @@ Disable the socket module. Must be called before exiting ctrµLua. @function shutdown */ static int socket_shutdown(lua_State *L) { + sslcDestroyRootCertChain(rootCertChain); + sslcExit(); socExit(); + initStateSocket = false; return 0; } @@ -76,6 +119,8 @@ static int socket_tcp(lua_State *L) { userdata->addr.sin_family = AF_INET; + userdata->isSSL = false; + return 1; } @@ -92,15 +137,36 @@ static int socket_udp(lua_State *L) { userdata->socket = socket(AF_INET, SOCK_DGRAM, 0); if (userdata->socket < 0) { lua_pushnil(L); - lua_pushstring(L, "Failed to create a TCP socket"); + lua_pushstring(L, strerror(errno)); return 2; } + fcntl(userdata->socket, F_SETFL, O_NONBLOCK); userdata->addr.sin_family = AF_INET; return 1; } +/*** +Add a trusted root CA to the certChain. +@function addTrustedRootCA +@tparam string cert DER cert +*/ +static int socket_addTrustedRootCA(lua_State *L) { + size_t size = 0; + const char* cert = luaL_checklstring(L, 1, &size); + + Result ret = sslcAddTrustedRootCA(rootCertChain, (u8*)cert, size, NULL); + if (R_FAILED(ret)) { + lua_pushnil(L); + lua_pushinteger(L, ret); + return 2; + } + + lua_pushboolean(L, true); + return 1; +} + /*** All sockets @section sockets @@ -130,6 +196,10 @@ Close an existing socket. static int socket_close(lua_State *L) { socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); + if (userdata->isSSL) { + sslcDestroyContext(&userdata->sslContext); + } + closesocket(userdata->socket); return 0; @@ -158,6 +228,7 @@ static int socket_accept(lua_State *L) { lua_pushnil(L); return 1; } + fcntl(client->socket, F_SETFL, O_NONBLOCK); return 1; } @@ -167,6 +238,7 @@ Connect a socket to a server. The TCP object becomes a TCPClient object. @function :connect @tparam string host address of the host @tparam number port port of the server +@tparam[opt=false] boolean ssl use SSL if `true` @treturn[1] boolean true if success @treturn[2] boolean false if failed @treturn[2] string error string @@ -175,11 +247,12 @@ static int socket_connect(lua_State *L) { socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); char *addr = (char*)luaL_checkstring(L, 2); int port = luaL_checkinteger(L, 3); + bool ssl = lua_toboolean(L, 4); userdata->host = gethostbyname(addr); if (userdata->host == NULL) { lua_pushnil(L); - lua_pushstring(L, "No such host"); + lua_pushstring(L, strerror(errno)); return 2; } @@ -188,10 +261,23 @@ static int socket_connect(lua_State *L) { if (connect(userdata->socket, (const struct sockaddr*)&userdata->addr, sizeof(userdata->addr)) < 0) { lua_pushnil(L); - lua_pushstring(L, "Connection failed"); + lua_pushstring(L, strerror(errno)); return 2; } + if (ssl) { // SSL context setup + sslcCreateContext(&userdata->sslContext, userdata->socket, SSLCOPT_Default, addr); + sslcContextSetRootCertChain(&userdata->sslContext, rootCertChain); + if (R_FAILED(sslcStartConnection(&userdata->sslContext, NULL, NULL))) { + sslcDestroyContext(&userdata->sslContext); + lua_pushnil(L); + lua_pushstring(L, "SSL connection failed"); + return 2; + } + userdata->isSSL = true; + } + fcntl(userdata->socket, F_SETFL, O_NONBLOCK); + lua_pushboolean(L, 1); return 1; } @@ -237,7 +323,11 @@ static int socket_receive(lua_State *L) { luaL_buffinit(L, &b); char buff; - while (recv(userdata->socket, &buff, 1, flags) > 0 && buff != '\n') luaL_addchar(&b, buff); + if (!userdata->isSSL) { + while (recv(userdata->socket, &buff, 1, flags) > 0 && buff != '\n') luaL_addchar(&b, buff); + } else { + while (!R_FAILED(sslcRead(&userdata->sslContext, &buff, 1, false)) && buff != '\n') luaL_addchar(&b, buff); + } luaL_pushresult(&b); return 1; @@ -247,7 +337,11 @@ static int socket_receive(lua_State *L) { luaL_buffinit(L, &b); char buff; - while (buff != '\n' && recv(userdata->socket, &buff, 1, flags) > 0) luaL_addchar(&b, buff); + if (!userdata->isSSL) { + while (buff != '\n' && recv(userdata->socket, &buff, 1, flags) > 0) luaL_addchar(&b, buff); + } else { + while (buff != '\n' && !R_FAILED(sslcRead(&userdata->sslContext, &buff, 1, false))) luaL_addchar(&b, buff); + } luaL_pushresult(&b); return 1; @@ -258,7 +352,17 @@ static int socket_receive(lua_State *L) { } char *buff = malloc(count+1); - int len = recv(userdata->socket, buff, count, flags); + int len; + if (!userdata->isSSL) { + len = recv(userdata->socket, buff, count, flags); + } else { + len = sslcRead(&userdata->sslContext, buff, count, false); + if (R_FAILED(len)) { + lua_pushnil(L); + lua_pushinteger(L, len); + return 2; + } + } *(buff+len) = 0x0; // text end lua_pushstring(L, buff); @@ -276,12 +380,68 @@ static int socket_send(lua_State *L) { size_t size = 0; char *data = (char*)luaL_checklstring(L, 2, &size); - size_t sent = send(userdata->socket, data, size, 0); + size_t sent; + if (!userdata->isSSL) { + sent = send(userdata->socket, data, size, 0); + } else { + sent = sslcWrite(&userdata->sslContext, data, size); + if (R_FAILED(sent)) { + lua_pushnil(L); + lua_pushinteger(L, sent); + return 2; + } + } + + if (sent < 0) { + lua_pushnil(L); + lua_pushstring(L, strerror(errno)); + return 2; + } lua_pushinteger(L, sent); return 1; } +/*** +Get some informations from a socket. +@function :getpeername +@treturn string IP +@treturn number port +*/ +static int socket_getpeername(lua_State *L) { + socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); + + struct sockaddr_in addr; + socklen_t addrSize = sizeof(addr); + + getpeername(userdata->socket, (struct sockaddr*)&addr, &addrSize); + + lua_pushstring(L, inet_ntoa(addr.sin_addr)); + lua_pushinteger(L, ntohs(addr.sin_port)); + + return 2; +} + +/*** +Get some local informations from a socket. +@function :getsockname +@treturn string IP +@treturn number port +*/ +static int socket_getsockname(lua_State *L) { + socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); + + struct sockaddr_in addr; + socklen_t addrSize = sizeof(addr); + + getsockname(userdata->socket, (struct sockaddr*)&addr, &addrSize); + + lua_pushstring(L, inet_ntoa(addr.sin_addr)); + lua_pushinteger(L, ntohs(addr.sin_port)); + + return 2; +} + /*** UDP sockets @section UDP @@ -360,10 +520,11 @@ static int socket_sendto(lua_State *L) { // module functions static const struct luaL_Reg socket_functions[] = { - {"init", socket_init }, - {"shutdown", socket_shutdown}, - {"tcp", socket_tcp }, - {"udp", socket_udp }, + {"init", socket_init }, + {"shutdown", socket_shutdown }, + {"tcp", socket_tcp }, + {"udp", socket_udp }, + {"addTrustedRootCA", socket_addTrustedRootCA}, {NULL, NULL} }; @@ -379,6 +540,8 @@ static const struct luaL_Reg socket_methods[] = { {"receivefrom", socket_receivefrom}, {"send", socket_send }, {"sendto", socket_sendto }, + {"getpeername", socket_getpeername}, + {"getsockname", socket_getsockname}, {NULL, NULL} }; diff --git a/source/texture.c b/source/texture.c index 6468da6..df87a22 100644 --- a/source/texture.c +++ b/source/texture.c @@ -280,23 +280,10 @@ static int texture_save(lua_State *L) { const char* path = luaL_checkstring(L, 2); u8 type = luaL_optinteger(L, 3, 0); - u32* buff = malloc(texture->texture->width * texture->texture->height * 4); - if (buff == NULL) { - lua_pushnil(L); - lua_pushstring(L, "Failed to allocate buffer"); - return 2; - } - for (int y=0;ytexture->height;y++) { - for (int x=0;xtexture->width;x++) { - buff[x+(y*texture->texture->width)] = __builtin_bswap32(sf2d_get_pixel(texture->texture, x, y)); - } - } - int result = 0; if (type == 0) { // PNG FILE* file = fopen(path, "wb"); if (file == NULL) { - free(buff); lua_pushnil(L); lua_pushstring(L, "Can open file"); return 2; @@ -313,7 +300,7 @@ static int texture_save(lua_State *L) { for(int y=0;ytexture->height;y++) { for (int x=0;xtexture->width;x++) { - ((u32*)row)[x] = buff[x+(y*texture->texture->width)]; + ((u32*)row)[x] = __builtin_bswap32(sf2d_get_pixel(texture->texture, x, y)); } png_write_row(png, row); } @@ -328,14 +315,25 @@ static int texture_save(lua_State *L) { result = 1; } else if (type == 2) { // BMP + u32* buff = malloc(texture->texture->width * texture->texture->height * 4); + if (buff == NULL) { + lua_pushnil(L); + lua_pushstring(L, "Failed to allocate buffer"); + return 2; + } + for (int y=0;ytexture->height;y++) { + for (int x=0;xtexture->width;x++) { + buff[x+(y*texture->texture->width)] = __builtin_bswap32(sf2d_get_pixel(texture->texture, x, y)); + } + } result = stbi_write_bmp(path, texture->texture->width, texture->texture->height, 4, buff); - } else { free(buff); + + } else { lua_pushnil(L); lua_pushstring(L, "Not a valid type"); return 2; } - free(buff); if (result == 0) { lua_pushnil(L); From 6b65df0b8ea3556e7f05d2a0889ceeb37b738bb7 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Wed, 13 Apr 2016 16:19:25 +0200 Subject: [PATCH 083/101] Fixed some documentation, added some things to sockets, "Fixed" the ROMFS The romfs is still not working, but it works better. --- Makefile | 4 +- sdcard/3ds/ctruLua/examples/qtm/qtm.lua | 4 +- sdcard/3ds/ctruLua/main.lua | 2 +- source/cfgu.c | 8 +- source/ctr.c | 3 +- source/fs.c | 1 + source/gfx.c | 7 +- source/main.c | 28 +++--- source/qtm.c | 2 +- source/socket.c | 126 +++++++++++++++--------- 10 files changed, 115 insertions(+), 70 deletions(-) diff --git a/Makefile b/Makefile index 9ea0a1a..b4d10bb 100644 --- a/Makefile +++ b/Makefile @@ -51,6 +51,9 @@ CFLAGS := -g -Wall -O2 -mword-relocations -std=gnu11 \ $(ARCH) CFLAGS += $(INCLUDE) -DARM11 -D_3DS -DCTR_VERSION=\"$(APP_VERSION)\" -DCTR_BUILD=\"$(LASTCOMMIT)\" +ifneq ($(ROMFS),) + CFLAGS += -DROMFS +endif CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 @@ -132,7 +135,6 @@ endif ifneq ($(ROMFS),) export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) - CFLAGS += -DROMFS endif .PHONY: $(BUILD) clean all diff --git a/sdcard/3ds/ctruLua/examples/qtm/qtm.lua b/sdcard/3ds/ctruLua/examples/qtm/qtm.lua index 25533c2..f4948ff 100644 --- a/sdcard/3ds/ctruLua/examples/qtm/qtm.lua +++ b/sdcard/3ds/ctruLua/examples/qtm/qtm.lua @@ -25,12 +25,12 @@ while ctr.run() do local keys = hid.keys() if keys.down.start then break end - local infos = qtm.getHeadTrackingInfo() + local infos = qtm.getHeadtrackingInfo() gfx.start(gfx.TOP) if infos:checkHeadFullyDetected() then for i=1, 4 do - gfx.point(infos:convertCoordToScreen(1, 400, 240)) + gfx.point(infos:convertCoordToScreen(i, 400, 240)) end end gfx.stop() diff --git a/sdcard/3ds/ctruLua/main.lua b/sdcard/3ds/ctruLua/main.lua index bea7791..e867261 100644 --- a/sdcard/3ds/ctruLua/main.lua +++ b/sdcard/3ds/ctruLua/main.lua @@ -51,4 +51,4 @@ while ctr.run() do end end -error("Main process has exited.\nPlease reboot.\nPressing Start does not work yet.") \ No newline at end of file +error("Main process has exited.\nPlease reboot.\nPressing Start does not work yet.") diff --git a/source/cfgu.c b/source/cfgu.c index 5b2e23b..c31a8af 100644 --- a/source/cfgu.c +++ b/source/cfgu.c @@ -109,9 +109,13 @@ static int cfgu_getUsername(lua_State *L) { CFGU_GetConfigInfoBlk2(0x1C, 0xA0000, (u8*)block); u8 *name = malloc(0x14); - utf16_to_utf8(name, block, 0x14); + ssize_t len = utf16_to_utf8(name, block, 0x14); + if (len < 0) { + lua_pushstring(L, ""); + return 1; + } - lua_pushlstring(L, (const char *)name, 0x14); // The username is only 0x14 characters long. + lua_pushlstring(L, (const char *)name, len); // The username is only 0x14 characters long. return 1; } diff --git a/source/ctr.c b/source/ctr.c index 366fc10..c545a84 100644 --- a/source/ctr.c +++ b/source/ctr.c @@ -215,7 +215,8 @@ int luaopen_ctr_lib(lua_State *L) { @field root */ #ifdef ROMFS - char* buff = "romfs:"; + char* buff = "romfs:/"; + chdir(buff); #else char* buff = malloc(1024); getcwd(buff, 1024); diff --git a/source/fs.c b/source/fs.c index b3e2235..dd5faeb 100644 --- a/source/fs.c +++ b/source/fs.c @@ -239,3 +239,4 @@ void unload_fs_lib(lua_State *L) { fsExit(); } + diff --git a/source/gfx.c b/source/gfx.c index 82342bb..d172eca 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -525,12 +525,17 @@ static int gfx_target___index(lua_State *L) { return 1; } +/*** +Console +@section console +*/ + /*** Initialize the console. You can print on it using print(), or any function that normally outputs to stdout. Warning: you can't use a screen for both a console and drawing, you have to disable the console first. @function console @tparam[opt=gfx.TOP] number screen screen to draw the console on. -@tparam[opt=true] boolean debug enable stderr output on the console +@tparam[opt=false] boolean debug enable stderr output on the console */ u8 consoleScreen = GFX_TOP; static int gfx_console(lua_State *L) { diff --git a/source/main.c b/source/main.c index d106898..33febb3 100644 --- a/source/main.c +++ b/source/main.c @@ -36,7 +36,23 @@ void error(const char *error) { // Main loop int main(int argc, char** argv) { // Default arguments + #ifdef ROMFS + char* mainFile = "romfs:/main.lua"; + #else char* mainFile = "main.lua"; + #endif + + // Init Lua + lua_State *L = luaL_newstate(); + if (L == NULL) { + error("Memory allocation error while creating a new Lua state"); + return 0; + } + + // Load libs + luaL_openlibs(L); + load_ctr_lib(L); + isGfxInitialized = true; // Parse arguments for (int i=0;iaddr.sin_family = AF_INET; userdata->isSSL = false; + fcntl(userdata->socket, F_SETFL, fcntl(userdata->socket, F_GETFL, 0)|O_NONBLOCK); return 1; } @@ -140,9 +147,9 @@ static int socket_udp(lua_State *L) { lua_pushstring(L, strerror(errno)); return 2; } - fcntl(userdata->socket, F_SETFL, O_NONBLOCK); userdata->addr.sin_family = AF_INET; + fcntl(userdata->socket, F_SETFL, fcntl(userdata->socket, F_GETFL, 0)|O_NONBLOCK); return 1; } @@ -151,6 +158,9 @@ static int socket_udp(lua_State *L) { Add a trusted root CA to the certChain. @function addTrustedRootCA @tparam string cert DER cert +@treturn[1] boolean `true` if everything went fine +@treturn[2] nil in case of error +@treturn[2] number error code */ static int socket_addTrustedRootCA(lua_State *L) { size_t size = 0; @@ -181,7 +191,7 @@ static int socket_bind(lua_State *L) { socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); int port = luaL_checkinteger(L, 2); - userdata->addr.sin_addr.s_addr = htonl(INADDR_ANY); + userdata->addr.sin_addr.s_addr = gethostid(); userdata->addr.sin_port = htons(port); bind(userdata->socket, (struct sockaddr*)&userdata->addr, sizeof(userdata->addr)); @@ -205,6 +215,64 @@ static int socket_close(lua_State *L) { return 0; } +/*** +Get some informations from a socket. +@function :getpeername +@treturn string IP +@treturn number port +*/ +static int socket_getpeername(lua_State *L) { + socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); + + struct sockaddr_in addr; + socklen_t addrSize = sizeof(addr); + + getpeername(userdata->socket, (struct sockaddr*)&addr, &addrSize); + + lua_pushstring(L, inet_ntoa(addr.sin_addr)); + lua_pushinteger(L, ntohs(addr.sin_port)); + + return 2; +} + +/*** +Get some local informations from a socket. +@function :getsockname +@treturn string IP +@treturn number port +*/ +static int socket_getsockname(lua_State *L) { + socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); + + struct sockaddr_in addr; + socklen_t addrSize = sizeof(addr); + + getsockname(userdata->socket, (struct sockaddr*)&addr, &addrSize); + + lua_pushstring(L, inet_ntoa(addr.sin_addr)); + lua_pushinteger(L, ntohs(addr.sin_port)); + + return 2; +} + +/*** +Set if the socket should be blocking. +@function :setBlocking +@tparam[opt=true] boolean block if `false`, the socket won't block +*/ +static int socket_setBlocking(lua_State *L) { + socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); + bool block = true; + if (lua_isboolean(L, 2)) + block = lua_toboolean(L, 2); + + int flags = fcntl(userdata->socket, F_GETFL, 0); + flags = block?(flags&~O_NONBLOCK):(flags|O_NONBLOCK); + fcntl(userdata->socket, F_SETFL, flags); + + return 0; +} + /*** TCP Sockets @section TCP @@ -221,6 +289,7 @@ static int socket_accept(lua_State *L) { socket_userdata *client = lua_newuserdata(L, sizeof(*client)); luaL_getmetatable(L, "LSocket"); lua_setmetatable(L, -2); + client->isSSL = false; socklen_t addrSize = sizeof(client->addr); client->socket = accept(userdata->socket, (struct sockaddr*)&client->addr, &addrSize); @@ -228,7 +297,6 @@ static int socket_accept(lua_State *L) { lua_pushnil(L); return 1; } - fcntl(client->socket, F_SETFL, O_NONBLOCK); return 1; } @@ -276,7 +344,6 @@ static int socket_connect(lua_State *L) { } userdata->isSSL = true; } - fcntl(userdata->socket, F_SETFL, O_NONBLOCK); lua_pushboolean(L, 1); return 1; @@ -402,46 +469,6 @@ static int socket_send(lua_State *L) { return 1; } -/*** -Get some informations from a socket. -@function :getpeername -@treturn string IP -@treturn number port -*/ -static int socket_getpeername(lua_State *L) { - socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); - - struct sockaddr_in addr; - socklen_t addrSize = sizeof(addr); - - getpeername(userdata->socket, (struct sockaddr*)&addr, &addrSize); - - lua_pushstring(L, inet_ntoa(addr.sin_addr)); - lua_pushinteger(L, ntohs(addr.sin_port)); - - return 2; -} - -/*** -Get some local informations from a socket. -@function :getsockname -@treturn string IP -@treturn number port -*/ -static int socket_getsockname(lua_State *L) { - socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); - - struct sockaddr_in addr; - socklen_t addrSize = sizeof(addr); - - getsockname(userdata->socket, (struct sockaddr*)&addr, &addrSize); - - lua_pushstring(L, inet_ntoa(addr.sin_addr)); - lua_pushinteger(L, ntohs(addr.sin_port)); - - return 2; -} - /*** UDP sockets @section UDP @@ -533,6 +560,7 @@ static const struct luaL_Reg socket_methods[] = { {"accept", socket_accept }, {"bind", socket_bind }, {"close", socket_close }, + {"setBlocking", socket_setBlocking}, {"__gc", socket_close }, {"connect", socket_connect }, {"listen", socket_listen }, From b798818e99116f58ed4dd02ac994d1441fe92d0b Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Sat, 16 Apr 2016 13:24:03 +0200 Subject: [PATCH 084/101] Fixed the ROMFS, Removed some fields in fs.list(), Updated sftdlib fs.list() now returns a table with tables containing the fields "name", "fileSize", and "isDirectory" --- libs/sftdlib/libsftd/include/sftd.h | 13 +- libs/sftdlib/libsftd/source/sftd.c | 134 +++++++++--------- libs/sftdlib/libsftd/source/texture_atlas.c | 1 + source/font.c | 2 +- source/fs.c | 142 +++++++++----------- 5 files changed, 149 insertions(+), 143 deletions(-) diff --git a/libs/sftdlib/libsftd/include/sftd.h b/libs/sftdlib/libsftd/include/sftd.h index 9e3facc..8702a04 100644 --- a/libs/sftdlib/libsftd/include/sftd.h +++ b/libs/sftdlib/libsftd/include/sftd.h @@ -110,9 +110,19 @@ void sftd_draw_wtextf(sftd_font *font, int x, int y, unsigned int color, unsigne * @param font the font used to calculate the width * @param size the font size * @param text a pointer to the text that will be used to calculate the length + * @return the width in pixels */ int sftd_get_text_width(sftd_font *font, unsigned int size, const char *text); +/** + * @brief Returns the width of the given wide text in pixels + * @param font the font used to calculate the width + * @param size the font size + * @param text a pointer to the wide text that will be used to calculate the length + * @return the width in pixels + */ +int sftd_get_wtext_width(sftd_font *font, unsigned int size, const wchar_t *text); + /** * @brief Draws text using a font. The text will wrap after the pixels specified in lineWidth. * @param font the font to use @@ -149,9 +159,6 @@ void sftd_calc_bounding_box(int *boundingWidth, int *boundingHeight, sftd_font * */ void sftd_draw_textf_wrap(sftd_font *font, int x, int y, unsigned int color, unsigned int size, unsigned int lineWidth, const char *text, ...); -// (ctruLua addition) Based on sftd_draw_wtext, returns the width of the text drawn. -int sftd_width_wtext(sftd_font *font, unsigned int size, const wchar_t *text); - #ifdef __cplusplus } #endif diff --git a/libs/sftdlib/libsftd/source/sftd.c b/libs/sftdlib/libsftd/source/sftd.c index d0adf24..6dca62f 100644 --- a/libs/sftdlib/libsftd/source/sftd.c +++ b/libs/sftdlib/libsftd/source/sftd.c @@ -227,6 +227,13 @@ void sftd_draw_text(sftd_font *font, int x, int y, unsigned int color, unsigned FT_ULong flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL; while (*text) { + if(*text == '\n') { + pen_x = x; + pen_y += size; + text++; + continue; + } + glyph_index = FTC_CMapCache_Lookup(font->cmapcache, (FTC_FaceID)font, charmap_index, *text); if (use_kerning && previous && glyph_index) { @@ -304,6 +311,13 @@ void sftd_draw_wtext(sftd_font *font, int x, int y, unsigned int color, unsigned FT_ULong flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL; while (*text) { + if(*text == '\n') { + pen_x = x; + pen_y += size; + text++; + continue; + } + glyph_index = FTC_CMapCache_Lookup(font->cmapcache, (FTC_FaceID)font, charmap_index, *text); if (use_kerning && previous && glyph_index) { @@ -417,6 +431,66 @@ int sftd_get_text_width(sftd_font *font, unsigned int size, const char *text) return pen_x; } +int sftd_get_wtext_width(sftd_font *font, unsigned int size, const wchar_t *text) +{ + FTC_FaceID face_id = (FTC_FaceID)font; + FT_Face face; + FTC_Manager_LookupFace(font->ftcmanager, face_id, &face); + + FT_Int charmap_index; + charmap_index = FT_Get_Charmap_Index(face->charmap); + + FT_Glyph glyph; + FT_Bool use_kerning = FT_HAS_KERNING(face); + FT_UInt glyph_index, previous = 0; + int pen_x = 0; + int pen_y = size; + + FTC_ScalerRec scaler; + scaler.face_id = face_id; + scaler.width = size; + scaler.height = size; + scaler.pixel = 1; + + FT_ULong flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL; + + while (*text) { + glyph_index = FTC_CMapCache_Lookup(font->cmapcache, (FTC_FaceID)font, charmap_index, *text); + + if (use_kerning && previous && glyph_index) { + FT_Vector delta; + FT_Get_Kerning(face, previous, glyph_index, FT_KERNING_DEFAULT, &delta); + pen_x += delta.x >> 6; + } + + if (!texture_atlas_exists(font->tex_atlas, glyph_index)) { + FTC_ImageCache_LookupScaler(font->imagecache, &scaler, flags, glyph_index, &glyph, NULL); + + if (!atlas_add_glyph(font->tex_atlas, glyph_index, (FT_BitmapGlyph)glyph, size)) { + continue; + } + } + + bp2d_rectangle rect; + int bitmap_left, bitmap_top; + int advance_x, advance_y; + int glyph_size; + + texture_atlas_get(font->tex_atlas, glyph_index, + &rect, &bitmap_left, &bitmap_top, + &advance_x, &advance_y, &glyph_size); + + const float draw_scale = size/(float)glyph_size; + + pen_x += (advance_x >> 16) * draw_scale; + pen_y += (advance_y >> 16) * draw_scale; + + previous = glyph_index; + text++; + } + return pen_x; +} + void sftd_draw_text_wrap(sftd_font *font, int x, int y, unsigned int color, unsigned int size, unsigned int lineWidth, const char *text) { FTC_FaceID face_id = (FTC_FaceID)font; @@ -610,63 +684,3 @@ void sftd_draw_textf_wrap(sftd_font *font, int x, int y, unsigned int color, uns sftd_draw_text_wrap(font, x, y, color, size, lineWidth, buffer); va_end(args); } - -// (ctruLua addition) Based on sftd_draw_wtext, returns the width of the text drawn. -int sftd_width_wtext(sftd_font *font, unsigned int size, const wchar_t *text) -{ - FTC_FaceID face_id = (FTC_FaceID)font; - FT_Face face; - FTC_Manager_LookupFace(font->ftcmanager, face_id, &face); - - FT_Int charmap_index; - charmap_index = FT_Get_Charmap_Index(face->charmap); - - FT_Glyph glyph; - FT_Bool use_kerning = FT_HAS_KERNING(face); - FT_UInt glyph_index, previous = 0; - int pen_x = 0; - - FTC_ScalerRec scaler; - scaler.face_id = face_id; - scaler.width = size; - scaler.height = size; - scaler.pixel = 1; - - FT_ULong flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL; - - while (*text) { - glyph_index = FTC_CMapCache_Lookup(font->cmapcache, (FTC_FaceID)font, charmap_index, *text); - - if (use_kerning && previous && glyph_index) { - FT_Vector delta; - FT_Get_Kerning(face, previous, glyph_index, FT_KERNING_DEFAULT, &delta); - pen_x += delta.x >> 6; - } - - if (!texture_atlas_exists(font->tex_atlas, glyph_index)) { - FTC_ImageCache_LookupScaler(font->imagecache, &scaler, flags, glyph_index, &glyph, NULL); - - if (!atlas_add_glyph(font->tex_atlas, glyph_index, (FT_BitmapGlyph)glyph, size)) { - continue; - } - } - - bp2d_rectangle rect; - int bitmap_left, bitmap_top; - int advance_x, advance_y; - int glyph_size; - - texture_atlas_get(font->tex_atlas, glyph_index, - &rect, &bitmap_left, &bitmap_top, - &advance_x, &advance_y, &glyph_size); - - const float draw_scale = size/(float)glyph_size; - - pen_x += (advance_x >> 16) * draw_scale; - - previous = glyph_index; - text++; - } - - return pen_x; -} diff --git a/libs/sftdlib/libsftd/source/texture_atlas.c b/libs/sftdlib/libsftd/source/texture_atlas.c index 03320c5..f8e76bc 100644 --- a/libs/sftdlib/libsftd/source/texture_atlas.c +++ b/libs/sftdlib/libsftd/source/texture_atlas.c @@ -15,6 +15,7 @@ texture_atlas *texture_atlas_create(int width, int height, sf2d_texfmt format, s rect.h = height; atlas->tex = sf2d_create_texture(width, height, format, place); + sf2d_texture_set_params(atlas->tex, GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR)); sf2d_texture_tile32(atlas->tex); atlas->bp_root = bp2d_create(&rect); diff --git a/source/font.c b/source/font.c index 935aa64..80542c9 100644 --- a/source/font.c +++ b/source/font.c @@ -95,7 +95,7 @@ static int font_object_width(lua_State *L) { len = mbstowcs(wtext, text, len); *(wtext+len) = 0x0; // text end - lua_pushinteger(L, sftd_width_wtext(font->font, size, wtext)); + lua_pushinteger(L, sftd_get_wtext_width(font->font, size, wtext)); return 1; } diff --git a/source/fs.c b/source/fs.c index dd5faeb..54d67d6 100644 --- a/source/fs.c +++ b/source/fs.c @@ -3,25 +3,24 @@ The `fs` module. @module ctr.fs @usage local fs = require("ctr.fs") */ +#include #include #include #include +#include +#include #include <3ds/types.h> #include <3ds/util/utf.h> #include <3ds/services/fs.h> +#include <3ds/sdmc.h> +#include <3ds/romfs.h> #include #include bool isFsInitialized = false; -Handle *fsuHandle; -FS_Archive sdmcArchive; -#ifdef ROMFS -FS_Archive romfsArchive; -#endif - /*** The `ctr.fs.lzlib` module. @table lzlib @@ -57,81 +56,76 @@ Lists a directory contents (unsorted). ` { name = "Item name.txt", - shortName = "ITEM~", - shortExt = "TXT", isDirectory = false, - isHidden = false, - isArchive = false, - isReadOnly = false, fileSize = 321 -- (integer) in bytes } ` */ static int fs_list(lua_State *L) { - const char *path = prefix_path(luaL_checkstring(L, 1)); + const char* basepath = prefix_path(luaL_checkstring(L, 1)); + char* path; + bool shouldFreePath = false; + if (basepath[strlen(basepath)-1] != '/') { + path = malloc(strlen(basepath)+2); + strcpy(path, basepath); + strcat(path, "/"); + shouldFreePath = true; + } else { + path = (char*)basepath; + } lua_newtable(L); int i = 1; // table index - - // Get default archive - #ifdef ROMFS - FS_Archive archive = romfsArchive; - #else - FS_Archive archive = sdmcArchive; - #endif - // Archive path override (and skip path prefix) - if (strncmp(path, "sdmc:", 5) == 0) { - path += 5; - archive = sdmcArchive; - #ifdef ROMFS - } else if (strncmp(path, "romfs:", 6) == 0) { - path += 6; - archive = romfsArchive; - #endif + + DIR* dir = opendir(path); + if (dir == NULL) { + if (shouldFreePath) free(path); + lua_pushboolean(L, false); + lua_pushstring(L, strerror(errno)); + return 2; } - - FS_Path dirPath = fsMakePath(PATH_ASCII, path); - - Handle dirHandle; - FSUSER_OpenDirectory(&dirHandle, archive, dirPath); - - u32 entriesRead = 0; - do { - FS_DirectoryEntry buffer; - - FSDIR_Read(dirHandle, &entriesRead, 1, &buffer); - - if (!entriesRead) break; - - uint8_t name[0x106+1]; // utf8 file name - size_t size = utf16_to_utf8(name, buffer.name, 0x106); - *(name+size) = 0x0; // mark text end - - lua_createtable(L, 0, 8); - - lua_pushstring(L, (const char *)name); + errno = 0; + struct dirent *entry; + while (((entry = readdir(dir)) != NULL) && !errno) { + lua_createtable(L, 0, 3); + + lua_pushstring(L, (const char*)entry->d_name); lua_setfield(L, -2, "name"); - lua_pushstring(L, (const char *)buffer.shortName); - lua_setfield(L, -2, "shortName"); - lua_pushstring(L, (const char *)buffer.shortExt); - lua_setfield(L, -2, "shortExt"); - lua_pushboolean(L, buffer.attributes&FS_ATTRIBUTE_DIRECTORY); + lua_pushboolean(L, entry->d_type==DT_DIR); lua_setfield(L, -2, "isDirectory"); - lua_pushboolean(L, buffer.attributes&FS_ATTRIBUTE_HIDDEN); - lua_setfield(L, -2, "isHidden"); - lua_pushboolean(L, buffer.attributes&FS_ATTRIBUTE_ARCHIVE); - lua_setfield(L, -2, "isArchive"); - lua_pushboolean(L, buffer.attributes&FS_ATTRIBUTE_READ_ONLY); - lua_setfield(L, -2, "isReadOnly"); - lua_pushinteger(L, buffer.fileSize); + + if (entry->d_type==DT_REG) { // Regular files: check size + char* filepath = malloc(strlen(path)+strlen(entry->d_name)+1); + if (filepath == NULL) { + luaL_error(L, "Memory allocation error"); + } + memset(filepath, 0, strlen(path)+strlen(entry->d_name)+1); + + strcpy(filepath, path); + strcat(filepath, entry->d_name); + struct stat stats; + if (stat(filepath, &stats)) { + free(filepath); + if (shouldFreePath) free(path); + luaL_error(L, "Stat error: %s (%d)", strerror(errno), errno); + lua_pushboolean(L, false); + lua_pushstring(L, strerror(errno)); + return 2; + } else { + lua_pushinteger(L, stats.st_size); + } + free(filepath); + } else { // Everything else: 0 bytes + lua_pushinteger(L, 0); + } lua_setfield(L, -2, "fileSize"); lua_seti(L, -2, i); i++; - - } while (entriesRead > 0); - - FSDIR_Close(dirHandle); + } + + closedir(dir); + if (shouldFreePath) free(path); return 1; } @@ -214,16 +208,9 @@ int luaopen_fs_lib(lua_State *L) { void load_fs_lib(lua_State *L) { if (!isFsInitialized) { - fsInit(); - - fsuHandle = fsGetSessionHandle(); - FSUSER_Initialize(*fsuHandle); - - sdmcArchive = (FS_Archive){ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")}; - FSUSER_OpenArchive(&sdmcArchive); + sdmcInit(); #ifdef ROMFS - romfsArchive = (FS_Archive){ARCHIVE_ROMFS, fsMakePath(PATH_EMPTY, "")}; - FSUSER_OpenArchive(&romfsArchive); + romfsInit(); #endif isFsInitialized = true; } @@ -232,11 +219,8 @@ void load_fs_lib(lua_State *L) { } void unload_fs_lib(lua_State *L) { - FSUSER_CloseArchive(&sdmcArchive); + sdmcExit(); #ifdef ROMFS - FSUSER_CloseArchive(&romfsArchive); + romfsExit(); #endif - - fsExit(); } - From 347e480d5a0edfaca3ad34ff0f3cab935e37375c Mon Sep 17 00:00:00 2001 From: Reuh Date: Thu, 21 Apr 2016 21:51:45 +0200 Subject: [PATCH 085/101] Moved ctr.gfx.set|getTextSize to ctr.gfx.font.set|getSize, added missing documentation to font:width Started working again on the editor --- sdcard/3ds/ctruLua/editor/color.lua | 2 +- sdcard/3ds/ctruLua/editor/main.lua | 12 +++++------- sdcard/3ds/ctruLua/editor/syntax.lua | 4 ++-- sdcard/3ds/ctruLua/main.lua | 2 +- source/font.c | 27 +++++++++++++++++++++++++++ source/font.h | 2 ++ source/gfx.c | 23 ----------------------- 7 files changed, 38 insertions(+), 34 deletions(-) diff --git a/sdcard/3ds/ctruLua/editor/color.lua b/sdcard/3ds/ctruLua/editor/color.lua index 5f6ef58..ed32152 100644 --- a/sdcard/3ds/ctruLua/editor/color.lua +++ b/sdcard/3ds/ctruLua/editor/color.lua @@ -15,4 +15,4 @@ return { ["keyword.control"] = hex(0xF92672FF), ["keyword.operator"] = hex(0xF92672FF), ["support.function"] = hex(0x66D9EFFF) -} \ No newline at end of file +} diff --git a/sdcard/3ds/ctruLua/editor/main.lua b/sdcard/3ds/ctruLua/editor/main.lua index 1831568..3c001e5 100644 --- a/sdcard/3ds/ctruLua/editor/main.lua +++ b/sdcard/3ds/ctruLua/editor/main.lua @@ -35,12 +35,13 @@ local coloredLines = syntax(lines, color) -- Variables local lineHeight = 10 +local fontSize = 9 local cursorX, cursorY = 1, 1 local scrollX, scrollY = 0, 0 -- Helper functions local function displayedText(text) - return text:gsub("\t", " ") + return text:gsub("\t", " "), nil end -- Set defaults @@ -48,6 +49,7 @@ gfx.set3D(false) gfx.color.setDefault(color.default) gfx.color.setBackground(color.background) gfx.font.setDefault(font) +gfx.font.setSize(fontSize) while ctr.run() do hid.read() @@ -157,11 +159,7 @@ while ctr.run() do for _,colored in ipairs(coloredLines[i]) do local str = displayedText(colored[1]) - - gfx.color.setDefault(colored[2]) - gfx.text(x, y, str) - gfx.color.setDefault(color.default) - + gfx.text(x, y, str, fontSize, colored[2]) x = x + font:width(str) end end @@ -184,4 +182,4 @@ while ctr.run() do gfx.render() end -font:unload() \ No newline at end of file +font:unload() diff --git a/sdcard/3ds/ctruLua/editor/syntax.lua b/sdcard/3ds/ctruLua/editor/syntax.lua index bebe3f5..18a47b9 100644 --- a/sdcard/3ds/ctruLua/editor/syntax.lua +++ b/sdcard/3ds/ctruLua/editor/syntax.lua @@ -50,7 +50,7 @@ local syntax = { return function(lines, color) local ret = {} - for _,line in ipairs(lines) do + for _, line in ipairs(lines) do local colored = { { line, color.default } } for _, patterns in ipairs(syntax) do @@ -87,4 +87,4 @@ return function(lines, color) end return ret -end \ No newline at end of file +end diff --git a/sdcard/3ds/ctruLua/main.lua b/sdcard/3ds/ctruLua/main.lua index e867261..9d21667 100644 --- a/sdcard/3ds/ctruLua/main.lua +++ b/sdcard/3ds/ctruLua/main.lua @@ -12,7 +12,7 @@ local function displayError(err, trace) gfx.set3D(false) gfx.color.setBackground(0xFF0000B3) gfx.color.setDefault(0xFFFDFDFD) - gfx.setTextSize(12) + gfx.font.setSize(12) gfx.font.setDefault(gfx.font.load(ctr.root .. "resources/VeraMono.ttf")) while ctr.run() do diff --git a/source/font.c b/source/font.c index 80542c9..a29f356 100644 --- a/source/font.c +++ b/source/font.c @@ -15,6 +15,8 @@ The `font` module #include "font.h" +u32 textSize = 9; + /*** Load a TTF font. @function load @@ -70,6 +72,28 @@ static int font_getDefault(lua_State *L) { return 1; } +/*** +Set the default text size. +@function setSize +@tparam number size new default text size +*/ +static int font_setSize(lua_State *L) { + textSize = luaL_checkinteger(L, 1); + + return 0; +} + +/*** +Return the default text size. +@function getSize +@treturn number the default text size +*/ +static int font_getSize(lua_State *L) { + lua_pushinteger(L, textSize); + + return 1; +} + /*** font object @section Methods @@ -79,6 +103,7 @@ font object Return the width of a string with a font. @function :width @tparam string text the text to test +@tparam[opt=default size] integer font size, in pixels @treturn number the width of the text (in pixels) */ static int font_object_width(lua_State *L) { @@ -127,6 +152,8 @@ static const struct luaL_Reg font_lib[] = { { "load", font_load }, { "setDefault", font_setDefault }, { "getDefault", font_getDefault }, + { "setSize", font_setSize }, + { "getSize", font_getSize }, { NULL, NULL } }; diff --git a/source/font.h b/source/font.h index 888176c..2dc87ad 100644 --- a/source/font.h +++ b/source/font.h @@ -5,4 +5,6 @@ typedef struct { sftd_font *font; } font_userdata; +extern u32 textSize; + #endif diff --git a/source/gfx.c b/source/gfx.c index d172eca..447f8df 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -26,7 +26,6 @@ typedef struct { bool isGfxInitialized = false; bool is3DEnabled = false; //TODO: add a function for this in the ctrulib/sf2dlib. -u32 textSize = 9; /*** The `ctr.gfx.color` module. @@ -373,26 +372,6 @@ static int gfx_calcBoundingBox(lua_State *L) { return 2; } -/*** -Set the default text size. -@function setTextSize -@tparam number size new default text size -*/ -static int gfx_setTextSize(lua_State *L) { - textSize = luaL_checkinteger(L, 1); - return 0; -} - -/*** -Return the default text size. -@function getTextSize -@treturn number the default text size -*/ -static int gfx_getTextSize(lua_State *L) { - lua_pushinteger(L, textSize); - return 1; -} - /*** Enables or disable the scissor test. When the scissor test is enabled, the drawing area will be limited to a specific rectangle, every pixel drawn outside will be discarded. @@ -593,8 +572,6 @@ static const struct luaL_Reg gfx_lib[] = { { "text", gfx_text }, { "wrappedText", gfx_wrappedText }, { "calcBoundingBox", gfx_calcBoundingBox }, - { "setTextSize", gfx_setTextSize }, - { "getTextSize", gfx_getTextSize }, { "scissor", gfx_scissor }, { "target", gfx_target }, { "console", gfx_console }, From 2b7d37304dc17bc2f89495ade12f1533493592a0 Mon Sep 17 00:00:00 2001 From: Reuh Date: Thu, 21 Apr 2016 22:31:48 +0200 Subject: [PATCH 086/101] Made the editor usable It was able to edit file before, but didn't show the changes on the screen, so this was easy to do. Also renamed some setTextSize->setSize missed in last commit. --- sdcard/3ds/ctruLua/editor/main.lua | 46 +++++++++++++++++++++++--- sdcard/3ds/ctruLua/editor/syntax.lua | 4 +-- sdcard/3ds/ctruLua/libs/filepicker.lua | 6 ++-- 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/sdcard/3ds/ctruLua/editor/main.lua b/sdcard/3ds/ctruLua/editor/main.lua index 3c001e5..5685fb5 100644 --- a/sdcard/3ds/ctruLua/editor/main.lua +++ b/sdcard/3ds/ctruLua/editor/main.lua @@ -38,6 +38,7 @@ local lineHeight = 10 local fontSize = 9 local cursorX, cursorY = 1, 1 local scrollX, scrollY = 0, 0 +local fileModified = false -- Helper functions local function displayedText(text) @@ -56,7 +57,30 @@ while ctr.run() do local keys = hid.keys() -- Keys input - if keys.down.start then return end + if keys.down.start then + local exit = not fileModified + if fileModified then + while ctr.run() do + hid.read() + local keys = hid.keys() + if keys.down.b then + exit = false + break + elseif keys.down.a then + exit = true + break + end + gfx.start(gfx.TOP) + gfx.text(3, 3, "The file was modified but not saved!") + gfx.text(3, 3 + lineHeight, "Are you sure you want to exit without saving?") + gfx.text(3, 3 + lineHeight*2, "Press A to exit and discard the modified file") + gfx.text(3, 3 + lineHeight*3, "Press B to return to the editor") + gfx.stop() + gfx.render() + end + end + if exit then break end + end if keys.down.dRight then cursorX = cursorX + 1 @@ -101,15 +125,16 @@ while ctr.run() do for i = 1, #lines, 1 do file:write(lines[i]..lineEnding) gfx.start(gfx.TOP) - gfx.rectangle(0, 0, math.ceil(i/#lines*gfx.TOP_WIDTH), gfx.TOP_HEIGHT, 0, 0xFFFFFFFF) - gfx.color.setDefault(color.background) - gfx.text(gfx.TOP_WIDTH/2, gfx.TOP_HEIGHT/2, math.ceil(i/#lines*100).."%") - gfx.color.setDefault(color.default) + gfx.rectangle(0, 0, math.ceil(i/#lines*gfx.TOP_WIDTH), gfx.TOP_HEIGHT, 0, 0xFFFFFFFF) + gfx.color.setDefault(color.background) + gfx.text(gfx.TOP_WIDTH/2, gfx.TOP_HEIGHT/2, math.ceil(i/#lines*100).."%") + gfx.color.setDefault(color.default) gfx.stop() gfx.render() end file:flush() file:close() + fileModified = false end end @@ -126,21 +151,30 @@ while ctr.run() do cursorX, cursorY = utf8.len(lines[cursorY-1])+1, cursorY - 1 lines[cursorY] = lines[cursorY]..lines[cursorY+1] table.remove(lines, cursorY+1) + table.remove(coloredLines, cursorY+1) end + + coloredLines[cursorY] = syntax(lines[cursorY], color) + elseif input == "\n" then local newline = lines[cursorY]:sub(utf8.offset(lines[cursorY], cursorX), -1) local whitespace = lines[cursorY]:match("^%s+") if whitespace then newline = whitespace .. newline end lines[cursorY] = lines[cursorY]:sub(1, utf8.offset(lines[cursorY], cursorX)-1) + coloredLines[cursorY] = syntax(lines[cursorY], color) table.insert(lines, cursorY + 1, newline) + table.insert(coloredLines, cursorY + 1, syntax(newline, color)) cursorX, cursorY = whitespace and #whitespace+1 or 1, cursorY + 1 + else lines[cursorY] = lines[cursorY]:sub(1, utf8.offset(lines[cursorY], cursorX)-1)..input.. lines[cursorY]:sub(utf8.offset(lines[cursorY], cursorX), -1) + coloredLines[cursorY] = syntax(lines[cursorY], color) cursorX = cursorX + 1 end + fileModified = true end -- Draw @@ -174,6 +208,8 @@ while ctr.run() do gfx.start(gfx.BOTTOM) gfx.text(3, 3, "FPS: "..math.ceil(gfx.getFPS())) + gfx.text(3, 3 + lineHeight, "Press select to save.") + gfx.text(3, 3 + lineHeight*2, "Press start to exit.") keyboard.draw(4, 115) diff --git a/sdcard/3ds/ctruLua/editor/syntax.lua b/sdcard/3ds/ctruLua/editor/syntax.lua index 18a47b9..c6845af 100644 --- a/sdcard/3ds/ctruLua/editor/syntax.lua +++ b/sdcard/3ds/ctruLua/editor/syntax.lua @@ -50,7 +50,7 @@ local syntax = { return function(lines, color) local ret = {} - for _, line in ipairs(lines) do + for _, line in ipairs(type(lines) == "table" and lines or {lines}) do local colored = { { line, color.default } } for _, patterns in ipairs(syntax) do @@ -86,5 +86,5 @@ return function(lines, color) table.insert(ret, colored) end - return ret + return type(lines) == "table" and ret or ret[1] end diff --git a/sdcard/3ds/ctruLua/libs/filepicker.lua b/sdcard/3ds/ctruLua/libs/filepicker.lua index e4ebd42..44f5f77 100644 --- a/sdcard/3ds/ctruLua/libs/filepicker.lua +++ b/sdcard/3ds/ctruLua/libs/filepicker.lua @@ -7,7 +7,7 @@ local externalConfig local function gfxPrepare() local old = {gfx.get3D(), gfx.color.getDefault(), gfx.color.getBackground(), - gfx.font.getDefault(), gfx.getTextSize()} + gfx.font.getDefault(), gfx.font.getSize()} local mono = gfx.font.load(ctr.root .. "resources/VeraMono.ttf") @@ -15,7 +15,7 @@ local function gfxPrepare() gfx.color.setDefault(0xFFFDFDFD) gfx.color.setBackground(0xFF333333) gfx.font.setDefault(mono) - gfx.setTextSize(12) + gfx.font.setSize(12) return old end @@ -25,7 +25,7 @@ local function gfxRestore(state) gfx.color.setDefault(state[2]) gfx.color.setBackground(state[3]) gfx.font.setDefault(state[4]) - gfx.setTextSize(state[5]) + gfx.font.setSize(state[5]) end local function systemBindings(bindings) From 358b68c643c50d6fb431bf463475b49df3468be1 Mon Sep 17 00:00:00 2001 From: Reuh Date: Fri, 22 Apr 2016 13:42:59 +0200 Subject: [PATCH 087/101] Renamed item.fileSize to item.size in fs.list() return value, enabled 3D in the editor, added a lot of missing documentation For the return values, I followed this rule: if the fuction returns true on success, it should return false, error on error; for every other case it should return nil, error on error. Also, who doesn't want to edit code in 3D ? The line depth depends on the indentation level. --- sdcard/3ds/ctruLua/editor/main.lua | 62 +++++++++++++++----------- sdcard/3ds/ctruLua/libs/filepicker.lua | 2 +- source/cam.c | 3 ++ source/font.c | 6 ++- source/fs.c | 22 +++++---- source/httpc.c | 34 ++++++++++---- source/ir.c | 28 +++++++++--- source/map.c | 6 ++- source/mic.c | 7 ++- source/ptm.c | 4 +- source/qtm.c | 11 +++-- source/socket.c | 16 +++++-- source/texture.c | 14 +++--- 13 files changed, 143 insertions(+), 72 deletions(-) diff --git a/sdcard/3ds/ctruLua/editor/main.lua b/sdcard/3ds/ctruLua/editor/main.lua index 5685fb5..e247583 100644 --- a/sdcard/3ds/ctruLua/editor/main.lua +++ b/sdcard/3ds/ctruLua/editor/main.lua @@ -44,9 +44,36 @@ local fileModified = false local function displayedText(text) return text:gsub("\t", " "), nil end +local function drawTop(eye) + -- Depth multiplicator. Multiply by a positive and add to x to add depth; multiply by a negative and add to x to go out of the screen. + local function d(mul) return math.floor(mul * hid.pos3d() * eye) end + + -- Lines + local sI = math.floor(scrollY / lineHeight) + if sI < 1 then sI = 1 end + + local eI = math.ceil((scrollY + gfx.TOP_HEIGHT) / lineHeight) + if eI > #lines then eI = #lines end + + for i = sI, eI, 1 do + local x = -scrollX + local y = -scrollY+ (i-1)*lineHeight + + for _,colored in ipairs(coloredLines[i]) do + local str = displayedText(colored[1]) + gfx.text(x + d(#(lines[i]:match("^%s+") or "")*3), y, str, fontSize, colored[2]) + x = x + font:width(str) + end + end + + -- Cursor + local curline = lines[cursorY] + gfx.rectangle(-scrollX+ font:width(displayedText(curline:sub(1, (utf8.offset(curline, cursorX) or 0)-1))) + d(#(curline:match("^%s+") or "")*3), + -scrollY+ (cursorY-1)*lineHeight, 1, lineHeight, 0, color.cursor) +end -- Set defaults -gfx.set3D(false) +gfx.set3D(true) gfx.color.setDefault(color.default) gfx.color.setBackground(color.background) gfx.font.setDefault(font) @@ -178,32 +205,17 @@ while ctr.run() do end -- Draw - gfx.start(gfx.TOP) - - -- Lines - local sI = math.floor(scrollY / lineHeight) - if sI < 1 then sI = 1 end - - local eI = math.ceil((scrollY + gfx.TOP_HEIGHT) / lineHeight) - if eI > #lines then eI = #lines end - - for i = sI, eI, 1 do - local x = -scrollX - local y = -scrollY+ (i-1)*lineHeight - - for _,colored in ipairs(coloredLines[i]) do - local str = displayedText(colored[1]) - gfx.text(x, y, str, fontSize, colored[2]) - x = x + font:width(str) - end - end - - -- Cursor - local curline = lines[cursorY] - gfx.rectangle(-scrollX+ font:width(displayedText(curline:sub(1, (utf8.offset(curline, cursorX) or 0)-1))), - -scrollY+ (cursorY-1)*lineHeight, 1, lineHeight, 0, color.cursor) + local is3D = hid.pos3d() > 0 + gfx.start(gfx.TOP, gfx.LEFT) + drawTop(is3D and 0 or -1) gfx.stop() + + if is3D then + gfx.start(gfx.TOP, gfx.RIGHT) + drawTop(1) + gfx.stop() + end gfx.start(gfx.BOTTOM) diff --git a/sdcard/3ds/ctruLua/libs/filepicker.lua b/sdcard/3ds/ctruLua/libs/filepicker.lua index 44f5f77..da4760d 100644 --- a/sdcard/3ds/ctruLua/libs/filepicker.lua +++ b/sdcard/3ds/ctruLua/libs/filepicker.lua @@ -115,7 +115,7 @@ local function drawBottom(externalConfig, workingDirectoryScroll, selected) gfx.text(1, 15, selectedFile.name, 12) if not selectedFile.isDirectory then - gfx.text(1, 45, tostring(selectedFile.fileSize) .. "B", 12, 0xFF727272) + gfx.text(1, 45, tostring(selectedFile.size) .. "B", 12, 0xFF727272) end local binding, pattern = getBinding(selectedFile, bindings) diff --git a/source/cam.c b/source/cam.c index 961d26f..4d756c5 100644 --- a/source/cam.c +++ b/source/cam.c @@ -21,6 +21,9 @@ The `cam` module. /*** Initialize the camera module. @function init +@treturn[1] boolean `true` if everything went fine +@treturn[2] boolean `false` in case of error +@treturn[2] integer error code */ static int cam_init(lua_State *L) { Result ret = camInit(); diff --git a/source/font.c b/source/font.c index a29f356..bac5c44 100644 --- a/source/font.c +++ b/source/font.c @@ -1,5 +1,5 @@ /*** -The `font` module +The `gfx.font` module @module ctr.gfx.font @usage local font = require("ctr.gfx.font") */ @@ -21,7 +21,9 @@ u32 textSize = 9; Load a TTF font. @function load @tparam string path path to the file -@treturn font the loaded font. +@treturn[1] font the loaded font. +@treturn[2] nil if an error occurred +@treturn[2] string error message */ static int font_load(lua_State *L) { const char *path = luaL_checkstring(L, 1); diff --git a/source/fs.c b/source/fs.c index 54d67d6..bc08fdd 100644 --- a/source/fs.c +++ b/source/fs.c @@ -52,14 +52,16 @@ const char* prefix_path(const char* path) { Lists a directory contents (unsorted). @function list @tparam string path the directory we wants to list the content -@treturn table the item list. Each item is a table like: +@treturn[1] table the item list. Each item is a table like: ` { name = "Item name.txt", isDirectory = false, - fileSize = 321 -- (integer) in bytes + size = 321 -- (integer) item size, in bytes } ` +@treturn[2] nil if an error occurred +@treturn[2] string error message */ static int fs_list(lua_State *L) { const char* basepath = prefix_path(luaL_checkstring(L, 1)); @@ -80,8 +82,8 @@ static int fs_list(lua_State *L) { DIR* dir = opendir(path); if (dir == NULL) { if (shouldFreePath) free(path); - lua_pushboolean(L, false); - lua_pushstring(L, strerror(errno)); + lua_pushnil(L); + lua_pushfstring(L, "Can't open directory: %s (%s)", strerror(errno), errno); return 2; } errno = 0; @@ -96,21 +98,17 @@ static int fs_list(lua_State *L) { if (entry->d_type==DT_REG) { // Regular files: check size char* filepath = malloc(strlen(path)+strlen(entry->d_name)+1); - if (filepath == NULL) { + if (filepath == NULL) luaL_error(L, "Memory allocation error"); - } - memset(filepath, 0, strlen(path)+strlen(entry->d_name)+1); - strcpy(filepath, path); strcat(filepath, entry->d_name); + struct stat stats; if (stat(filepath, &stats)) { free(filepath); if (shouldFreePath) free(path); luaL_error(L, "Stat error: %s (%d)", strerror(errno), errno); - lua_pushboolean(L, false); - lua_pushstring(L, strerror(errno)); - return 2; + return 0; } else { lua_pushinteger(L, stats.st_size); } @@ -118,7 +116,7 @@ static int fs_list(lua_State *L) { } else { // Everything else: 0 bytes lua_pushinteger(L, 0); } - lua_setfield(L, -2, "fileSize"); + lua_setfield(L, -2, "size"); lua_seti(L, -2, i); i++; diff --git a/source/httpc.c b/source/httpc.c index ef70afc..97ad4a0 100644 --- a/source/httpc.c +++ b/source/httpc.c @@ -40,6 +40,9 @@ Open an url in the context. @function :open @tparam string url the url to open @tparam[opt="GET"] string method method to use; can be `"GET"`, `"POST"`, `"HEAD"`, `"PUT"` or `"DELETE"` +@treturn[1] boolean `true` if everything went fine +@treturn[2] boolean `false` in case of error +@treturn[2] integer error code */ static int httpc_open(lua_State *L) { httpcContext *context = lua_touserdata(L, 1); @@ -59,7 +62,7 @@ static int httpc_open(lua_State *L) { ret = httpcOpenContext(context, method, url, 0); if (ret != 0) { - lua_pushnil(L); + lua_pushboolean(L, false); lua_pushinteger(L, ret); return 2; } @@ -72,6 +75,9 @@ Add a field in the request header. @function :addRequestHeaderField @tparam string name Name of the field @tparam string value Value of the field +@treturn[1] boolean `true` if everything went fine +@treturn[2] boolean `false` in case of error +@treturn[2] integer error code */ static int httpc_addRequestHeaderField(lua_State *L) { httpcContext *context = lua_touserdata(L, 1); @@ -80,7 +86,7 @@ static int httpc_addRequestHeaderField(lua_State *L) { Result ret = httpcAddRequestHeaderField(context, name ,value); if (ret != 0) { - lua_pushnil(L); + lua_pushboolean(L, false); lua_pushinteger(L, ret); return 2; } @@ -91,6 +97,9 @@ static int httpc_addRequestHeaderField(lua_State *L) { /*** Begin a request to get the content at the URL. @function :beginRequest +@treturn[1] boolean `true` if everything went fine +@treturn[2] boolean `false` in case of error +@treturn[2] integer error code */ static int httpc_beginRequest(lua_State *L) { httpcContext *context = lua_touserdata(L, 1); @@ -98,7 +107,7 @@ static int httpc_beginRequest(lua_State *L) { ret = httpcBeginRequest(context); if (ret != 0) { - lua_pushnil(L); + lua_pushboolean(L, false); lua_pushinteger(L, ret); return 2; } @@ -109,7 +118,9 @@ static int httpc_beginRequest(lua_State *L) { /*** Return the status code returned by the request. @function :getStatusCode -@treturn number the status code +@treturn[1] integer the status code +@treturn[2] nil in case of error +@treturn[2] integer error code */ static int httpc_getStatusCode(lua_State *L) { httpcContext *context = lua_touserdata(L, 1); @@ -143,7 +154,9 @@ static int httpc_getDownloadSize(lua_State *L) { /*** Download and return the data of the context. @function :downloadData -@treturn string data +@treturn[1] string data +@treturn[2] nil in case of error +@treturn[2] integer error code */ static int httpc_downloadData(lua_State *L) { httpcContext *context = lua_touserdata(L, 1); @@ -167,9 +180,9 @@ static int httpc_downloadData(lua_State *L) { } lua_pushstring(L, (char*)buff); - //free(buff); - lua_pushinteger(L, size); // only for test purposes. - return 2; + //free(buff); FIXME we need to free this buffer at some point ? + //lua_pushinteger(L, size); // only for test purposes. + return 1; } /*** @@ -223,6 +236,9 @@ static int httpc_getResponseHeader(lua_State *L) { Add a trusted RootCA cert to a context. @function :addTrustedRootCA @tparam string DER certificate +@treturn[1] boolean `true` if everything went fine +@treturn[2] boolean `false` in case of error +@treturn[2] integer error code */ static int httpc_addTrustedRootCA(lua_State *L) { httpcContext *context = lua_touserdata(L, 1); @@ -231,7 +247,7 @@ static int httpc_addTrustedRootCA(lua_State *L) { Result ret = httpcAddTrustedRootCA(context, cert, certsize); if (ret != 0) { - lua_pushnil(L); + lua_pushboolean(L, false); lua_pushinteger(L, ret); return 2; } diff --git a/source/ir.c b/source/ir.c index 86d85fe..0144bfb 100644 --- a/source/ir.c +++ b/source/ir.c @@ -37,6 +37,9 @@ Bitrate codes list (this is not a part of the module, just a reference) Initialize the IR module. @function init @tparam[opt=6] number bitrate bitrate of the IR module (more informations below) +@treturn[1] boolean `true` if everything went fine +@treturn[2] boolean `false` in case of error +@treturn[2] integer error code */ static int ir_init(lua_State *L) { u8 bitrate = luaL_optinteger(L, 1, 6); @@ -56,6 +59,9 @@ static int ir_init(lua_State *L) { /*** Disable the IR module. @function shutdown +@treturn[1] boolean `true` if everything went fine +@treturn[2] boolean `false` in case of error +@treturn[2] integer error code */ static int ir_shutdown(lua_State *L) { Result ret = IRU_Shutdown(); @@ -74,6 +80,9 @@ Send some data over the IR module. @function send @tparam string data just some data @tparam[opt=false] boolean wait set to `true` to wait until the data is sent. +@treturn[1] boolean `true` if everything went fine +@treturn[2] boolean `false` in case of error +@treturn[2] integer error code */ static int ir_send(lua_State *L) { u8 *data = (u8*)luaL_checkstring(L, 1); @@ -98,7 +107,9 @@ Receive some data from the IR module. @function receive @tparam number size bytes to receive @tparam[opt=false] boolean wait wait until the data is received -@return string data +@treturn[1] string data +@treturn[2] nil in case of error +@treturn[2] integer error code */ static int ir_receive(lua_State *L) { u32 size = luaL_checkinteger(L, 1); @@ -108,7 +119,7 @@ static int ir_receive(lua_State *L) { Result ret = iruRecvData(data, size, 0x00, &transfercount, wait); if (ret) { - lua_pushboolean(L, false); + lua_pushnil(L); lua_pushinteger(L, ret); return 2; } @@ -122,6 +133,9 @@ static int ir_receive(lua_State *L) { Set the bitrate of the communication. @function setBitRate @tparam number bitrate new bitrate for the communication +@treturn[1] boolean `true` if everything went fine +@treturn[2] boolean `false` in case of error +@treturn[2] integer error code */ static int ir_setBitRate(lua_State *L) { u8 bitrate = luaL_checkinteger(L, 1); @@ -133,20 +147,24 @@ static int ir_setBitRate(lua_State *L) { return 2; } - return 0; + lua_pushboolean(L, true); + + return 1; } /*** Return the actual bitrate of the communication. @function getBitRate -@treturn number actual bitrate +@treturn[1] number actual bitrate +@treturn[2] nil in case of error +@treturn[2] integer error code */ static int ir_getBitRate(lua_State *L) { u8 bitrate = 0; Result ret = IRU_GetBitRate(&bitrate); if (ret) { - lua_pushboolean(L, false); + lua_pushnil(L); lua_pushinteger(L, ret); return 2; } diff --git a/source/map.c b/source/map.c index 6509d0c..c92242d 100644 --- a/source/map.c +++ b/source/map.c @@ -1,5 +1,5 @@ /*** -The `map` module. +The `gfx.map` module. Tile coordinates start at x=0,y=0. @module ctr.gfx.map @usage local map = require("ctr.gfx.map") @@ -47,7 +47,9 @@ Load a map from a file. @tparam texture tileset containing the tileset @tparam number tileWidth tile width @tparam number tileHeight tile height -@treturn map loaded map object +@treturn[1] map loaded map object +@treturn[2] nil in case of error +@treturn[2] string error message */ static int map_load(lua_State *L) { texture_userdata *texture = luaL_checkudata(L, 2, "LTexture"); diff --git a/source/mic.c b/source/mic.c index ed9d308..0e3f19a 100644 --- a/source/mic.c +++ b/source/mic.c @@ -20,20 +20,23 @@ u32 bufferSize = 0; Initialize the mic module. @function init @tparam[opt=0x50000] number bufferSize size of the buffer (must be a multiple of 0x1000) +@treturn[1] boolean `true` if everything went fine +@treturn[2] boolean `false` in case of error +@treturn[2] integer/string error code/message */ static int mic_init(lua_State *L) { bufferSize = luaL_optinteger(L, 1, 0x50000); buff = memalign(0x1000, bufferSize); if (buff == NULL) { - lua_pushnil(L); + lua_pushboolean(L, false); lua_pushstring(L, "Couldn't allocate buffer"); return 2; } Result ret = micInit(buff, bufferSize); if (ret) { free(buff); - lua_pushnil(L); + lua_pushboolean(L, false); lua_pushinteger(L, ret); return 2; } diff --git a/source/ptm.c b/source/ptm.c index 438acde..5401fe3 100644 --- a/source/ptm.c +++ b/source/ptm.c @@ -117,7 +117,9 @@ Setup the new 3DS CPU features (overclock, 4 cores ...) @newonly @function configureNew3DSCPU @tparam boolean enable enable the New3DS CPU features -@treturn boolean `true` if everything went fine +@treturn[1] boolean `true` if everything went fine +@treturn[2] boolean `false` in case of error +@treturn[2] integer error code */ static int ptm_configureNew3DSCPU(lua_State *L) { u8 conf = false; diff --git a/source/qtm.c b/source/qtm.c index 1356d0d..dfa9ca4 100644 --- a/source/qtm.c +++ b/source/qtm.c @@ -22,6 +22,9 @@ static const struct luaL_Reg qtm_methods[]; /*** Initialize the QTM module. @function init +@treturn[1] boolean `true` if everything went fine +@treturn[2] boolean `false` in case of error +@treturn[2] integer error code */ static int qtm_init(lua_State *L) { Result ret = qtmInit(); @@ -121,8 +124,10 @@ Convert QTM coordinates to screen coordinates @tparam number coordinates index @tparam[opt=400] number screenWidth specify a screen width @tparam[opt=320] number screenHeight specify a screen height -@treturn number screen X coordinate -@treturn number screen Y coordinate +@treturn[1] number screen X coordinate +@treturn[1] number screen Y coordinate +@treturn[2] nil in case of error +@treturn[2] string error message */ static int qtm_convertCoordToScreen(lua_State *L) { qtm_userdata *info = luaL_checkudata(L, 1, "LQTM"); @@ -130,7 +135,7 @@ static int qtm_convertCoordToScreen(lua_State *L) { index = index - 1; // Lua index begins at 1 if (index > 3 || index < 0) { lua_pushnil(L); - lua_pushnil(L); + lua_pushstring(L, "Index must be between 1 and 3"); return 2; } float screenWidth = luaL_optnumber(L, 3, 400.0f); diff --git a/source/socket.c b/source/socket.c index 7eb61f8..27fa28f 100644 --- a/source/socket.c +++ b/source/socket.c @@ -109,7 +109,9 @@ static int socket_shutdown(lua_State *L) { /*** Return a TCP socket. @function tcp -@treturn TCPMaster TCP socket +@treturn[1] TCPMaster TCP socket +@treturn[2] nil in case of error +@treturn[2] string error message */ static int socket_tcp(lua_State *L) { socket_userdata *userdata = lua_newuserdata(L, sizeof(*userdata)); @@ -134,7 +136,9 @@ static int socket_tcp(lua_State *L) { /*** Return an UDP socket. @function udp -@treturn UDPMaster UDP socket +@treturn[1] UDPMaster UDP socket +@treturn[2] nil in case of error +@treturn[2] string error message */ static int socket_udp(lua_State *L) { socket_userdata *userdata = lua_newuserdata(L, sizeof(*userdata)); @@ -371,7 +375,9 @@ If no data is avaible, it returns an empty string (non-blocking). "a" to receive everything, "l" to receive the next line, skipping the end of line, "L" to receive the next line, keeping the end of line. -@treturn string data +@treturn[1] string data +@treturn[2] nil in case of error +@treturn[2] integer error code */ static int socket_receive(lua_State *L) { socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); @@ -440,7 +446,9 @@ static int socket_receive(lua_State *L) { Send some data over the TCP socket. @function :send @tparam string data data to send -@treturn number amount of data sent +@treturn[1] number amount of data sent +@treturn[2] nil in case of error +@treturn[2] integer/string error code/message */ static int socket_send(lua_State *L) { socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); diff --git a/source/texture.c b/source/texture.c index df87a22..f995386 100644 --- a/source/texture.c +++ b/source/texture.c @@ -43,7 +43,9 @@ Load a texture from a file. Supported formats: PNG, JPEG, BMP. @tparam string path path to the image file @tparam[opt=PLACE_RAM] number place where to put the loaded texture @tparam[opt=auto] number type type of the image -@treturn texture the loaded texture object +@treturn[1] texture the loaded texture object +@treturn[2] nil in case of error +@treturn[2] string error message */ static int texture_load(lua_State *L) { const char *path = luaL_checkstring(L, 1); @@ -272,7 +274,7 @@ Save a texture to a file. @tparam string filename path to the file to save the texture to @tparam[opt=TYPE_PNG] number type type of the image to save. Can be TYPE_PNG or TYPE_BMP @treturn[1] boolean true on success -@treturn[2] nil +@treturn[2] boolean `false` in case of error @treturn[2] string error message */ static int texture_save(lua_State *L) { @@ -284,7 +286,7 @@ static int texture_save(lua_State *L) { if (type == 0) { // PNG FILE* file = fopen(path, "wb"); if (file == NULL) { - lua_pushnil(L); + lua_pushboolean(L, false); lua_pushstring(L, "Can open file"); return 2; } @@ -317,7 +319,7 @@ static int texture_save(lua_State *L) { } else if (type == 2) { // BMP u32* buff = malloc(texture->texture->width * texture->texture->height * 4); if (buff == NULL) { - lua_pushnil(L); + lua_pushboolean(L, false); lua_pushstring(L, "Failed to allocate buffer"); return 2; } @@ -330,13 +332,13 @@ static int texture_save(lua_State *L) { free(buff); } else { - lua_pushnil(L); + lua_pushboolean(L, false); lua_pushstring(L, "Not a valid type"); return 2; } if (result == 0) { - lua_pushnil(L); + lua_pushboolean(L, false); lua_pushstring(L, "Failed to save the texture"); return 2; } From 4d1e3ec455d9ae2593bac57e33e40e4312ec1178 Mon Sep 17 00:00:00 2001 From: Reuh Date: Fri, 22 Apr 2016 16:45:56 +0200 Subject: [PATCH 088/101] Improved map:draw a lot, fixed GC issues with ctr.map, changed some things internally map:draw should be faster and more flexible. --- source/gfx.c | 28 ++++++++++++++------ source/gfx.h | 12 +++++++++ source/map.c | 73 ++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 88 insertions(+), 25 deletions(-) create mode 100644 source/gfx.h diff --git a/source/gfx.c b/source/gfx.c index 447f8df..f464c7e 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -17,6 +17,7 @@ The `gfx` module. #include #include +#include "gfx.h" #include "font.h" #include "texture.h" @@ -27,6 +28,13 @@ typedef struct { bool isGfxInitialized = false; bool is3DEnabled = false; //TODO: add a function for this in the ctrulib/sf2dlib. +// The scissor-test state, as defined in Lua code. When you apply a new scissor in C, remember to get back to this state to avoid unexpected behaviour. +scissor_state lua_scissor = { + GPU_SCISSOR_DISABLE, + 0, 0, + 0, 0 +}; + /*** The `ctr.gfx.color` module. @table color @@ -385,17 +393,21 @@ Calls this function without argument to disable the scissor test. */ static int gfx_scissor(lua_State *L) { if (lua_gettop(L) == 0) { - sf2d_set_scissor_test(GPU_SCISSOR_DISABLE, 0, 0, 0, 0); + lua_scissor.mode = GPU_SCISSOR_DISABLE; + lua_scissor.x = 0; + lua_scissor.y = 0; + lua_scissor.width = 0; + lua_scissor.height = 0; } else { - int x = luaL_checkinteger(L, 1); - int y = luaL_checkinteger(L, 2); - int width = luaL_checkinteger(L, 3); - int height = luaL_checkinteger(L, 4); - bool invert = lua_toboolean(L, 5); - - sf2d_set_scissor_test(invert ? GPU_SCISSOR_INVERT : GPU_SCISSOR_NORMAL, x, y, width, height); + lua_scissor.x = luaL_checkinteger(L, 1); + lua_scissor.y = luaL_checkinteger(L, 2); + lua_scissor.width = luaL_checkinteger(L, 3); + lua_scissor.height = luaL_checkinteger(L, 4); + lua_scissor.mode = lua_toboolean(L, 5) ? GPU_SCISSOR_INVERT : GPU_SCISSOR_NORMAL; } + sf2d_set_scissor_test(lua_scissor.mode, lua_scissor.x, lua_scissor.y, lua_scissor.width, lua_scissor.height); + return 0; } diff --git a/source/gfx.h b/source/gfx.h new file mode 100644 index 0000000..7a7afbe --- /dev/null +++ b/source/gfx.h @@ -0,0 +1,12 @@ +#ifndef GFX_H +#define GFX_H + +typedef struct { + GPU_SCISSORMODE mode; + u32 x; u32 y; + u32 width; u32 height; +} scissor_state; + +extern scissor_state lua_scissor; + +#endif diff --git a/source/map.c b/source/map.c index c92242d..193347d 100644 --- a/source/map.c +++ b/source/map.c @@ -13,7 +13,9 @@ Tile coordinates start at x=0,y=0. #include #include #include +#include +#include "gfx.h" #include "texture.h" typedef struct { @@ -59,7 +61,16 @@ static int map_load(lua_State *L) { map_userdata *map = lua_newuserdata(L, sizeof(map_userdata)); luaL_getmetatable(L, "LMap"); lua_setmetatable(L, -2); - + + // Block GC of the texture by keeping a reference to it in the registry + // registry[map_userdata] = texture_userdata + lua_pushnil(L); + lua_copy(L, -2, -1); // map_userdata + lua_pushnil(L); + lua_copy(L, 2, -1); // texture_userdata + lua_settable(L, LUA_REGISTRYINDEX); + + // Init userdata fields map->texture = texture; map->tileSizeX = tileSizeX; map->tileSizeY = tileSizeY; @@ -158,50 +169,81 @@ Map object */ /*** -Draw a map. +Draw (a part of) the map on the screen. @function :draw -@tparam number x X position -@tparam number y Y position -@within Methods +@tparam integer x X top-left coordinate to draw the map on the screen (pixels) +@tparam integer y Y top-left coordinate to draw the map on the screen (pixels) +@tparam[opt=0] integer offsetX drawn area X start coordinate on the map (pixels) (x=0,y=0 correspond to the first tile top-left corner) +@tparam[opt=0] integer offsetY drawn area Y start coordinate on the map (pixels) +@tparam[opt=400] integer width width of the drawn area on the map (pixels) +@tparam[opt=240] integer height height of the drawn area on the map (pixels) +@usage +-- This will draw on the screen at x=5,y=5 a part of the map. The part is the rectangle on the map starting at x=16,y=16 and width=32,height=48. +-- For example, if you use 16x16 pixel tiles, this will draw the tiles from 1,1 (top-left corner of the rectangle) to 2,3 (bottom-right corner). +map:draw(5, 5, 16, 16, 32, 48) */ static int map_draw(lua_State *L) { map_userdata *map = luaL_checkudata(L, 1, "LMap"); int x = luaL_checkinteger(L, 2); int y = luaL_checkinteger(L, 3); + int offsetX = luaL_optinteger(L, 4, 0); + int offsetY = luaL_optinteger(L, 5, 0); + int width = luaL_optinteger(L, 6, 400); + int height = luaL_optinteger(L, 7, 240); + + int xI = fmax(floor((double)offsetX / map->tileSizeX), 0); // initial tile X + int xF = fmin(ceil((double)(offsetX + width) / map->tileSizeX), map->width); // final tile X + + int yI = fmax(floor((double)offsetY / map->tileSizeY), 0); // initial tile Y + int yF = fmin(ceil((double)(offsetY + height) / map->tileSizeY), map->height); // final tile Y + + if (sf2d_get_current_screen() == GFX_TOP) + sf2d_set_scissor_test(GPU_SCISSOR_NORMAL, x, y, fmin(width, 400 - x), fmin(height, 240 - y)); // Scissor test doesn't work when x/y + width > screenWidth/Height + else + sf2d_set_scissor_test(GPU_SCISSOR_NORMAL, x, y, fmin(width, 320 - x), fmin(height, 240 - y)); + int texX = 0; int texY = 0; - + if (map->texture->blendColor == 0xffffffff) { - for (int xp=0; xpwidth; xp++) { - for (int yp=0; ypheight; yp++) { + for (int xp = xI; xp < xF; xp++) { + for (int yp = yI; yp < yF; yp++) { u16 tile = getTile(map, xp, yp); getTilePos(map, tile, &texX, &texY); - sf2d_draw_texture_part(map->texture->texture, (x+(map->tileSizeX*xp)+(xp*map->spaceX)), (y+(map->tileSizeY*yp)+(yp*map->spaceY)), texX, texY, map->tileSizeX, map->tileSizeY); + sf2d_draw_texture_part(map->texture->texture, x+(map->tileSizeX*xp)+(xp*map->spaceX)-offsetX, y+(map->tileSizeY*yp)+(yp*map->spaceY)-offsetY, texX, texY, map->tileSizeX, map->tileSizeY); } } } else { - for (int xp=0; xpwidth; xp++) { - for (int yp=0; ypheight; yp++) { + for (int xp = xI; xp < xF; xp++) { + for (int yp = yI; yp < yF; yp++) { u16 tile = getTile(map, xp, yp); getTilePos(map, tile, &texX, &texY); - sf2d_draw_texture_part_blend(map->texture->texture, (x+(map->tileSizeX*xp)+(xp*map->spaceX)), (y+(map->tileSizeY*yp)+(yp*map->spaceY)), texX, texY, map->tileSizeX, map->tileSizeY, map->texture->blendColor); + sf2d_draw_texture_part_blend(map->texture->texture, x+(map->tileSizeX*xp)+(xp*map->spaceX)-offsetX, y+(map->tileSizeY*yp)+(yp*map->spaceY)-offsetY, texX, texY, map->tileSizeX, map->tileSizeY, map->texture->blendColor); } } } - + + sf2d_set_scissor_test(lua_scissor.mode, lua_scissor.x, lua_scissor.y, lua_scissor.width, lua_scissor.height); + return 0; } /*** Unload a map. @function :unload -@within Methods */ static int map_unload(lua_State *L) { map_userdata *map = luaL_checkudata(L, 1, "LMap"); free(map->data); + // Remove the reference to the texture in the registry + // registry[map_userdata] = nil + lua_pushnil(L); + lua_copy(L, 1, -1); // map_userdata + lua_pushnil(L); + lua_settable(L, LUA_REGISTRYINDEX); + return 0; } @@ -210,7 +252,6 @@ Return the size of a map. @function :getSize @treturn number width of the map, in tiles @treturn number height of the map, in tiles -@within Methods */ static int map_getSize(lua_State *L) { map_userdata *map = luaL_checkudata(L, 1, "LMap"); @@ -227,7 +268,6 @@ Return the value of a tile. @tparam number x X position of the tile (in tiles) @tparam number y Y position of the tile (in tiles) @treturn number value of the tile -@within Methods */ static int map_getTile(lua_State *L) { map_userdata *map = luaL_checkudata(L, 1, "LMap"); @@ -245,7 +285,6 @@ Set the value of a tile. @tparam number x X position of the tile (in tiles) @tparam number y Y position of the tile (in tiles) @tparam number value new value for the tile -@within Methods */ static int map_setTile(lua_State *L) { map_userdata *map = luaL_checkudata(L, 1, "LMap"); From ac47b1d981118dd2ac670286f3deda76834ae85b Mon Sep 17 00:00:00 2001 From: Reuh Date: Sun, 24 Apr 2016 18:21:21 +0200 Subject: [PATCH 089/101] Added missing documentation in texture.load --- source/texture.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/texture.c b/source/texture.c index f995386..ee71ad3 100644 --- a/source/texture.c +++ b/source/texture.c @@ -38,11 +38,11 @@ int getType(const char *name) { // module functions /*** -Load a texture from a file. Supported formats: PNG, JPEG, BMP. +Load a texture from a file. Supported formats: PNG, JPEG, BMP, GIF, PSD, TGA, HDR, PIC, PNM. @function load @tparam string path path to the image file @tparam[opt=PLACE_RAM] number place where to put the loaded texture -@tparam[opt=auto] number type type of the image +@tparam[opt=auto] number type type of the image. This is only used to force loading PNG, JPEG or BMP files with the sfil library; any value between 5 and 250 will force using the stbi library. Leave nil to autodetect the format. @treturn[1] texture the loaded texture object @treturn[2] nil in case of error @treturn[2] string error message From d0fb7042059b76af9ee9aefdc4a325f20b5a03e8 Mon Sep 17 00:00:00 2001 From: Reuh Date: Mon, 25 Apr 2016 19:43:09 +0200 Subject: [PATCH 090/101] Added hotspot arguments when drawing rotated textures Did related changes to sprite.lua and cleaned stuff. I had to add a function to sf2dlib to make this work, so a make build-sf2dlib is required. Also, we should probably send this change to the original sf2dlib repository... --- libs/sf2dlib/libsf2d/include/sf2d.h | 18 ++++ libs/sf2dlib/libsf2d/source/sf2d_texture.c | 24 +++-- sdcard/3ds/ctruLua/libs/sprite.lua | 10 ++- source/texture.c | 100 +++++++++++++-------- 4 files changed, 103 insertions(+), 49 deletions(-) diff --git a/libs/sf2dlib/libsf2d/include/sf2d.h b/libs/sf2dlib/libsf2d/include/sf2d.h index f282d4f..9322ebb 100644 --- a/libs/sf2dlib/libsf2d/include/sf2d.h +++ b/libs/sf2dlib/libsf2d/include/sf2d.h @@ -588,6 +588,24 @@ void sf2d_draw_texture_part_rotate_scale(const sf2d_texture *texture, int x, int */ void sf2d_draw_texture_part_rotate_scale_blend(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale, u32 color); +/** + * @brief Draws a part of a texture, with rotation, scaling, color and hotspot + * @param texture the texture to draw + * @param x the x coordinate to draw the texture to + * @param y the y coordinate to draw the texture to + * @param rad rotation (in radians) to draw the texture + * @param tex_x the starting point (x coordinate) where to start drawing + * @param tex_y the starting point (y coordinate) where to start drawing + * @param tex_w the width to draw from the starting point + * @param tex_h the height to draw from the starting point + * @param x_scale the x scale + * @param y_scale the y scale + * @param center_x the x position of the hotspot + * @param center_y the y position of the hotspot + * @param color the color to blend with the texture + */ +void sf2d_draw_texture_part_rotate_scale_hotspot_blend(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale, float center_x, float center_y, u32 color); + /** * @brief Draws a texture blended in a certain depth * @param texture the texture to draw diff --git a/libs/sf2dlib/libsf2d/source/sf2d_texture.c b/libs/sf2dlib/libsf2d/source/sf2d_texture.c index 13345e9..12469ed 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d_texture.c +++ b/libs/sf2dlib/libsf2d/source/sf2d_texture.c @@ -599,18 +599,18 @@ void sf2d_draw_texture_part_scale_blend(const sf2d_texture *texture, float x, fl sf2d_draw_texture_part_scale_generic(texture, x, y, tex_x, tex_y, tex_w, tex_h, x_scale, y_scale); } -static inline void sf2d_draw_texture_part_rotate_scale_generic(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale) +static inline void sf2d_draw_texture_part_rotate_scale_hotspot_generic(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale, float center_x, float center_y) { sf2d_vertex_pos_tex *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_tex), 8); if (!vertices) return; - int w2 = (tex_w * x_scale)/2.0f; - int h2 = (tex_h * y_scale)/2.0f; + int w = tex_w; + int h = tex_h; - vertices[0].position = (sf2d_vector_3f){(float)-w2, (float)-h2, SF2D_DEFAULT_DEPTH}; - vertices[1].position = (sf2d_vector_3f){(float) w2, (float)-h2, SF2D_DEFAULT_DEPTH}; - vertices[2].position = (sf2d_vector_3f){(float)-w2, (float) h2, SF2D_DEFAULT_DEPTH}; - vertices[3].position = (sf2d_vector_3f){(float) w2, (float) h2, SF2D_DEFAULT_DEPTH}; + vertices[0].position = (sf2d_vector_3f){(float)-center_x * x_scale, (float)-center_y * y_scale, SF2D_DEFAULT_DEPTH}; + vertices[1].position = (sf2d_vector_3f){(float) (w - center_x) * x_scale, (float)-center_y * y_scale, SF2D_DEFAULT_DEPTH}; + vertices[2].position = (sf2d_vector_3f){(float)-center_x * x_scale, (float) (h - center_y) * y_scale, SF2D_DEFAULT_DEPTH}; + vertices[3].position = (sf2d_vector_3f){(float) (w - center_x) * x_scale, (float) h - center_y * y_scale, SF2D_DEFAULT_DEPTH}; float u0 = tex_x/(float)texture->pow2_w; float v0 = tex_y/(float)texture->pow2_h; @@ -650,13 +650,19 @@ static inline void sf2d_draw_texture_part_rotate_scale_generic(const sf2d_textur void sf2d_draw_texture_part_rotate_scale(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale) { sf2d_bind_texture(texture, GPU_TEXUNIT0); - sf2d_draw_texture_part_rotate_scale_generic(texture, x, y, rad, tex_x, tex_y, tex_w, tex_h, x_scale, y_scale); + sf2d_draw_texture_part_rotate_scale_hotspot_generic(texture, x, y, rad, tex_x, tex_y, tex_w, tex_h, x_scale, y_scale, tex_w/2.0f, tex_h/2.0f); } void sf2d_draw_texture_part_rotate_scale_blend(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale, u32 color) { sf2d_bind_texture_color(texture, GPU_TEXUNIT0, color); - sf2d_draw_texture_part_rotate_scale_generic(texture, x, y, rad, tex_x, tex_y, tex_w, tex_h, x_scale, y_scale); + sf2d_draw_texture_part_rotate_scale_hotspot_generic(texture, x, y, rad, tex_x, tex_y, tex_w, tex_h, x_scale, y_scale, tex_w/2.0f, tex_h/2.0f); +} + +void sf2d_draw_texture_part_rotate_scale_hotspot_blend(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale, float center_x, float center_y, u32 color) +{ + sf2d_bind_texture_color(texture, GPU_TEXUNIT0, color); + sf2d_draw_texture_part_rotate_scale_hotspot_generic(texture, x, y, rad, tex_x, tex_y, tex_w, tex_h, x_scale, y_scale, center_x, center_y); } static inline void sf2d_draw_texture_depth_generic(const sf2d_texture *texture, int x, int y, signed short z) diff --git a/sdcard/3ds/ctruLua/libs/sprite.lua b/sdcard/3ds/ctruLua/libs/sprite.lua index aec1995..621e4d8 100644 --- a/sdcard/3ds/ctruLua/libs/sprite.lua +++ b/sdcard/3ds/ctruLua/libs/sprite.lua @@ -26,7 +26,7 @@ local function draw(self, x, y, rad) local tsx, tsy = self.texture:getSize() local sx, sy = getBox(tsx, tsy, frame, self.frameSizeX, self.frameSizeY) - self.texture:drawPart(x, y, sx, sy, self.frameSizeX, self.frameSizeY, rad) + self.texture:drawPart(x, y, sx, sy, self.frameSizeX, self.frameSizeY, rad, self.offsetX, self.offsetY) return frame end @@ -52,12 +52,19 @@ local function resetTimer(self) self.frameTimer = ctr.time() end +local function setOffset(self, x, y) + self.offsetX = x or 0 + self.offsetY = y or self.offsetX +end + -- Sprite object constructor function mod.new(texture, fsx, fsy) return { texture = texture, frameSizeX = fsx, frameSizeY = fsy, + offsetX = 0, + offsetY = 0, animations = {}, currentAnimation = 0, currentFrame = 1, @@ -66,6 +73,7 @@ function mod.new(texture, fsx, fsy) draw = draw, addAnimation = addAnimation, setAnimation = setAnimation, + setOffset = setOffset, resetTimer = resetTimer, } end diff --git a/source/texture.c b/source/texture.c index f995386..84467de 100644 --- a/source/texture.c +++ b/source/texture.c @@ -54,10 +54,10 @@ static int texture_load(lua_State *L) { texture_userdata *texture; texture = (texture_userdata *)lua_newuserdata(L, sizeof(*texture)); - + luaL_getmetatable(L, "LTexture"); lua_setmetatable(L, -2); - + if (type==3) type = getType(path); if (type==0) { //PNG texture->texture = sfil_load_PNG_file(path, place); @@ -76,17 +76,17 @@ static int texture_load(lua_State *L) { texture->texture = sf2d_create_texture_mem_RGBA8(data, w, h, TEXFMT_RGBA8, place); free(data); } - + if (texture->texture == NULL) { lua_pushnil(L); lua_pushstring(L, "No such file"); return 2; } - + texture->scaleX = 1.0f; texture->scaleY = 1.0f; texture->blendColor = 0xffffffff; - + return 1; } @@ -102,20 +102,20 @@ static int texture_new(lua_State *L) { int w = luaL_checkinteger(L, 1); int h = luaL_checkinteger(L, 2); u8 place = luaL_checkinteger(L, 3); - + texture_userdata *texture; texture = (texture_userdata *)lua_newuserdata(L, sizeof(*texture)); - + luaL_getmetatable(L, "LTexture"); lua_setmetatable(L, -2); - + texture->texture = sf2d_create_texture(w, h, TEXFMT_RGBA8, place); sf2d_texture_tile32(texture->texture); - + texture->scaleX = 1.0f; texture->scaleY = 1.0f; texture->blendColor = 0xffffffff; - + return 1; } @@ -127,35 +127,41 @@ Texture object /*** Draw a texture. @function :draw -@tparam number x X position -@tparam number y Y position -@tparam[opt=0.0] number rad rotation of the texture (in radians) +@tparam integer x X position +@tparam integer y Y position +@tparam[opt=0.0] number rad rotation of the texture around the hotspot (in radians) +@tparam[opt=0.0] number hotspotX the hostpot X coordinate +@tparam[opt=0.0] number hotspotY the hostpot Y coordinate */ static int texture_draw(lua_State *L) { texture_userdata *texture = luaL_checkudata(L, 1, "LTexture"); int x = luaL_checkinteger(L, 2); int y = luaL_checkinteger(L, 3); float rad = luaL_optnumber(L, 4, 0.0f); - + float hotspotX = luaL_optnumber(L, 5, 0.0f); + float hotspotY = luaL_optnumber(L, 6, 0.0f); + if (rad == 0.0f && texture->scaleX == 1.0f && texture->scaleY == 1.0f && texture->blendColor == 0xffffffff) { - sf2d_draw_texture(texture->texture, x, y); + sf2d_draw_texture(texture->texture, x - hotspotX, y - hotspotY); } else { - sf2d_draw_texture_part_rotate_scale_blend(texture->texture, x, y, rad, 0, 0, texture->texture->width, texture->texture->height, texture->scaleX, texture->scaleY, texture->blendColor); + sf2d_draw_texture_rotate_scale_hotspot_blend(texture->texture, x, y, rad, texture->scaleX, texture->scaleY, hotspotX, hotspotY, texture->blendColor); } - + return 0; } /*** Draw a part of the texture @function :drawPart -@tparam number x X position -@tparam number y Y position -@tparam number sx X position of the beginning of the part -@tparam number sy Y position of the beginning of the part -@tparam number w width of the part -@tparam number h height of the part -@tparam[opt=0.0] number rad rotation of the part (in radians) +@tparam integer x X position +@tparam integer y Y position +@tparam integer sx X position of the beginning of the part +@tparam integer sy Y position of the beginning of the part +@tparam integer w width of the part +@tparam integer h height of the part +@tparam[opt=0.0] number rad rotation of the part around the hotspot (in radians) +@tparam[opt=0.0] number hotspotX the hostpot X coordinate +@tparam[opt=0.0] number hotspotY the hostpot Y coordinate */ static int texture_drawPart(lua_State *L) { texture_userdata *texture = luaL_checkudata(L, 1, "LTexture"); @@ -166,9 +172,11 @@ static int texture_drawPart(lua_State *L) { int w = luaL_checkinteger(L, 6); int h = luaL_checkinteger(L, 7); int rad = luaL_optnumber(L, 8, 0.0f); - - sf2d_draw_texture_part_rotate_scale_blend(texture->texture, x, y, rad, sx, sy, w, h, texture->scaleX, texture->scaleY, texture->blendColor); - + float hotspotX = luaL_optnumber(L, 9, 0.0f); + float hotspotY = luaL_optnumber(L, 10, 0.0f); + + sf2d_draw_texture_part_rotate_scale_hotspot_blend(texture->texture, x, y, rad, sx, sy, w, h, texture->scaleX, texture->scaleY, hotspotX, hotspotY, texture->blendColor); + return 0; } @@ -193,12 +201,12 @@ Unload a texture. */ static int texture_unload(lua_State *L) { texture_userdata *texture = luaL_checkudata(L, 1, "LTexture"); - + if (texture->texture == NULL) return 0; sf2d_free_texture(texture->texture); texture->texture = NULL; - + return 0; } @@ -206,16 +214,16 @@ static int texture_unload(lua_State *L) { Rescale the texture. The default scale is `1.0`. @function :scale @tparam number scaleX new scale of the width -@tparam number scaleY new scale of the height +@tparam[opt=scaleX] number scaleY new scale of the height */ static int texture_scale(lua_State *L) { texture_userdata *texture = luaL_checkudata(L, 1, "LTexture"); float sx = luaL_checknumber(L, 2); - float sy = luaL_checknumber(L, 3); - + float sy = luaL_optnumber(L, 3, sx); + texture->scaleX = sx; texture->scaleY = sy; - + return 0; } @@ -230,9 +238,9 @@ static int texture_getPixel(lua_State *L) { texture_userdata *texture = luaL_checkudata(L, 1, "LTexture"); int x = luaL_checkinteger(L, 2); int y = luaL_checkinteger(L, 3); - + lua_pushinteger(L, sf2d_get_pixel(texture->texture, x, y)); - + return 1; } @@ -248,9 +256,9 @@ static int texture_setPixel(lua_State *L) { int x = luaL_checkinteger(L, 2); int y = luaL_checkinteger(L, 3); u32 color = luaL_checkinteger(L, 4); - + sf2d_set_pixel(texture->texture, x, y, color); - + return 0; } @@ -262,12 +270,25 @@ Set the blend color of the texture. static int texture_setBlendColor(lua_State *L) { texture_userdata *texture = luaL_checkudata(L, 1, "LTexture"); u32 color = luaL_checkinteger(L, 2); - + texture->blendColor = color; - + return 0; } +/*** +Get the blend color of the texture. +@function :getBlendColor +@treturn number the blend color +*/ +static int texture_getBlendColor(lua_State *L) { + texture_userdata *texture = luaL_checkudata(L, 1, "LTexture"); + + lua_pushinteger(L, texture->blendColor); + + return 1; +} + /*** Save a texture to a file. @function :save @@ -357,6 +378,7 @@ static const struct luaL_Reg texture_methods[] = { { "getPixel", texture_getPixel }, { "setPixel", texture_setPixel }, { "setBlendColor", texture_setBlendColor }, + { "getBlendColor", texture_getBlendColor }, { "save", texture_save }, { "__gc", texture_unload }, {NULL, NULL} From f554f53a47d08ce79e0dfb153b48ae91180fc45c Mon Sep 17 00:00:00 2001 From: Reuh Date: Mon, 25 Apr 2016 21:02:32 +0200 Subject: [PATCH 091/101] ctr.time() returns a negative value; updated documentation and sprite.lua --- sdcard/3ds/ctruLua/libs/sprite.lua | 2 +- source/ctr.c | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/sdcard/3ds/ctruLua/libs/sprite.lua b/sdcard/3ds/ctruLua/libs/sprite.lua index 621e4d8..7e82555 100644 --- a/sdcard/3ds/ctruLua/libs/sprite.lua +++ b/sdcard/3ds/ctruLua/libs/sprite.lua @@ -68,7 +68,7 @@ function mod.new(texture, fsx, fsy) animations = {}, currentAnimation = 0, currentFrame = 1, - frameTimer = 0, + frameTimer = ctr.time(), draw = draw, addAnimation = addAnimation, diff --git a/source/ctr.c b/source/ctr.c index c545a84..80d36f1 100644 --- a/source/ctr.c +++ b/source/ctr.c @@ -140,11 +140,23 @@ static int ctr_run(lua_State *L) { } /*** -Return the number of milliseconds since 1st Jan 1900 00:00. +Return the number of milliseconds spent since some point in time. +This can be used to measure a duration with milliseconds precision; however this can't be used to get the current time or date. +See Lua's os.date() for this use. +For various reasons (see the C source), this will actually returns a negative value. @function time @treturn number milliseconds +@usage +-- Measuring a duration: +local startTime = ctr.time() +-- do stuff +local duration = ctr.time() - startTime */ static int ctr_time(lua_State *L) { + // osGetTime actually returns the number of seconds elapsed since 1st Jan 1900 00:00. + // However, it returns a u64, we build Lua with 32bits numbers, and every number is signed in Lua, so this obvioulsy doesn't work + // and actually returns a negative value. It still works for durations however. Because having the date and time with millisecond-presion + // doesn't really seem useful and changing ctrµLua's API to work on 64bits numbers will take a long time, we choosed to keep this as-is. lua_pushinteger(L, osGetTime()); return 1; From 707b1a451ee79c546f0363e01c17a4dd11277902 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Tue, 26 Apr 2016 14:44:18 +0200 Subject: [PATCH 092/101] Added the ctr.uds lib (3DS-to-3DS wireless communication), Added a function to add ssl certificates for HTTP contexts, Minor documentation update. The UDS lib is completely untested, because I only have 1 3DS. --- source/ctr.c | 9 + source/httpc.c | 17 ++ source/socket.c | 2 +- source/uds.c | 589 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 616 insertions(+), 1 deletion(-) create mode 100644 source/uds.c diff --git a/source/ctr.c b/source/ctr.c index 80d36f1..62e1a38 100644 --- a/source/ctr.c +++ b/source/ctr.c @@ -128,6 +128,14 @@ The `ctr.thread` module. */ void load_thread_lib(lua_State *L); +/*** +The `ctr.uds` module. +@table uds +@see ctr.uds +*/ +void load_uds_lib(lua_State *L); +void unload_uds_lib(lua_State *L); + /*** Return whether or not the program should continue. @function run @@ -198,6 +206,7 @@ struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } { "apt", load_apt_lib, unload_apt_lib }, { "mic", load_mic_lib, NULL }, { "thread", load_thread_lib, NULL }, + { "uds", load_uds_lib, unload_uds_lib }, { NULL, NULL, NULL } }; diff --git a/source/httpc.c b/source/httpc.c index 97ad4a0..d114445 100644 --- a/source/httpc.c +++ b/source/httpc.c @@ -275,6 +275,22 @@ static int httpc_setSSLOptions(lua_State *L) { return 0; } +/*** +Add all the default certificates to the context. +@function addDefaultCert +*/ +static int httpc_addDefaultCert(lua_State *L) { + httpcContext *context = lua_touserdata(L, 1); + + httpcAddDefaultCert(context, SSLC_DefaultRootCert_CyberTrust); + httpcAddDefaultCert(context, SSLC_DefaultRootCert_AddTrust_External_CA); + httpcAddDefaultCert(context, SSLC_DefaultRootCert_COMODO); + httpcAddDefaultCert(context, SSLC_DefaultRootCert_USERTrust); + httpcAddDefaultCert(context, SSLC_DefaultRootCert_DigiCert_EV); + + return 0; +} + // object static const struct luaL_Reg httpc_methods[] = { {"open", httpc_open }, @@ -289,6 +305,7 @@ static const struct luaL_Reg httpc_methods[] = { {"getResponseHeader", httpc_getResponseHeader }, {"addTrustedRootCA", httpc_addTrustedRootCA }, {"setSSLOptions", httpc_setSSLOptions }, + {"addDefaultCert", httpc_addDefaultCert }, {NULL, NULL} }; diff --git a/source/socket.c b/source/socket.c index 27fa28f..08d30c8 100644 --- a/source/socket.c +++ b/source/socket.c @@ -92,7 +92,7 @@ static int socket_init(lua_State *L) { } /*** -Disable the socket module. Must be called before exiting ctrµLua. +Disable the socket module. @function shutdown */ static int socket_shutdown(lua_State *L) { diff --git a/source/uds.c b/source/uds.c new file mode 100644 index 0000000..3139fbf --- /dev/null +++ b/source/uds.c @@ -0,0 +1,589 @@ +/*** +The `uds` module. Used for 3DS-to-3DS wireless communication. +The default wlancommID is 0x637472c2. +@module ctr.uds +@usage local uds = require("ctr.uds") +*/ + +#include +#include +#include + +#include <3ds/result.h> +#include <3ds/types.h> +#include <3ds/services/uds.h> + +#include +#include + +bool initStateUDS = false; + +udsBindContext bind = {0}; +udsNetworkStruct network = {0}; +u8 data_channel = 1; + +/*** +Initialize the UDS module. +@function init +@tparam[opt=0x3000] number context size in bytes, must be a multiple of 0x1000 +@tparam[opt=3DS username] string username UTF-8 username on the network +@treturn[1] boolean `true` on success +@treturn[2] boolean `false` on error +@treturn[2] number error code +*/ +static int uds_init(lua_State *L) { + if (!initStateUDS) { + size_t memSize = luaL_optinteger(L, 1, 0x3000); + const char* username = luaL_optstring(L, 2, NULL); + + Result ret = udsInit(memSize, username); + if (R_FAILED(ret)) { + lua_pushboolean(L, false); + lua_pushinteger(L, ret); + return 2; + } + initStateUDS = true; + } + + lua_pushboolean(L, true); + return 1; +} + +/*** +Disable the UDS module. +@function shutdown +*/ +static int uds_shutdown(lua_State *L) { + udsExit(); + initStateUDS = false; + + return 0; +} + +/*** +Scan for network beacons. +@function scan +@tparam[opt=0x637472c2] number commID application local-WLAN unique ID +@tparam[opt=0] number id8 additional ID to use different network types +@tparam[opt=all] string hostMAC if set, only scan networks from this MAC address (format: `XX:XX:XX:XX:XX:XX`, with hexadecimal values) +@treturn[1] table a table containing beacons objects +@treturn[2] nil +@treturn[2] number/string error code +*/ +static int uds_scan(lua_State *L) { + static const size_t tmpbuffSize = 0x4000; + u32* tmpbuff = malloc(tmpbuffSize); + + udsNetworkScanInfo* networks = NULL; + size_t totalNetworks = 0; + + u32 wlanCommID = luaL_optinteger(L, 1, 0x637472c2); + u8 id8 = luaL_optinteger(L, 2, 0); + + // MAC address conversion + const char* hostMACString = luaL_optstring(L, 3, NULL); + u8* hostMAC; + if (hostMACString != NULL) { + hostMAC = malloc(6*sizeof(u8)); + unsigned int tmpMAC[6]; + if (sscanf(hostMACString, "%x:%x:%x:%x:%x:%x", &tmpMAC[0], &tmpMAC[1], &tmpMAC[2], &tmpMAC[3], &tmpMAC[4], &tmpMAC[5]) != 6) { + free(tmpbuff); + free(hostMAC); + lua_pushnil(L); + lua_pushstring(L, "Bad MAC formating"); + return 2; + } + for (int i=0;i<6;i++) { + hostMAC[i] = tmpMAC[i]; + } + } else { + hostMAC = NULL; + } + + udsConnectionStatus status; + udsGetConnectionStatus(&status); + Result ret = udsScanBeacons(tmpbuff, tmpbuffSize, &networks, &totalNetworks, wlanCommID, id8, hostMAC, (status.status!=0x03)); + free(tmpbuff); + free(hostMAC); + if (R_FAILED(ret)) { + lua_pushnil(L); + lua_pushinteger(L, ret); + return 2; + } + + // Convert the networks to a table of userdatas + lua_createtable(L, 0, totalNetworks); + for (int i=1;i<=totalNetworks;i++) { + udsNetworkScanInfo* beacon = lua_newuserdata(L, sizeof(udsNetworkScanInfo)); + luaL_getmetatable(L, "LUDSBeaconScan"); + lua_setmetatable(L, -2); + memcpy(beacon, &networks[0], sizeof(udsNetworkScanInfo)); + lua_seti(L, -3, i); + } + free(networks); + + return 1; +} + +/*** +Check for data in the receive buffer. +@function available +@treturn boolean `true` if there is data to receive, `false` if not +*/ +static int uds_available(lua_State *L) { + lua_pushboolean(L, udsWaitDataAvailable(&bind, false, false)); + return 1; +} + +/*** +Return a packet from the receive buffer. +@function receive +@tparam[opt=maximum] number size maximum size of the data to receive +@treturn string data, can be an empty string +@treturn number source node, 0 if nothing has been received +*/ +static int uds_receive(lua_State *L) { + size_t maxSize = luaL_optinteger(L, 1, UDS_DATAFRAME_MAXSIZE); + char* buff = malloc(maxSize); + if (buff == NULL) luaL_error(L, "Memory allocation error"); + size_t received = 0; + u16 sourceID = 0; + + Result ret = udsPullPacket(&bind, buff, maxSize, &received, &sourceID); + if (R_FAILED(ret)) { + free(buff); + lua_pushnil(L); + lua_pushinteger(L, ret); + return 2; + } + + lua_pushlstring(L, buff, received); + free(buff); + lua_pushinteger(L, sourceID); + return 2; +} + +/*** +Send a packet to a node. +@function send +@tparam string data data to send +@tparam[opt=BROADCAST] number nodeID nodeID to send the packet to +*/ +static int uds_send(lua_State *L) { + size_t size = 0; + const char *buff = luaL_checklstring(L, 1, &size); + u16 nodeID = luaL_optinteger(L, 2, UDS_BROADCAST_NETWORKNODEID); + + udsSendTo(nodeID, data_channel, 0, buff, size); + + return 0; +} + +/*** +Return information about nodes in the networks +@function getNodesInfo +@treturn tablea table containing nodes informations, as tables (not userdatas). +A node table is like: `{ username = "azerty", nodeID = 12 }` +*/ +static int uds_getNodesInfo(lua_State *L) { + lua_newtable(L); + for (int i=0;inetwork, passphrase, passphraseSize, &bind, recvNetworkNodeID, connType, dataChannel, recvBufferSize); + if (R_FAILED(ret)) { + lua_pushnil(L); + lua_pushinteger(L, ret); + return 2; + } + + lua_pushboolean(L, true); + return 1; +} + +/*** +Disconnect from the network. +@function disconnect +*/ +static int uds_disconnect(lua_State *L) { + udsDisconnectNetwork(); + udsUnbind(&bind); + + return 0; +} + +/*** +Server part +@section server +*/ + +/*** +Create a network. +@function createNetwork +@tparam[opt=""] string passphrase passphrase of the network +@tparam[opt=16] number maxNodes maximum number of nodes that can be connected to the network, including the host (max 16) +@tparam[opt=0x637472c2] number commID application local-WLAN unique ID +@tparam[opt=default] number recvBuffSize size of the buffer that receives data +@tparam[opt=1] number dataChannel data channel of the network; this should be 1 +@treturn[1] boolean `true` on success +@treturn[2] boolean `false` on error +@treturn[2] number error code +*/ +static int uds_createNetwork(lua_State *L) { + size_t passSize = 0; + const char *pass = luaL_optlstring(L, 1, "", &passSize); + u8 maxNodes = luaL_optinteger(L, 2, UDS_MAXNODES); + u32 commID = luaL_optinteger(L, 3, 0x637472c2); + u32 recvBuffSize = luaL_optinteger(L, 4, UDS_DEFAULT_RECVBUFSIZE); + u8 dataChannel = luaL_optinteger(L, 5, 1); + + udsGenerateDefaultNetworkStruct(&network, commID, dataChannel, maxNodes); + Result ret = udsCreateNetwork(&network, pass, passSize, &bind, dataChannel, recvBuffSize); + if (R_FAILED(ret)) { + lua_pushboolean(L, false); + lua_pushinteger(L, ret); + return 2; + } + + lua_pushboolean(L, 1); + return 1; +} + +/*** +Set the application data of the created network. +@function setAppData +@tparam string appData application data +@treturn[1] boolean `true` on success +@treturn[2] boolean `false` on error +@treturn[2] number error code +*/ +static int uds_setAppData(lua_State *L) { + size_t size = 0; + const char* data = luaL_checklstring(L, 1, &size); + + Result ret = udsSetApplicationData(data, size); + if (R_FAILED(ret)) { + lua_pushboolean(L, false); + lua_pushinteger(L, ret); + return 2; + } + + lua_pushboolean(L, true); + return 1; +} + +/*** +Destroy the network. +@function destroyNetwork +*/ +static int uds_destroyNetwork(lua_State *L) { + udsDestroyNetwork(); + udsUnbind(&bind); + + return 0; +} + +/*** +Eject all the spectators connected to the network. +@function ejectSpectators +@tparam[opt=false] boolean reallow set to `true` to still allow the spectators to connect +*/ +static int uds_ejectSpectators(lua_State *L) { + bool reallow = false; + if (lua_isboolean(L, 1)) + reallow = lua_toboolean(L, 1); + + udsEjectSpectator(); + + if (reallow) + udsAllowSpectators(); + + return 0; +} + +/*** +Eject a client connected to the network. +@function ejectClient +@tparam[opt=BROADCAST] number nodeID node ID of the client to eject, or `BROADCAST` for all the clients +*/ +static int uds_ejectClient(lua_State *L) { + u16 nodeID = luaL_optinteger(L, 1, UDS_BROADCAST_NETWORKNODEID); + + udsEjectClient(nodeID); + + return 0; +} + +/*** +beaconScan +@section beaconScan +*/ +static const struct luaL_Reg beaconScan_methods[]; + +static int beaconScan___index(lua_State *L) { + udsNetworkScanInfo* beacon = luaL_checkudata(L, 1, "LUDSBeaconScan"); + + if (lua_isstring(L, 2)) { + const char* index = luaL_checkstring(L, 2); + /*** + @tfield integer channel Wifi channel of the beacon + */ + if (!strcmp(index, "channel")) { + lua_pushinteger(L, beacon->datareply_entry.channel); + return 1; + /*** + @tfield string mac MAC address of the beacon (`mac` for lowercase, `MAC` for uppercase) + @usage +beacon.mac -> ab:cd:ef:ab:cd:ef +beacon.MAC -> AB:CD:EF:AB:CD:EF + */ + } else if (!strcmp(index, "mac") && !strcmp(index, "MAC")) { + char* macString = malloc(18); + if (macString == NULL) luaL_error(L, "Out of memory"); + if (index[1] == 'm') { // lowercase + sprintf(macString, "%0x2:%0x2:%0x2:%0x2:%0x2:%0x2", beacon->datareply_entry.mac_address[0], beacon->datareply_entry.mac_address[1], beacon->datareply_entry.mac_address[2], beacon->datareply_entry.mac_address[3], beacon->datareply_entry.mac_address[4], beacon->datareply_entry.mac_address[5]); + } else { + sprintf(macString, "%0X2:%0X2:%0X2:%0X2:%0X2:%0X2", beacon->datareply_entry.mac_address[0], beacon->datareply_entry.mac_address[1], beacon->datareply_entry.mac_address[2], beacon->datareply_entry.mac_address[3], beacon->datareply_entry.mac_address[4], beacon->datareply_entry.mac_address[5]); + } + lua_pushstring(L, macString); + free(macString); + return 1; + /*** + @tfield table nodes a table containing nodes informations, as tables (not userdatas). + A node table is like: `{ username = "azerty", nodeID = 12 }` + */ + } else if (!strcmp(index, "nodes")) { + lua_newtable(L); + for (int i=0;inodes[i])) continue; + lua_createtable(L, 0, 2); + char tmpstr[256]; // 256 is maybe too much ... But we have a lot of RAM. + memset(tmpstr, 0, sizeof(tmpstr)); + udsGetNodeInfoUsername(&beacon->nodes[i], tmpstr); + lua_pushstring(L, tmpstr); + lua_setfield(L, -2, "username"); + lua_pushinteger(L, (&beacon->nodes[i])->NetworkNodeID); + lua_setfield(L, -2, "nodeID"); + + lua_seti(L, -3, i+1); + } + return 1; + /*** + @tfield number id8 id8 of the beacon's network + */ + } else if (!strcmp(index, "id8")) { + lua_pushinteger(L, beacon->network.id8); + return 1; + /*** + @tfield number networkID random ID of the network + */ + } else if (!strcmp(index, "networkID")) { + lua_pushinteger(L, beacon->network.networkID); + return 1; + /*** + @tfield boolean allowSpectators `true` if new spectators are allowed on the network + */ + } else if (!strcmp(index, "allowSpectators")) { + lua_pushboolean(L, !(beacon->network.attributes&UDSNETATTR_DisableConnectSpectators)); + return 1; + /*** + @tfield boolean allowClients `true` if new clients are allowed on the network + */ + } else if (!strcmp(index, "allowClients")) { + lua_pushboolean(L, !(beacon->network.attributes&UDSNETATTR_DisableConnectClients)); + return 1; + // methods + } else { + for (int i=0;beaconScan_methods[i].name;i++) { + if (!strcmp(beaconScan_methods[i].name, index)) { + lua_pushcfunction(L, beaconScan_methods[i].func); + return 1; + } + } + } + } + + lua_pushnil(L); + return 1; +} + +/*** +Return the application data of the beacon +@function :getAppData +@tparam[opt=0x4000] number maxSize maximum application data size to return +@treturn[1] string application data +@treturn[2] nil +@treturn[2] error code +*/ +static int beaconScan_getAppData(lua_State *L) { + udsNetworkScanInfo* beacon = luaL_checkudata(L, 1, "LUDSBeaconScan"); + size_t maxSize = luaL_optinteger(L, 2, 0x4000); + + u8* data = malloc(maxSize); + if (data == NULL) luaL_error(L, "Memory allocation error"); + + size_t size = 0; + udsGetNetworkStructApplicationData(&beacon->network, data, maxSize, &size); + + lua_pushlstring(L, (const char*)data, size); + free(data); + + return 1; +} + +// beaconScan object +static const struct luaL_Reg beaconScan_methods[] = { + {"__index", beaconScan___index }, + {"getAppData", beaconScan_getAppData}, + {NULL, NULL} +}; + +// module functions +static const struct luaL_Reg uds_lib[] = { + {"init", uds_init }, + {"shutdown", uds_shutdown }, + {"scan", uds_scan }, + {"getNodesInfo ", uds_getNodesInfo }, + {"getAppData", uds_getAppData }, + {"connect", uds_connect }, + {"available", uds_available }, + {"send", uds_send }, + {"receive", uds_receive }, + {"disconnect", uds_disconnect }, + {"createNetwork", uds_createNetwork }, + {"setAppData", uds_setAppData }, + {"destroyNetwork", uds_destroyNetwork }, + {"ejectSpectators", uds_ejectSpectators}, + {"ejectClient", uds_ejectClient }, + {NULL, NULL} +}; + +/*** +Constants +@section constants +*/ + +struct { char *name; int value; } uds_constants[] = { + /*** + @field BROADCAST broadcast node ID + */ + {"BROADCAST", UDS_BROADCAST_NETWORKNODEID}, + /*** + @field HOST host node ID + */ + {"HOST", UDS_HOST_NETWORKNODEID }, + /*** + @field CLIENT used to specify a connection as a client + */ + {"CLIENT", UDSCONTYPE_Client }, + /*** + @field SPECTATOR used to specify a connection as a spectator + */ + {"SPECTATOR", UDSCONTYPE_Spectator }, + {NULL, 0} +}; + +int luaopen_uds_lib(lua_State *L) { + luaL_newmetatable(L, "LUDSBeaconScan"); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + luaL_setfuncs(L, beaconScan_methods, 0); + + luaL_newlib(L, uds_lib); + + for (int i = 0; uds_constants[i].name; i++) { + lua_pushinteger(L, uds_constants[i].value); + lua_setfield(L, -2, uds_constants[i].name); + } + + return 1; +} + +void load_uds_lib(lua_State *L) { + luaL_requiref(L, "ctr.uds", luaopen_uds_lib, 0); +} + +void unload_uds_lib(lua_State *L) { + if (initStateUDS) { + udsConnectionStatus status; + udsGetConnectionStatus(&status); + switch (status.status) { + case 0x6: // connected as host + udsDestroyNetwork(); + udsUnbind(&bind); + break; + + case 0x9: // connected as client + case 0xA: // connected as spectator + udsDisconnectNetwork(); + udsUnbind(&bind); + break; + + default: + break; + } + udsExit(); + initStateUDS = false; + } +} From b4ceb200eafe16e1a1ec02564822ba136037432d Mon Sep 17 00:00:00 2001 From: Reuh Date: Fri, 29 Apr 2016 17:55:59 +0200 Subject: [PATCH 093/101] Added gfx.linedRectangle and gfx.linedCircle --- sdcard/3ds/ctruLua/main.lua | 1 + source/gfx.c | 108 +++++++++++++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 2 deletions(-) diff --git a/sdcard/3ds/ctruLua/main.lua b/sdcard/3ds/ctruLua/main.lua index 9d21667..a574487 100644 --- a/sdcard/3ds/ctruLua/main.lua +++ b/sdcard/3ds/ctruLua/main.lua @@ -14,6 +14,7 @@ local function displayError(err, trace) gfx.color.setDefault(0xFFFDFDFD) gfx.font.setSize(12) gfx.font.setDefault(gfx.font.load(ctr.root .. "resources/VeraMono.ttf")) + gfx.disableConsole() while ctr.run() do gfx.start(gfx.BOTTOM) diff --git a/source/gfx.c b/source/gfx.c index f464c7e..43a4fb5 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -35,6 +35,14 @@ scissor_state lua_scissor = { 0, 0 }; +// Rotate a point (x,y) around the center (cx,cy) by angle radians. +void rotatePoint(int x, int y, int cx, int cy, float angle, int* outx, int* outy) { + float s = sin(angle), c = cos(angle); + int tx = x - cx, ty = y - cy; + *outx = round(tx * c - ty * s) + cx; + *outy = round(tx * s + ty * c) + cy; +} + /*** The `ctr.gfx.color` module. @table color @@ -227,7 +235,7 @@ static int gfx_point(lua_State *L) { } /*** -Draw a rectangle on the current screen. +Draw a filled rectangle on the current screen. @function rectangle @tparam integer x rectangle origin horizontal coordinate, in pixels @tparam integer y rectangle origin vertical coordinate, in pixels @@ -254,7 +262,51 @@ static int gfx_rectangle(lua_State *L) { } /*** -Draw a circle on the current screen. +Draw a rectangle outline on the current screen. +@function linedRectangle +@tparam integer x rectangle origin horizontal coordinate, in pixels +@tparam integer y rectangle origin vertical coordinate, in pixels +@tparam integer width rectangle width, in pixels +@tparam integer height rectangle height, in pixels +@tparam[opt=1] integer lineWidth line's thickness, in pixels +@tparam[opt=0] number angle rectangle rotation, in radians +@tparam[opt=default color] integer color drawing color +*/ +static int gfx_linedRectangle(lua_State *L) { + int x = luaL_checkinteger(L, 1); + int y = luaL_checkinteger(L, 2); + int width = luaL_checkinteger(L, 3); + int height = luaL_checkinteger(L, 4); + float lineWidth = luaL_optnumber(L, 5, 1.0f); + + float angle = luaL_optnumber(L, 6, 0); + u32 color = luaL_optinteger(L, 7, color_default); + + // Corner coordinates + int x2 = x + width, y2 = y; + int x3 = x2, y3 = y + height; + int x4 = x, y4 = y3; + + // Rotate corners + if (angle != 0) { + int cx = x + width/2, cy = y + height/2; + rotatePoint(x, y, cx, cy, angle, &x, &y ); + rotatePoint(x2, y2, cx, cy, angle, &x2, &y2); + rotatePoint(x3, y3, cx, cy, angle, &x3, &y3); + rotatePoint(x4, y4, cx, cy, angle, &x4, &y4); + } + + // Draw lines + sf2d_draw_line(x, y, x2, y2, lineWidth, color); + sf2d_draw_line(x2, y2, x3, y3, lineWidth, color); + sf2d_draw_line(x3, y3, x4, y4, lineWidth, color); + sf2d_draw_line(x4, y4, x, y, lineWidth, color); + + return 0; +} + +/*** +Draw a filled circle on the current screen. @function circle @tparam integer x circle center horizontal coordinate, in pixels @tparam integer y circle center vertical coordinate, in pixels @@ -273,6 +325,56 @@ static int gfx_circle(lua_State *L) { return 0; } +/*** +Draw a circle outline on the current screen. +@function linedCircle +@tparam integer x circle center horizontal coordinate, in pixels +@tparam integer y circle center vertical coordinate, in pixels +@tparam integer radius circle radius, in pixels +@tparam[opt=1] integer width line's thickness, in pixels +@tparam[opt=default color] integer color drawing color +*/ +static int gfx_linedCircle(lua_State *L) { + int x0 = luaL_checkinteger(L, 1); + int y0 = luaL_checkinteger(L, 2); + int radius = luaL_checkinteger(L, 3); + float width = luaL_optnumber(L, 4, 1.0f); + + u32 color = luaL_optinteger(L, 5, color_default); + + for (int r = ceil(radius - width/2), maxr = ceil(radius + width/2)-1; r <= maxr; r++) { + // Implementatin of the Andres circle algorithm. + int x = 0; + int y = r; + int d = r - 1; + while (y >= x) { + // Best way to draw a lot of points, 10/10 + sf2d_draw_rectangle(x0 + x , y0 + y, 1, 1, color); + sf2d_draw_rectangle(x0 + y , y0 + x, 1, 1, color); + sf2d_draw_rectangle(x0 - x , y0 + y, 1, 1, color); + sf2d_draw_rectangle(x0 - y , y0 + x, 1, 1, color); + sf2d_draw_rectangle(x0 + x , y0 - y, 1, 1, color); + sf2d_draw_rectangle(x0 + y , y0 - x, 1, 1, color); + sf2d_draw_rectangle(x0 - x , y0 - y, 1, 1, color); + sf2d_draw_rectangle(x0 - y , y0 - x, 1, 1, color); + + if (d >= 2*x) { + d -= 2*x + 1; + x++; + } else if (d < 2*(r-y)) { + d += 2*y - 1; + y--; + } else { + d += 2*(y - x - 1); + y--; + x++; + } + } + } + + return 0; +} + /*** Draw a text on the current screen. @function text @@ -580,7 +682,9 @@ static const struct luaL_Reg gfx_lib[] = { { "line", gfx_line }, { "point", gfx_point }, { "rectangle", gfx_rectangle }, + { "linedRectangle", gfx_linedRectangle }, { "circle", gfx_circle }, + { "linedCircle", gfx_linedCircle }, { "text", gfx_text }, { "wrappedText", gfx_wrappedText }, { "calcBoundingBox", gfx_calcBoundingBox }, From 5494f3d2e5e9c56e592fc994e079eb7128638c22 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Mon, 9 May 2016 23:28:41 +0200 Subject: [PATCH 094/101] Added some sleep mode related functions, fixed the example, fixed some things --- sdcard/3ds/ctruLua/examples/example.lua | 3 ++- source/apt.c | 26 +++++++++++++++++++++++++ source/font.c | 2 ++ source/httpc.c | 3 ++- source/ptm.c | 4 ++-- 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/sdcard/3ds/ctruLua/examples/example.lua b/sdcard/3ds/ctruLua/examples/example.lua index 1c2a307..67ed406 100644 --- a/sdcard/3ds/ctruLua/examples/example.lua +++ b/sdcard/3ds/ctruLua/examples/example.lua @@ -10,6 +10,7 @@ local angle = 0 local texture1 = gfx.texture.load(ctr.root.."icon.png"); if not texture1 then error("Giants ducks came from another planet") end +local tWidth, tHeight = texture1:getSize() gfx.color.setBackground(gfx.color.RGBA8(200, 200, 200)) gfx.set3D(true) @@ -66,7 +67,7 @@ while ctr.run() do gfx.text(5, 17, "Hello world, from Lua ! éàçù", 20, gfx.color.RGBA8(0, 0, 0)) gfx.text(5, 50, "Time: "..os.date()) - texture1:draw(280, 80, angle); + texture1:draw(280, 80, angle, tWidth/2, tHeight/2); local cx, cy = hid.circle() gfx.rectangle(40, 90, 60, 60, 0, 0xDDDDDDFF) diff --git a/source/apt.c b/source/apt.c index e860736..bcbc26b 100644 --- a/source/apt.c +++ b/source/apt.c @@ -107,6 +107,30 @@ static int apt_getMenuAppID(lua_State *L) { return 1; } +/*** +Allow or not the system to enter sleep mode. +@function setSleepAllowed +@tparam boolean allowed `true` to allow, `false` to disallow +*/ +static int apt_setSleepAllowed(lua_State *L) { + bool allowed = lua_toboolean(L, 1); + + aptSetSleepAllowed(allowed); + + return 0; +} + +/*** +Check if sleep mode is allowed. +@function isSleepAllowed +@treturn boolean `true` is allowed, false if not. +*/ +static int apt_isSleepAllowed(lua_State *L) { + lua_pushboolean(L, aptIsSleepAllowed()); + + return 1; +} + static const struct luaL_Reg apt_lib[] = { {"openSession", apt_openSession }, {"closeSession", apt_closeSession }, @@ -117,6 +141,8 @@ static const struct luaL_Reg apt_lib[] = { {"setStatusPower", apt_setStatusPower }, {"signalReadyForSleep", apt_signalReadyForSleep}, {"getMenuAppID", apt_getMenuAppID }, + {"setSleepAllowed", apt_setSleepAllowed }, + {"isSleepAllowed", apt_isSleepAllowed }, {NULL, NULL} }; diff --git a/source/font.c b/source/font.c index bac5c44..1110432 100644 --- a/source/font.c +++ b/source/font.c @@ -189,4 +189,6 @@ void unload_font_lib(lua_State *L) { if (luaL_testudata(L, -1, "LFont") != NULL) sftd_free_font(((font_userdata *)lua_touserdata(L, -1))->font); // Unload current font + + lua_pop(L, 1); } diff --git a/source/httpc.c b/source/httpc.c index d114445..87c826d 100644 --- a/source/httpc.c +++ b/source/httpc.c @@ -174,13 +174,14 @@ static int httpc_downloadData(lua_State *L) { ret = httpcDownloadData(context, buff, size, NULL); if (ret != 0) { + free(buff); lua_pushnil(L); lua_pushinteger(L, ret); return 2; } lua_pushstring(L, (char*)buff); - //free(buff); FIXME we need to free this buffer at some point ? + free(buff); //lua_pushinteger(L, size); // only for test purposes. return 1; } diff --git a/source/ptm.c b/source/ptm.c index 5401fe3..125e0ea 100644 --- a/source/ptm.c +++ b/source/ptm.c @@ -45,13 +45,13 @@ static int ptm_shutdown(lua_State *L) { /*** Return the shell state. @function getShellState -@treturn number shell state +@treturn boolean shell state, `true` if open, `false` if closed. */ static int ptm_getShellState(lua_State *L) { u8 out = 0; PTMU_GetShellState(&out); - lua_pushinteger(L, out); + lua_pushboolean(L, out); return 1; } From 20f8cd3cb9fab9f15fc5931baf3e4f4047e81c41 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Mon, 6 Jun 2016 19:17:47 +0200 Subject: [PATCH 095/101] Added "ctr.hid.cstick()", untested with the circle pad pro, works on New 3DS. I'm working on the issue with :bind(), looks like it's not just my code ... --- source/hid.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/source/hid.c b/source/hid.c index 141f49d..ed76ee7 100644 --- a/source/hid.c +++ b/source/hid.c @@ -1,11 +1,12 @@ /*** The `hid` module. -The circle pad pro is supported, it's keys replace de "3ds only" keys +The circle pad pro is supported, it's keys replace the "3ds only" keys @module ctr.hid @usage local hid = require("ctr.hid") */ #include <3ds/types.h> #include <3ds/services/hid.h> +#include <3ds/services/irrst.h> #include #include @@ -175,6 +176,25 @@ static int hid_circle(lua_State *L) { return 2; } +/*** +Return the C-stick position. +`0,0` is the center position, and the stick should return to it if not touched (95% of the time). +Range is from `-146` to `146` on both X and Y, but these are hard to reach. +@newonly +@function cstick +@treturn number X position +@treturn number Y position +*/ +static int hid_cstick(lua_State *L) { + circlePosition pos; + irrstCstickRead(&pos); + + lua_pushinteger(L, pos.dx); + lua_pushinteger(L, pos.dy); + + return 2; +} + /*** Return the accelerometer vector @function accel @@ -243,6 +263,7 @@ static const struct luaL_Reg hid_lib[] = { { "keys", hid_keys }, { "touch", hid_touch }, { "circle", hid_circle }, + { "cstick", hid_cstick }, { "accel", hid_accel }, { "gyro", hid_gyro }, { "volume", hid_volume }, From 4a2c1a7c68d99e1d4c16d486b21208b6922f47e9 Mon Sep 17 00:00:00 2001 From: Reuh Date: Mon, 27 Jun 2016 13:42:38 +0200 Subject: [PATCH 096/101] Updated ctr.apt to the latest ctrulib, added ctr.apt.isNew3DS() --- source/apt.c | 111 +++++++++++++++++++++++++++----------------------- source/font.c | 3 +- 2 files changed, 63 insertions(+), 51 deletions(-) diff --git a/source/apt.c b/source/apt.c index bcbc26b..6f2f28c 100644 --- a/source/apt.c +++ b/source/apt.c @@ -13,44 +13,29 @@ Used to manage the applets and application status. #include #include -/*** -Open an APT session. Should only work if you don't use the homebrew menu. -@function openSession -*/ -static int apt_openSession(lua_State *L) { - aptOpenSession(); - return 0; -} - -/*** -Close the current APT session. -@function closeSession -*/ -static int apt_closeSession(lua_State *L) { - aptCloseSession(); - return 0; -} - /*** Set the app status. @function setStatus +@tparam integer status the new app status */ static int apt_setStatus(lua_State *L) { APT_AppStatus status = luaL_checkinteger(L, 1); - + aptSetStatus(status); - + return 0; } /*** Get the app status. @function getStatus +@treturn integer the app status */ static int apt_getStatus(lua_State *L) { APT_AppStatus status = aptGetStatus(); lua_pushinteger(L, status); + return 1; } @@ -60,6 +45,7 @@ Return to the Home menu. */ static int apt_returnToMenu(lua_State *L) { aptReturnToMenu(); + return 0; } @@ -70,8 +56,9 @@ Get the power status. */ static int apt_getStatusPower(lua_State *L) { u32 status = aptGetStatusPower(); - + lua_pushboolean(L, status); + return 1; } @@ -82,9 +69,9 @@ Set the power status. */ static int apt_setStatusPower(lua_State *L) { u32 status = lua_toboolean(L, 1); - + aptSetStatusPower(status); - + return 0; } @@ -94,6 +81,7 @@ Signal that the application is ready for sleeping. */ static int apt_signalReadyForSleep(lua_State *L) { aptSignalReadyForSleep(); + return 0; } @@ -104,6 +92,7 @@ Return the Home menu AppID. */ static int apt_getMenuAppID(lua_State *L) { lua_pushinteger(L, aptGetMenuAppID()); + return 1; } @@ -114,9 +103,9 @@ Allow or not the system to enter sleep mode. */ static int apt_setSleepAllowed(lua_State *L) { bool allowed = lua_toboolean(L, 1); - + aptSetSleepAllowed(allowed); - + return 0; } @@ -127,22 +116,36 @@ Check if sleep mode is allowed. */ static int apt_isSleepAllowed(lua_State *L) { lua_pushboolean(L, aptIsSleepAllowed()); - + + return 1; +} + +/*** +Checks whether the system is a New 3DS. +@function isNew3DS +@treturn boolean `true` if it's a New3DS, false otherwise +*/ +static int apt_isNew3DS(lua_State *L) { + bool isNew3ds; + + APT_CheckNew3DS(&isNew3ds); + + lua_pushboolean(L, isNew3ds); + return 1; } static const struct luaL_Reg apt_lib[] = { - {"openSession", apt_openSession }, - {"closeSession", apt_closeSession }, - {"setStatus", apt_setStatus }, - {"getStatus", apt_getStatus }, - {"returnToMenu", apt_returnToMenu }, - {"getStatusPower", apt_getStatusPower }, - {"setStatusPower", apt_setStatusPower }, - {"signalReadyForSleep", apt_signalReadyForSleep}, - {"getMenuAppID", apt_getMenuAppID }, - {"setSleepAllowed", apt_setSleepAllowed }, - {"isSleepAllowed", apt_isSleepAllowed }, + { "setStatus", apt_setStatus }, + { "getStatus", apt_getStatus }, + { "returnToMenu", apt_returnToMenu }, + { "getStatusPower", apt_getStatusPower }, + { "setStatusPower", apt_setStatusPower }, + { "signalReadyForSleep", apt_signalReadyForSleep }, + { "getMenuAppID", apt_getMenuAppID }, + { "setSleepAllowed", apt_setSleepAllowed }, + { "isSleepAllowed", apt_isSleepAllowed }, + { "isNew3DS", apt_isNew3DS }, {NULL, NULL} }; @@ -268,37 +271,45 @@ struct { char *name; int value; } apt_constants[] = { */ {"APTSIGNAL_HOMEBUTTON", APTSIGNAL_HOMEBUTTON }, /*** - @field APTSIGNAL_PREPARESLEEP + @field APTSIGNAL_HOMEBUTTON2 */ - {"APTSIGNAL_PREPARESLEEP", APTSIGNAL_PREPARESLEEP}, + {"APTSIGNAL_HOMEBUTTON2", APTSIGNAL_HOMEBUTTON2 }, /*** - @field APTSIGNAL_ENTERSLEEP + @field APTSIGNAL_SLEEP_QUERY */ - {"APTSIGNAL_ENTERSLEEP", APTSIGNAL_ENTERSLEEP }, + {"APTSIGNAL_SLEEP_QUERY", APTSIGNAL_SLEEP_QUERY }, + /*** + @field APTSIGNAL_SLEEP_CANCEL + */ + {"APTSIGNAL_SLEEP_CANCEL", APTSIGNAL_SLEEP_CANCEL}, + /*** + @field APTSIGNAL_SLEEP_ENTER + */ + {"APTSIGNAL_SLEEP_ENTER", APTSIGNAL_SLEEP_ENTER }, /*** @field APTSIGNAL_WAKEUP */ - {"APTSIGNAL_WAKEUP", APTSIGNAL_WAKEUP }, + {"APTSIGNAL_SLEEP_WAKEUP", APTSIGNAL_SLEEP_WAKEUP}, /*** - @field APTSIGNAL_ENABLE + @field APTSIGNAL_SHUTDOWN */ - {"APTSIGNAL_ENABLE", APTSIGNAL_ENABLE }, + {"APTSIGNAL_SHUTDOWN", APTSIGNAL_SHUTDOWN }, /*** @field APTSIGNAL_POWERBUTTON */ {"APTSIGNAL_POWERBUTTON", APTSIGNAL_POWERBUTTON }, /*** - @field APTSIGNAL_UTILITY + @field APTSIGNAL_POWERBUTTON2 */ - {"APTSIGNAL_UTILITY", APTSIGNAL_UTILITY }, + {"APTSIGNAL_POWERBUTTON2", APTSIGNAL_POWERBUTTON2}, /*** - @field APTSIGNAL_SLEEPSYSTEM + @field APTSIGNAL_TRY_SLEEP */ - {"APTSIGNAL_SLEEPSYSTEM", APTSIGNAL_SLEEPSYSTEM }, + {"APTSIGNAL_TRY_SLEEP", APTSIGNAL_TRY_SLEEP }, /*** - @field APTSIGNAL_ERROR + @field APTSIGNAL_ORDERTOCLOSE */ - {"APTSIGNAL_ERROR", APTSIGNAL_ERROR }, + {"APTSIGNAL_ORDERTOCLOSE", APTSIGNAL_ORDERTOCLOSE}, /*** @field APTHOOK_ONSUSPEND */ diff --git a/source/font.c b/source/font.c index 1110432..615b299 100644 --- a/source/font.c +++ b/source/font.c @@ -18,7 +18,8 @@ The `gfx.font` module u32 textSize = 9; /*** -Load a TTF font. +Load a font. Supported formats: TTF, OTF, TTC, OTC, WOFF, PFA, PFB, PCF, FNT, BDF, PFR, and others. +ctrµLua support all formats supported by FreeType. See here for a more complete list: http://freetype.org/freetype2/docs/index.html @function load @tparam string path path to the file @treturn[1] font the loaded font. From b47971bfcac5d83d052851170ec20a03abd95c0a Mon Sep 17 00:00:00 2001 From: Reuh Date: Mon, 27 Jun 2016 15:29:55 +0200 Subject: [PATCH 097/101] Added rectangle gradients, triangles, updated sf2dlib Gradients are done with additional parameters to gfx.rectangle, so old code that gave the function too many parameters and expected them to be ignored may break. --- libs/sf2dlib/libsf2d/.gitignore | 1 + libs/sf2dlib/libsf2d/include/sf2d.h | 45 +++++ libs/sf2dlib/libsf2d/include/sf2d_private.h | 2 + libs/sf2dlib/libsf2d/source/sf2d_draw.c | 187 +++++++++++--------- libs/sf2dlib/libsf2d/source/sf2d_texture.c | 29 ++- source/gfx.c | 129 +++++++++++--- 6 files changed, 280 insertions(+), 113 deletions(-) create mode 100644 libs/sf2dlib/libsf2d/.gitignore diff --git a/libs/sf2dlib/libsf2d/.gitignore b/libs/sf2dlib/libsf2d/.gitignore new file mode 100644 index 0000000..e63ddd8 --- /dev/null +++ b/libs/sf2dlib/libsf2d/.gitignore @@ -0,0 +1 @@ +/Default/ diff --git a/libs/sf2dlib/libsf2d/include/sf2d.h b/libs/sf2dlib/libsf2d/include/sf2d.h index 9322ebb..b6f3277 100644 --- a/libs/sf2dlib/libsf2d/include/sf2d.h +++ b/libs/sf2dlib/libsf2d/include/sf2d.h @@ -67,6 +67,14 @@ typedef enum { TEXFMT_ETC1A4 = 13 } sf2d_texfmt; +/** + * @brief Represents a direction for drawing a gradient + */ + +typedef enum { + SF2D_TOP_TO_BOTTOM, + SF2D_LEFT_TO_RIGHT +} sf2d_gradient_dir; /** * @brief Data allocated on the RAM or VRAM @@ -264,6 +272,18 @@ void sf2d_set_clear_color(u32 color); */ void sf2d_draw_rectangle(int x, int y, int w, int h, u32 color); +/** + * @brief Draws a triangle + * @param x1 x coordinate of a vertex of the triangle + * @param y1 y coordinate of a vertex of the triangle + * @param x2 x coordinate of a vertex of the triangle + * @param y2 y coordinate of a vertex of the triangle + * @param x3 x coordinate of a vertex of the triangle + * @param y3 y coordinate of a vertex of the triangle + * @param color the color to draw the triangle + */ +void sf2d_draw_triangle(float x1, float y1, float x2, float y2, float x3, float y3, u32 color); + /** * @brief Draws a rotated rectangle * @param x x coordinate of the top left corner of the rectangle @@ -275,6 +295,31 @@ void sf2d_draw_rectangle(int x, int y, int w, int h, u32 color); */ void sf2d_draw_rectangle_rotate(int x, int y, int w, int h, u32 color, float rad); +/** + * @brief Draws a rectangle + * @param x x coordinate of the top left corner of the rectangle + * @param y y coordinate of the top left corner of the rectangle + * @param w rectangle width + * @param h rectangle height + * @param color1 the color at the start of the gradient + * @param color2 the color at the end of the gradient + * @param left_to_right determines which direction the gradient is in + */ +void sf2d_draw_rectangle_gradient(int x, int y, int w, int h, u32 color1, u32 color2, sf2d_gradient_dir direction); + +/** + * @brief Draws a rotated rectangle + * @param x x coordinate of the top left corner of the rectangle + * @param y y coordinate of the top left corner of the rectangle + * @param w rectangle width + * @param h rectangle height + * @param color1 the color at the start of the gradient + * @param color2 the color at the end of the gradient + * @param left_to_right determines which direction the gradient is in + * @param rad rotation (in radians) to draw the rectangle + */ +void sf2d_draw_rectangle_gradient_rotate(int x, int y, int w, int h, u32 color1, u32 color2, sf2d_gradient_dir direction, float rad); + /** * @brief Draws a filled circle * @param x x coordinate of the center of the circle diff --git a/libs/sf2dlib/libsf2d/include/sf2d_private.h b/libs/sf2dlib/libsf2d/include/sf2d_private.h index 76cf70d..07579ca 100644 --- a/libs/sf2dlib/libsf2d/include/sf2d_private.h +++ b/libs/sf2dlib/libsf2d/include/sf2d_private.h @@ -7,6 +7,8 @@ void GPU_SetDummyTexEnv(u8 num); +void sf2d_draw_rectangle_internal(const sf2d_vertex_pos_col *vertices); + // Vector operations void vector_mult_matrix4x4(const float *msrc, const sf2d_vector_3f *vsrc, sf2d_vector_3f *vdst); diff --git a/libs/sf2dlib/libsf2d/source/sf2d_draw.c b/libs/sf2dlib/libsf2d/source/sf2d_draw.c index 59a792e..a28e818 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d_draw.c +++ b/libs/sf2dlib/libsf2d/source/sf2d_draw.c @@ -6,6 +6,30 @@ #define M_PI (3.14159265358979323846) #endif +void sf2d_setup_env_internal(const sf2d_vertex_pos_col* vertices) { + GPU_SetTexEnv( + 0, + GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR), + GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR), + GPU_TEVOPERANDS(0, 0, 0), + GPU_TEVOPERANDS(0, 0, 0), + GPU_REPLACE, GPU_REPLACE, + 0xFFFFFFFF + ); + + GPU_SetAttributeBuffers( + 2, // number of attributes + (u32*)osConvertVirtToPhys(vertices), + GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE), + 0xFFFC, //0b1100 + 0x10, + 1, //number of buffers + (u32[]){0x0}, // buffer offsets (placeholders) + (u64[]){0x10}, // attribute permutations for each buffer + (u8[]){2} // number of attributes for each buffer + ); +} + void sf2d_draw_line(float x0, float y0, float x1, float y1, float width, u32 color) { sf2d_vertex_pos_col *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_col), 8); @@ -38,31 +62,25 @@ void sf2d_draw_line(float x0, float y0, float x1, float y1, float width, u32 col vertices[2].color = vertices[0].color; vertices[3].color = vertices[0].color; - GPU_SetTexEnv( - 0, - GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR), - GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR), - GPU_TEVOPERANDS(0, 0, 0), - GPU_TEVOPERANDS(0, 0, 0), - GPU_REPLACE, GPU_REPLACE, - 0xFFFFFFFF - ); - - GPU_SetAttributeBuffers( - 2, // number of attributes - (u32*)osConvertVirtToPhys(vertices), - GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE), - 0xFFFC, //0b1100 - 0x10, - 1, //number of buffers - (u32[]){0x0}, // buffer offsets (placeholders) - (u64[]){0x10}, // attribute permutations for each buffer - (u8[]){2} // number of attributes for each buffer - ); + sf2d_setup_env_internal(vertices); GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); } +void sf2d_draw_rectangle_internal(const sf2d_vertex_pos_col *vertices) +{ + sf2d_setup_env_internal(vertices); + + GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); +} + +void sf2d_draw_triangle_internal(const sf2d_vertex_pos_col *vertices) +{ + sf2d_setup_env_internal(vertices); + + GPU_DrawArray(GPU_TRIANGLES, 0, 3); +} + void sf2d_draw_rectangle(int x, int y, int w, int h, u32 color) { sf2d_vertex_pos_col *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_col), 8); @@ -78,29 +96,23 @@ void sf2d_draw_rectangle(int x, int y, int w, int h, u32 color) vertices[2].color = vertices[0].color; vertices[3].color = vertices[0].color; - GPU_SetTexEnv( - 0, - GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR), - GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR), - GPU_TEVOPERANDS(0, 0, 0), - GPU_TEVOPERANDS(0, 0, 0), - GPU_REPLACE, GPU_REPLACE, - 0xFFFFFFFF - ); + sf2d_draw_rectangle_internal(vertices); +} - GPU_SetAttributeBuffers( - 2, // number of attributes - (u32*)osConvertVirtToPhys(vertices), - GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE), - 0xFFFC, //0b1100 - 0x10, - 1, //number of buffers - (u32[]){0x0}, // buffer offsets (placeholders) - (u64[]){0x10}, // attribute permutations for each buffer - (u8[]){2} // number of attributes for each buffer - ); +void sf2d_draw_triangle(float x1, float y1, float x2, float y2, float x3, float y3, u32 color) +{ + sf2d_vertex_pos_col *vertices = sf2d_pool_memalign(3 * sizeof(sf2d_vertex_pos_col), 8); + if (!vertices) return; - GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); + vertices[0].position = (sf2d_vector_3f){(float)x1, (float)y1, SF2D_DEFAULT_DEPTH}; + vertices[1].position = (sf2d_vector_3f){(float)x2, (float)y2, SF2D_DEFAULT_DEPTH}; + vertices[2].position = (sf2d_vector_3f){(float)x3, (float)y3, SF2D_DEFAULT_DEPTH}; + + vertices[0].color = color; + vertices[1].color = vertices[0].color; + vertices[2].color = vertices[0].color; + + sf2d_draw_triangle_internal(vertices); } void sf2d_draw_rectangle_rotate(int x, int y, int w, int h, u32 color, float rad) @@ -131,29 +143,56 @@ void sf2d_draw_rectangle_rotate(int x, int y, int w, int h, u32 color, float rad vertices[i].position = (sf2d_vector_3f){rot[i].x + x + w2, rot[i].y + y + h2, rot[i].z}; } - GPU_SetTexEnv( - 0, - GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR), - GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR), - GPU_TEVOPERANDS(0, 0, 0), - GPU_TEVOPERANDS(0, 0, 0), - GPU_REPLACE, GPU_REPLACE, - 0xFFFFFFFF - ); + sf2d_draw_rectangle_internal(vertices); +} - GPU_SetAttributeBuffers( - 2, // number of attributes - (u32*)osConvertVirtToPhys(vertices), - GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE), - 0xFFFC, //0b1100 - 0x10, - 1, //number of buffers - (u32[]){0x0}, // buffer offsets (placeholders) - (u64[]){0x10}, // attribute permutations for each buffer - (u8[]){2} // number of attributes for each buffer - ); +void sf2d_draw_rectangle_gradient(int x, int y, int w, int h, u32 color1, u32 color2, sf2d_gradient_dir direction) +{ + sf2d_vertex_pos_col *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_col), 8); + if (!vertices) return; - GPU_DrawArray(GPU_TRIANGLE_STRIP, 0, 4); + vertices[0].position = (sf2d_vector_3f){(float)x, (float)y, SF2D_DEFAULT_DEPTH}; + vertices[1].position = (sf2d_vector_3f){(float)x+w, (float)y, SF2D_DEFAULT_DEPTH}; + vertices[2].position = (sf2d_vector_3f){(float)x, (float)y+h, SF2D_DEFAULT_DEPTH}; + vertices[3].position = (sf2d_vector_3f){(float)x+w, (float)y+h, SF2D_DEFAULT_DEPTH}; + + vertices[0].color = color1; + vertices[1].color = (direction == SF2D_LEFT_TO_RIGHT) ? color2 : color1; + vertices[2].color = (direction == SF2D_LEFT_TO_RIGHT) ? color1 : color2; + vertices[3].color = color2; + + sf2d_draw_rectangle_internal(vertices); +} + +void sf2d_draw_rectangle_gradient_rotate(int x, int y, int w, int h, u32 color1, u32 color2, sf2d_gradient_dir direction, float rad) +{ + sf2d_vertex_pos_col *vertices = sf2d_pool_memalign(4 * sizeof(sf2d_vertex_pos_col), 8); + if (!vertices) return; + + int w2 = w/2.0f; + int h2 = h/2.0f; + + vertices[0].position = (sf2d_vector_3f){(float)-w2, (float)-h2, SF2D_DEFAULT_DEPTH}; + vertices[1].position = (sf2d_vector_3f){(float) w2, (float)-h2, SF2D_DEFAULT_DEPTH}; + vertices[2].position = (sf2d_vector_3f){(float)-w2, (float) h2, SF2D_DEFAULT_DEPTH}; + vertices[3].position = (sf2d_vector_3f){(float) w2, (float) h2, SF2D_DEFAULT_DEPTH}; + + vertices[0].color = color1; + vertices[1].color = (direction == SF2D_LEFT_TO_RIGHT) ? color2 : color1; + vertices[2].color = (direction == SF2D_LEFT_TO_RIGHT) ? color1 : color2; + vertices[3].color = color2; + + float m[4*4]; + matrix_set_z_rotation(m, rad); + sf2d_vector_3f rot[4]; + + int i; + for (i = 0; i < 4; i++) { + vector_mult_matrix4x4(m, &vertices[i].position, &rot[i]); + vertices[i].position = (sf2d_vector_3f){rot[i].x + x + w2, rot[i].y + y + h2, rot[i].z}; + } + + sf2d_draw_rectangle_internal(vertices); } void sf2d_draw_fill_circle(int x, int y, int radius, u32 color) @@ -186,27 +225,7 @@ void sf2d_draw_fill_circle(int x, int y, int radius, u32 color) vertices[num_segments + 1].position = vertices[1].position; vertices[num_segments + 1].color = vertices[1].color; - GPU_SetTexEnv( - 0, - GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR), - GPU_TEVSOURCES(GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR), - GPU_TEVOPERANDS(0, 0, 0), - GPU_TEVOPERANDS(0, 0, 0), - GPU_REPLACE, GPU_REPLACE, - 0xFFFFFFFF - ); - - GPU_SetAttributeBuffers( - 2, // number of attributes - (u32*)osConvertVirtToPhys(vertices), - GPU_ATTRIBFMT(0, 3, GPU_FLOAT) | GPU_ATTRIBFMT(1, 4, GPU_UNSIGNED_BYTE), - 0xFFFC, //0b1100 - 0x10, - 1, //number of buffers - (u32[]){0x0}, // buffer offsets (placeholders) - (u64[]){0x10}, // attribute permutations for each buffer - (u8[]){2} // number of attributes for each buffer - ); + sf2d_setup_env_internal(vertices); GPU_DrawArray(GPU_TRIANGLE_FAN, 0, num_segments + 2); } diff --git a/libs/sf2dlib/libsf2d/source/sf2d_texture.c b/libs/sf2dlib/libsf2d/source/sf2d_texture.c index 12469ed..d0b7f09 100644 --- a/libs/sf2dlib/libsf2d/source/sf2d_texture.c +++ b/libs/sf2dlib/libsf2d/source/sf2d_texture.c @@ -8,7 +8,7 @@ #define M_PI (3.14159265358979323846) #endif -#define TEX_MIN_SIZE 8 +#define TEX_MIN_SIZE 32 static unsigned int nibbles_per_pixel(sf2d_texfmt format) { @@ -146,21 +146,40 @@ void sf2d_clear_target(sf2d_rendertarget *target, u32 color) { sf2d_texture_tile32(&(target->texture)); } +void sf2d_texture_tile32_hardware(sf2d_texture *texture, const void *data, int w, int h) +{ + if (texture->tiled) return; + const u32 flags = (GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) | + GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGBA8) | + GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)); + + GSPGPU_FlushDataCache(data, (w*h)<<2); + GX_DisplayTransfer( + (u32*)data, + GX_BUFFER_DIM(w, h), + (u32*)texture->data, + GX_BUFFER_DIM(texture->pow2_w, texture->pow2_h), + flags + ); + gspWaitForPPF(); + GSPGPU_InvalidateDataCache(texture->data, texture->data_size); + texture->tiled = 1; +} + void sf2d_fill_texture_from_RGBA8(sf2d_texture *dst, const void *rgba8, int source_w, int source_h) { // TODO: add support for non-RGBA8 textures - u8 *tmp = linearAlloc(dst->pow2_w * dst->pow2_h * 4); + u8 *tmp = linearAlloc((dst->pow2_w * dst->pow2_h)<<2); int i, j; for (i = 0; i < source_h; i++) { for (j = 0; j < source_w; j++) { - ((u32 *)tmp)[i*dst->pow2_w + j] = ((u32 *)rgba8)[i*source_w + j]; + ((u32 *)tmp)[i*dst->pow2_w + j] = __builtin_bswap32(((u32 *)rgba8)[i*source_w + j]); } } - memcpy(dst->data, tmp, dst->pow2_w*dst->pow2_h*4); + sf2d_texture_tile32_hardware(dst, tmp, dst->pow2_w, dst->pow2_h); linearFree(tmp); - sf2d_texture_tile32(dst); } sf2d_texture *sf2d_create_texture_mem_RGBA8(const void *src_buffer, int src_w, int src_h, sf2d_texfmt pixel_format, sf2d_place place) diff --git a/source/gfx.c b/source/gfx.c index 43a4fb5..3893380 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -192,6 +192,24 @@ static int gfx_vramSpaceFree(lua_State *L) { return 1; } +/*** +Draw a point, a single pixel, on the current screen. +@function point +@tparam integer x point horizontal coordinate, in pixels +@tparam integer y point vertical coordinate, in pixels +@tparam[opt=default color] integer color drawing color +*/ +static int gfx_point(lua_State *L) { + int x = luaL_checkinteger(L, 1); + int y = luaL_checkinteger(L, 2); + + u32 color = luaL_optinteger(L, 3, color_default); + + sf2d_draw_rectangle(x, y, 1, 1, color); // well, it looks like a point + + return 0; +} + /*** Draw a line on the current screen. @function line @@ -217,19 +235,57 @@ static int gfx_line(lua_State *L) { } /*** -Draw a point, a single pixel, on the current screen. -@function point -@tparam integer x point horizontal coordinate, in pixels -@tparam integer y point vertical coordinate, in pixels +Draw a triangle on the current screen. +@function triangle +@tparam integer x1 horizontal coordinate of a vertex of the triangle, in pixels +@tparam integer y1 vertical coordinate of a vertex of the triangle, in pixels +@tparam integer x2 horizontal coordinate of a vertex of the triangle, in pixels +@tparam integer y2 vertical coordinate of a vertex of the triangle, in pixels +@tparam integer x3 horizontal coordinate of a vertex of the triangle, in pixels +@tparam integer y3 vertical coordinate of a vertex of the triangle, in pixels @tparam[opt=default color] integer color drawing color */ -static int gfx_point(lua_State *L) { - int x = luaL_checkinteger(L, 1); - int y = luaL_checkinteger(L, 2); +static int gfx_triangle(lua_State *L) { + int x1 = luaL_checkinteger(L, 1); + int y1 = luaL_checkinteger(L, 2); + int x2 = luaL_checkinteger(L, 3); + int y2 = luaL_checkinteger(L, 4); + int x3 = luaL_checkinteger(L, 5); + int y3 = luaL_checkinteger(L, 6); - u32 color = luaL_optinteger(L, 3, color_default); + u32 color = luaL_optinteger(L, 7, color_default); - sf2d_draw_rectangle(x, y, 1, 1, color); // well, it looks like a point + sf2d_draw_triangle(x1, y1, x2, y2, x3, y3, color); + + return 0; +} + +/*** +Draw a triangle on the current screen. +@function triangle +@tparam integer x1 horizontal coordinate of a vertex of the triangle, in pixels +@tparam integer y1 vertical coordinate of a vertex of the triangle, in pixels +@tparam integer x2 horizontal coordinate of a vertex of the triangle, in pixels +@tparam integer y2 vertical coordinate of a vertex of the triangle, in pixels +@tparam integer x3 horizontal coordinate of a vertex of the triangle, in pixels +@tparam integer y3 vertical coordinate of a vertex of the triangle, in pixels +@tparam[opt=1] number lineWidth line's thickness, in pixels +@tparam[opt=default color] integer color drawing color +*/ +static int gfx_linedTriangle(lua_State *L) { + int x1 = luaL_checkinteger(L, 1); + int y1 = luaL_checkinteger(L, 2); + int x2 = luaL_checkinteger(L, 3); + int y2 = luaL_checkinteger(L, 4); + int x3 = luaL_checkinteger(L, 5); + int y3 = luaL_checkinteger(L, 6); + float lineWidth = luaL_optnumber(L, 7, 1.0f); + + u32 color = luaL_optinteger(L, 8, color_default); + + sf2d_draw_line(x1, y1, x2, y2, lineWidth, color); + sf2d_draw_line(x2, y2, x3, y3, lineWidth, color); + sf2d_draw_line(x3, y3, x1, y1, lineWidth, color); return 0; } @@ -243,6 +299,8 @@ Draw a filled rectangle on the current screen. @tparam integer height rectangle height, in pixels @tparam[opt=0] number angle rectangle rotation, in radians @tparam[opt=default color] integer color drawing color +@tparam[opt] integer color2 Second drawing color ; if the argument is not nil, the rectangle will be filled with a gradient from color to color2 +@tparam[opt] integer direction Gradient drawing direction (`gfx.TOP_TO_BOTTOM` or `gfx.LEFT_TO_RIGHT`). This argument is mandatory if a second color was specified. */ static int gfx_rectangle(lua_State *L) { int x = luaL_checkinteger(L, 1); @@ -252,11 +310,23 @@ static int gfx_rectangle(lua_State *L) { float angle = luaL_optnumber(L, 5, 0); u32 color = luaL_optinteger(L, 6, color_default); - - if (angle == 0) - sf2d_draw_rectangle(x, y, width, height, color); - else - sf2d_draw_rectangle_rotate(x, y, width, height, color, angle); + + // Not second color : fill with plain color. + if (lua_isnoneornil(L, 7)) { + if (angle == 0) + sf2d_draw_rectangle(x, y, width, height, color); + else + sf2d_draw_rectangle_rotate(x, y, width, height, color, angle); + // Two colors : fill with a gradient. + } else { + u32 color2 = luaL_checkinteger(L, 7); + u8 direction = luaL_checkinteger(L, 8); + + if (angle == 0) + sf2d_draw_rectangle_gradient(x, y, width, height, color, color2, direction); + else + sf2d_draw_rectangle_gradient_rotate(x, y, width, height, color, color2, direction, angle); + } return 0; } @@ -679,8 +749,10 @@ static const struct luaL_Reg gfx_lib[] = { { "setVBlankWait", gfx_setVBlankWait }, { "waitForVBlank", gfx_waitForVBlank }, { "vramSpaceFree", gfx_vramSpaceFree }, - { "line", gfx_line }, { "point", gfx_point }, + { "line", gfx_line }, + { "triangle", gfx_triangle }, + { "linedTriangle", gfx_linedTriangle }, { "rectangle", gfx_rectangle }, { "linedRectangle", gfx_linedRectangle }, { "circle", gfx_circle }, @@ -709,56 +781,65 @@ static const struct luaL_Reg target_methods[] = { Constants @section constants */ -// Constants struct { char *name; int value; } gfx_constants[] = { /*** Constant used to select the top screen. It is equal to `0`. @field TOP */ - { "TOP", GFX_TOP }, + { "TOP", GFX_TOP }, /*** Constant used to select the bottom screen. It is equal to `1`. @field BOTTOM */ - { "BOTTOM", GFX_BOTTOM }, + { "BOTTOM", GFX_BOTTOM }, /*** Constant used to select the left eye. It is equal to `0`. @field LEFT */ - { "LEFT", GFX_LEFT }, + { "LEFT", GFX_LEFT }, /*** Constant used to select the right eye. It is equal to `1`. @field RIGHT */ - { "RIGHT", GFX_RIGHT }, + { "RIGHT", GFX_RIGHT }, /*** The top screen height, in pixels. It is equal to `240`. @field TOP_HEIGHT */ - { "TOP_HEIGHT", 240 }, + { "TOP_HEIGHT", 240 }, /*** The top screen width, in pixels. It is equal to `400`. @field TOP_WIDTH */ - { "TOP_WIDTH", 400 }, + { "TOP_WIDTH", 400 }, /*** The bottom screen height, in pixels. It is equal to `240`. @field BOTTOM_HEIGHT */ - { "BOTTOM_HEIGHT", 240 }, + { "BOTTOM_HEIGHT", 240 }, /*** The bottom screen width, in pixels. It is equal to `320`. @field BOTTOM_WIDTH */ - { "BOTTOM_WIDTH", 320 }, + { "BOTTOM_WIDTH", 320 }, + /*** + Represents a vertical gradient drawn from the top (first color) to the bottom (second color). + @field TOP_TO_BOTTOM + */ + { "TOP_TO_BOTTOM", SF2D_TOP_TO_BOTTOM }, + /*** + Represents a horizontal gradient drawn from the left (first color) to the right (second color). + @field LEFT_TO_RIGHT + */ + { "LEFT_TO_RIGHT", SF2D_LEFT_TO_RIGHT }, { NULL, 0 } }; From 3303e9783d488362edc21a147cdfdd3630855c2e Mon Sep 17 00:00:00 2001 From: Reuh Date: Mon, 27 Jun 2016 15:38:26 +0200 Subject: [PATCH 098/101] Fix documentation --- source/gfx.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/source/gfx.c b/source/gfx.c index 3893380..bf9dc8d 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -235,7 +235,7 @@ static int gfx_line(lua_State *L) { } /*** -Draw a triangle on the current screen. +Draw a filled triangle on the current screen. @function triangle @tparam integer x1 horizontal coordinate of a vertex of the triangle, in pixels @tparam integer y1 vertical coordinate of a vertex of the triangle, in pixels @@ -261,8 +261,8 @@ static int gfx_triangle(lua_State *L) { } /*** -Draw a triangle on the current screen. -@function triangle +Draw a triangle outline on the current screen. +@function linedTriangle @tparam integer x1 horizontal coordinate of a vertex of the triangle, in pixels @tparam integer y1 vertical coordinate of a vertex of the triangle, in pixels @tparam integer x2 horizontal coordinate of a vertex of the triangle, in pixels @@ -770,10 +770,10 @@ static const struct luaL_Reg gfx_lib[] = { // Render target static const struct luaL_Reg target_methods[] = { - { "__index", gfx_target___index }, - {"clear", gfx_target_clear }, - {"destroy", gfx_target_destroy }, - {"__gc", gfx_target_destroy }, + { "__index", gfx_target___index }, + { "clear", gfx_target_clear }, + { "destroy", gfx_target_destroy }, + { "__gc", gfx_target_destroy }, { NULL, NULL } }; @@ -787,7 +787,7 @@ struct { char *name; int value; } gfx_constants[] = { It is equal to `0`. @field TOP */ - { "TOP", GFX_TOP }, + { "TOP", GFX_TOP }, /*** Constant used to select the bottom screen. It is equal to `1`. From 5888ee381060206070dc795c058ece99cd95d8ce Mon Sep 17 00:00:00 2001 From: Reuh Date: Mon, 27 Jun 2016 19:31:07 +0200 Subject: [PATCH 099/101] Fixed UDP sockets, added documentation, cleaning udp:receivefrom arguments and return value changed so it actually works. --- source/socket.c | 149 ++++++++++++++++++++++++++---------------------- 1 file changed, 81 insertions(+), 68 deletions(-) diff --git a/source/socket.c b/source/socket.c index 08d30c8..947a035 100644 --- a/source/socket.c +++ b/source/socket.c @@ -455,7 +455,7 @@ static int socket_send(lua_State *L) { size_t size = 0; char *data = (char*)luaL_checklstring(L, 2, &size); - size_t sent; + ssize_t sent; if (!userdata->isSSL) { sent = send(userdata->socket, data, size, 0); } else { @@ -483,101 +483,114 @@ UDP sockets */ /*** -Receive some data from a server. +Receive a datagram from the UDP object. @function :receivefrom -@tparam number count amount of data to receive -@tparam string host host name -@tparam number port port -@treturn string data +@tparam[opt=8191] number count maximum amount of bytes to receive from the datagram. Must be lower than 8192. +@treturn[1] string data +@treturn[1] string IP address of the sender +@treturn[1] integer port number of the sender +@treturn[2] nil in case of error or no datagram to receive +@treturn[2] string error message */ static int socket_receivefrom(lua_State *L) { socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); - int count = luaL_checkinteger(L, 2); - size_t namesize = 0; - char *hostname = (char*)luaL_optlstring(L, 3, NULL, &namesize); - int port = luaL_optinteger(L, 4, 0); - - struct sockaddr_in from = {0}; - if (hostname != NULL) { // For a server - struct hostent *hostinfo = gethostbyname(hostname); - if (hostinfo == NULL) { - lua_pushnil(L); - return 1; - } - from.sin_addr = *(struct in_addr*)hostinfo->h_addr; - from.sin_port = htons(port); - from.sin_family = AF_INET; + int count = luaL_optinteger(L, 2, 8191); + + struct sockaddr_in from; + socklen_t addr_len; + + char* buffer = calloc(1, count+1); + ssize_t n = recvfrom(userdata->socket, buffer, count, 0, (struct sockaddr *)&from, &addr_len); + + if (n == 0) { + free(buffer); + lua_pushnil(L); + lua_pushstring(L, "nothing to receive"); + return 2; + + } else if (n < 0) { + free(buffer); + lua_pushnil(L); + lua_pushstring(L, strerror(n)); + return 2; } - char* buffer = malloc(count+1); - int n = recvfrom(userdata->socket, buffer, count, 0, (struct sockaddr*)&from, NULL); - *(buffer+n) = 0x0; - lua_pushstring(L, buffer); - if (hostname != NULL) { - return 1; - } else { - lua_pushstring(L, inet_ntoa(from.sin_addr)); - lua_pushinteger(L, ntohs(from.sin_port)); - return 3; - } + lua_pushstring(L, inet_ntoa(from.sin_addr)); + lua_pushinteger(L, ntohs(from.sin_port)); + + free(buffer); + + return 3; } /*** -Send some data to a server. +Send a datagram to the specified IP and port. @function :sendto @tparam string data data to send -@tparam string host host name -@tparam number port port +@tparam string host IP/hostname of the recipient +@tparam number port port number of the recipient +@treturn[1] boolean true in case of success +@treturn[2] boolean false in case of error +@treturn[2] string error message */ static int socket_sendto(lua_State *L) { socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); - size_t datasize = 0; - char *data = (char*)luaL_checklstring(L, 2, &datasize); - size_t namesize = 0; - char *hostname = (char*)luaL_checklstring(L, 3, &namesize); + size_t datasize; + const char *data = luaL_checklstring(L, 2, &datasize); + const char *hostname = luaL_checkstring(L, 3); int port = luaL_checkinteger(L, 4); - + struct hostent *hostinfo = gethostbyname(hostname); if (hostinfo == NULL) { - lua_pushnil(L); - return 1; + lua_pushboolean(L, false); + lua_pushstring(L, "unknown host"); + return 2; } - struct sockaddr_in to = {0}; - to.sin_addr = *(struct in_addr*)hostinfo->h_addr; + + struct sockaddr_in to; + to.sin_addr = *(struct in_addr *)hostinfo->h_addr; to.sin_port = htons(port); to.sin_family = AF_INET; - - sendto(userdata->socket, data, datasize, 0, (struct sockaddr*)&to, sizeof(to)); - - return 0; + + ssize_t n = sendto(userdata->socket, data, datasize, 0, (struct sockaddr *)&to, sizeof(to)); + + if (n < 0) { + lua_pushboolean(L, false); + lua_pushstring(L, strerror(n)); + return 2; + } + + lua_pushboolean(L, true); + + return 1; } -// module functions +// Module functions static const struct luaL_Reg socket_functions[] = { - {"init", socket_init }, - {"shutdown", socket_shutdown }, - {"tcp", socket_tcp }, - {"udp", socket_udp }, - {"addTrustedRootCA", socket_addTrustedRootCA}, + { "init", socket_init }, + { "shutdown", socket_shutdown }, + { "tcp", socket_tcp }, + { "udp", socket_udp }, + { "addTrustedRootCA", socket_addTrustedRootCA }, {NULL, NULL} }; -// object +// Object methods static const struct luaL_Reg socket_methods[] = { - {"accept", socket_accept }, - {"bind", socket_bind }, - {"close", socket_close }, - {"setBlocking", socket_setBlocking}, - {"__gc", socket_close }, - {"connect", socket_connect }, - {"listen", socket_listen }, - {"receive", socket_receive }, - {"receivefrom", socket_receivefrom}, - {"send", socket_send }, - {"sendto", socket_sendto }, - {"getpeername", socket_getpeername}, - {"getsockname", socket_getsockname}, + { "accept", socket_accept }, + { "bind", socket_bind }, + { "close", socket_close }, + { "setBlocking", socket_setBlocking }, + { "__gc", socket_close }, + { "connect", socket_connect }, + { "listen", socket_listen }, + { "receive", socket_receive }, + { "receivefrom", socket_receivefrom }, + { "send", socket_send }, + { "sendto", socket_sendto }, + { "getpeername", socket_getpeername }, + { "getsockname", socket_getsockname }, {NULL, NULL} }; From f118baa37c242a383c4e11ab975eef3ba7585ea6 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Mon, 27 Jun 2016 20:16:38 +0200 Subject: [PATCH 100/101] README rewrite --- README.md | 81 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c8e96c4..fcd4421 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,86 @@ # ctrµLua - -Everything is in the Wiki. +![banner](https://www.dropbox.com/s/cqmtoohyx6t7q7c/banner.png?raw=1) Warning: the 'u' in the repo's name is a 'µ', not a 'u'. +### Users part + +#### How to install + +* Download ctruLua.zip from the [releases](https://github.com/ctruLua/ctruLua/releases) (for something stable) or the [CI server](http://ci.reuh.tk/ctrulua) (for more features) +* Unzip it on your SD card, in a folder inside your homebrews folder; if you use HBL, extract it in `/3ds/ctrulua/` +* Put some scripts wherever you want, just remember where +* Launch CtrµLua from your homebrew launcher +* Use the shell to run your scripts + +### Homebrewers part + #### Builds ![build status](http://ci.reuh.tk/ctrulua.png) -* Most recent working build: [here](http://ci.reuh.tk/ctrulua/builds/latest/artifacts/ctruLua.3dsx) (warning: only tested with Citra, sometimes on real hardware). -* See http://ci.reuh.tk/ctrulua for all the builds. +* Most recent working build: [ctruLua.3dsx](http://ci.reuh.tk/ctrulua/builds/latest/artifacts/ctruLua.3dsx) +* See [http://ci.reuh.tk/ctrulua](http://ci.reuh.tk/ctrulua) for all the builds. + +#### Hello world + +```Lua +local ctr = require("ctr") +local gfx = require("ctr.gfx") +local hid = require("ctr.hid") + +while ctr.run() do + hid.read() + local keys = hid.keys() + if keys.held.start then break end + + gfx.start(gfx.TOP) + gfx.text(2, 2, "Hello, world !") + gfx.stop() + + gfx.render() +end +``` +This script will print "Hello, world !" on the top screen, and will exit if the user presses Start. +This is the "graphical" version; there's also a text-only version, based on the console: +```Lua +local ctr = require("ctr") +local gfx = require("ctr.gfx") +local hid = require("ctr.hid") + +gfx.console() +print("Hello, world !") + +while ctr.run() do + hid.read() + local keys = hid.keys() + if keys.held.start then break end + + gfx.render() +end + +gfx.disableConsole() +``` + +#### Lua API Documentation + +* An online version of the documentation can be found [here](http://reuh.tk/ctrulua) +* To build the documentation, run `make build-doc-html` (requires [LDoc](https://github.com/stevedonovan/LDoc)). + +### Developers part #### Build instructions * Setup your environment as shown here : http://3dbrew.org/wiki/Setting_up_Development_Environment * Clone this repository and run the command `make build-all` to build all the dependencies. -* If you only made changes to ctrµLua, run `make` to rebuild ctµLua without rebuilding all the dependencies. +* If you only made changes to ctrµLua, run `make` to rebuild ctrµLua without rebuilding all the dependencies. May not work under Windows. -#### Lua API Documentation +### Credits -* An online version of the documentation can be found here : http://reuh.tk/ctrulua -* To build the documentation, run `make build-doc` (requires [LDoc](https://github.com/stevedonovan/LDoc)). - -#### Based on ctrulib by smealum: [https://github.com/smealum/ctrulib](https://github.com/smealum/ctrulib) +* __Smealum__ and everyone who worked on the ctrulib: [https://github.com/smealum/ctrulib](https://github.com/smealum/ctrulib) +* __Xerpi__ for the [sf2dlib](https://github.com/xerpi/sf2dlib), [sftdlib](https://github.com/xerpi/sftdlib) and [sfillib](https://github.com/xerpi/sfillib) +* __All the [Citra](https://citra-emu.org/) developers__ +* __Everyone who worked on [DevKitARM](http://devkitpro.org/)__ +* __Nothings__ for the [stb](https://github.com/nothings/stb) libs +* Everyone who worked on the other libs we use + From 9a97ad0bcad99f95d37f30145660ffb16accf617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Reuh=20Fildadut?= Date: Sun, 27 Jun 2021 14:27:16 +0200 Subject: [PATCH 101/101] Update links before they break --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fcd4421..75dbf84 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Warning: the 'u' in the repo's name is a 'µ', not a 'u'. #### How to install -* Download ctruLua.zip from the [releases](https://github.com/ctruLua/ctruLua/releases) (for something stable) or the [CI server](http://ci.reuh.tk/ctrulua) (for more features) +* Download ctruLua.zip from the [releases](https://github.com/ctruLua/ctruLua/releases) (for something stable) or the [CI server](https://reuh.eu/ctrulua/ci/ctrulua) (for more features) * Unzip it on your SD card, in a folder inside your homebrews folder; if you use HBL, extract it in `/3ds/ctrulua/` * Put some scripts wherever you want, just remember where * Launch CtrµLua from your homebrew launcher @@ -15,10 +15,10 @@ Warning: the 'u' in the repo's name is a 'µ', not a 'u'. ### Homebrewers part -#### Builds ![build status](http://ci.reuh.tk/ctrulua.png) +#### Builds ![build status](https://reuh.eu/ctrulua/ci/ctrulua.png) -* Most recent working build: [ctruLua.3dsx](http://ci.reuh.tk/ctrulua/builds/latest/artifacts/ctruLua.3dsx) -* See [http://ci.reuh.tk/ctrulua](http://ci.reuh.tk/ctrulua) for all the builds. +* Most recent working build: [ctruLua.3dsx](https://reuh.eu/ctrulua/ci/ctrulua/builds/latest/artifacts/ctruLua.3dsx) +* See [https://reuh.eu/ctrulua/ci/ctrulua](https://reuh.eu/ctrulua/ci/ctrulua) for all the builds. #### Hello world @@ -62,7 +62,7 @@ gfx.disableConsole() #### Lua API Documentation -* An online version of the documentation can be found [here](http://reuh.tk/ctrulua) +* An online version of the documentation can be found [here](https://reuh.eu/ctrulua/latest/html/) * To build the documentation, run `make build-doc-html` (requires [LDoc](https://github.com/stevedonovan/LDoc)). ### Developers part