Ftf libs
Here are some longer code (classes) from the Gideros forum and some other places :-)
All samples are ready to use. Enjoy!
AUDIO
Producing Sound Without Resources @PaulH
Creates files on the fly using the .wav format for playback
-- https://forum.gideros.rocks/discussion/comment/68095/#Comment_68095
function make_wave(file_name, length, wave, freq, vol)
-- We'll need to write some 32 bit values in binary. Helper function for that:
local function to4Bytes(n) return n & 0xff, (n>>8) & 0xff, (n>>16) & 0xff, (n>>24) & 0xff end
-- For a given point (sample number) in a wav file, return the 16 bit value for the specified sound wave and frequency
local function sound_wave(wave,p,freq,vol)
local sample_rate = 6000
local n = 0 -- Default to silence
if wave == "noise" then n = math.random(-255*128*vol, 255*128*vol)
elseif wave == "sine" then n = math.floor(math.sin(p/sample_rate*freq*math.rad(180))*256*vol*127)
elseif wave == "sawtooth" then n = math.floor((freq*p/sample_rate)*256*vol*256)
end
local c2, c1 = (n>>8) & 0xff, n & 0xff
return string.char(c1, c2)
end
-- We're just dealing with a fixed format wav file:
local sample_rate = 6000
local samples = length*sample_rate
local data_size = samples*2
local total_file_length = 44+data_size
local fl1, fl2, fl3, fl4 = to4Bytes(total_file_length)
local ds1, ds2, ds3, ds4 = to4Bytes(data_size)
local f = io.open(file_name, "wb")
-- Write the WAV file header:
f:write(
string.char(
82,73,70,70,--RIFF
fl1, fl2, fl3, fl4,-- File size overall
87,65,86,69,-- WAVE header
102,109,116,32,--fmt chunk marker w/ trailing null
16,0,0,0, -- Length of format data (16 bytes before this)
1,0, -- Wave type. 1 == PCM
1,0, -- Channels. 1 == mono
112,23,0,0,-- Sample rate (Hertz)
224,46,0,0, -- Bits per sample * channels * sample rate / 8
2,0,
16,0, -- Bytes per sample - this is 16
100,97,116,97, -- "data" - chunk header
ds1,ds2,ds3,ds4-- size of data section
)
)
-- Write the data:
for l = 1, samples do
f:write(sound_wave(wave,l,freq, vol or 1))
end
f:close()
end
-- Demo some white noise:
--make_wave("|D|noise.wav", 1, "noise") -- 0.1 second sine wave at 256 Herz (middle C) at full volume
--s=Sound.new("|D|noise.wav")
--s:play()
-- one note with a sawtooth wave:
--make_wave("|D|note.wav", 2, "sawtooth", 262, 1) -- 0.1 second sine wave at 256 Herz (middle C) at full volume
--s=Sound.new("|D|note.wav")
--s:play()
-- Make wave files for the chromatic scale.
-- The frequencies in Hertz of the notes in octave zero:
notes = { 16.35, 17.32, 18.35, 19.45, 20.60, 21.83, 23.12, 24.5, 25.96, 27.50, 29.14, 30.87 }
-- 3 letter names for those notes:
names = { "C0n", "C0s", "D0n", "D0s", "E0n", "F0n", "F0s", "G0n", "G0s", "A0n", "A0s", "B0n" }
-- Compute the ratio of each note to the C:
local note_multipliers = {}
for i = 1, #notes do
note_multipliers[i] = notes[i]/notes[1]
end
-- Make a sound for each note in several octaves:
local sounds = {}
for octave = 3, 7 do
for i = 1, #names do
local original_note = names[i]
local octave_note = string.gsub(original_note, "0", octave)
local file_name = "|D|note_" .. octave_note .. ".wav"
-- Compute the frequency for the note by applying the ratio of that note over C
-- and then apply the octave multiplier. Each octave is double the frequency
-- of the one below it:
local fr = math.floor(notes[1] * note_multipliers[i] * math.pow(2, octave))
-- We'll make each note 0.2 seconds, using a sine wave
--function make_wave(file_name, length, wave, freq, vol)
make_wave(file_name, 0.2, "sine", fr, 1)
sounds[octave_note] = Sound.new(file_name)
end
end
-- Given a string of 3 letter note names, play those notes in order:
function play_sequence(seq)
for i = 1, string.len(seq), 3 do
local note = string.sub(seq, i, i+2)
if sounds[note] then
Timer.delayedCall(60 * (i-1), function()
sounds[note]:play()
end)
end
end
end
local fur_elise =
"E4nD4sE4nD4sE4nB3nD4nC4nA3n "..
"C3nE3nA3nB3n "..
"E3nG3sB3nC4n "..
"E3nE4nD4sE4nD4sE4nB3nD4nC4nA3n "..
"C3nE3nA3nB3n "..
"E3nC4nB3nA3n"
play_sequence(fur_elise)
-- To play an octave higher:
Timer.delayedCall(9000, function()
play_sequence(string.gsub(string.gsub(fur_elise,"4","5"),"3","4"))
end)
STRING
Score Counting TextField @errorpi
--[[
Score Counting TextField
by: @errorpi
]]
Score = Core.class(Sprite)
function Score:init(xscore, xincrease)
self.score = xscore or 0
self.countTime = (xincrease or 5) * 100
-- a textfield
self.scoreText = TextField.new(nil, tostring(self.score))
self.scoreText:setLayout({wid = 80, hei = 40, flags=FontBase.TLF_CENTER})
self.scoreText:setTextColor(math.random(0xffffff))
self.scoreText:setScale(5)
self.scoreText:setPosition(64, 64)
self:addChild(self.scoreText)
-- a timer
self.t = Timer.new(1) -- timer init
self.t:addEventListener(Event.TIMER, self.updateText, self)
end
function Score:updateText()
local displayScore = tonumber(self.scoreText:getText())
if displayScore < self.score then
self.scoreText:setText(tostring( displayScore + 1) )
end
end
function Score:addScore(add)
self.score += add
local displayScore = tonumber(self.scoreText:getText())
-- set timer properties (bigger add score means faster steps)
local tRepeat = self.score - displayScore
local tDelay = self.countTime / tRepeat
self.t:stop()
self.t:reset()
self.t:setDelay(tDelay)
self.t:setRepeatCount(tRepeat)
self.t:start()
return self.score
end
--------------------
application:setBackgroundColor(0x4d4d4d)
-- demo
local initialscore = 500
local score = Score.new(initialscore, 6) -- initial score value, increase speed
stage:addChild(score)
local earnedpoints = 300
Timer.delayedCall(3000, function()
score:addScore(earnedpoints) -- target score value
end)
GFX
Hex to Number / Number to Hex @hanzhao
--[[
Lua Hex v0.4
-------------------
Hex conversion lib for Lua.
How to use:
lhex.to_hex(n) -- convert a number to a hex string
lhex.to_dec(hex) -- convert a hex "string" (prefix with "0x..." or "0X...") to number
Part of LuaBit(http://luaforge.net/projects/bit/).
Under the MIT license.
copyright(c) 2006~2007 hanzhao (abrash_han@hotmail.com)
--]]
local function to_bits(n)
-- checking not float
if(n - (n//1) > 0) then error("trying to apply bitwise operation on non-integer!") end
if(n < 0) then return to_bits(~(n//1) + 1) end -- negative
-- to bits table
local tbl = {}
local cnt = 1
while (n > 0) do
local last = n%2
if(last == 1) then tbl[cnt] = 1
else tbl[cnt] = 0
end
n = (n-last)/2
cnt += 1
end
return tbl
end
local function tbl_to_number(tbl)
local n = #tbl
local rslt = 0
local power = 1
for i = 1, n do
rslt += tbl[i]*power
power *= 2
end
return rslt
end
local function to_hex(n)
if(type(n) ~= "number") then error("non-number type passed in.") end
-- checking not float
if(n - (n//1) > 0) then error("trying to apply bitwise operation on non-integer!") end
if(n < 0) then -- negative
n = to_bits(~(-n<>n) + 1)
n = tbl_to_number(n)
end
local hex_tbl = { "A", "B", "C", "D", "E", "F" }
local hex_str = ""
while(n ~= 0) do
local last = n%16
if(last < 10) then hex_str = tostring(last) .. hex_str
else hex_str = hex_tbl[last-10+1] .. hex_str
end
n = (n/16)//1 -- floor
end
if(hex_str == "") then hex_str = "0" end
return "0x"..hex_str
end
local function to_dec(hexstring)
if(type(hexstring) ~= "string") then error("non-string type passed in.") end
local head = string.sub(hexstring, 1, 2)
if(head ~= "0x" and head ~= "0X") then error("wrong hex format, should lead by 0x or 0X.") end
return tonumber(hexstring:sub(3), 16) -- base 16
end
--------------------
-- lua hex lib interface
lhex = {
to_hex=to_hex,
to_dec=to_dec,
}
--------------------
-- examples
local dec = 4341688
local hex = lhex.to_hex(dec) -- number to hex
print(hex) -- 0x423FB8
local revdec = lhex.to_dec(hex) -- hex string (prefix with "0x" or "0X") to number
print(revdec) -- 4341688
print(lhex.to_dec("0x0")) -- 0, hex string (prefix with "0x" or "0X") to number
print(lhex.to_dec("0x00ff00")) -- 65280, hex string (prefix with "0x" or "0X") to number
print(lhex.to_hex(16777215)) -- 0xFFFFFF, number to hex
local pix = Pixel.new(lhex.to_hex(56816), 1, 32, 32)
stage:addChild(pix)
print("hex is", hex)
GTween Demo @rrraptor
require "easing"
application:setBackgroundColor(0x4c4c4c)
function animate(values) -- create a new GTween or use existing one
local function NULL() end
values.target.gtween = values.target.gtween or nil -- GTween
values.duration = values.duration or 0
values.values = values.values or {}
values.props = values.props or { delay=0, ease=easing.linear, repeatCount=1, dispatchEvents=false, }
values.target.callback = values.target.callback or NULL
if values.target.gtween then -- use existing gtween
print("using existing gtween", math.random(100))
values.target.gtween:setValues(values.values)
else -- create new gtween
print("creating new gtween", math.random(100))
--function GTween:init(target, duration, values, props)
values.target.gtween = GTween.new(values.target, values.duration, values.values, values.props)
values.target.gtween:addEventListener("complete", values.target.callback, values.target.gtween)
-- values.target.gtween:addEventListener("change", function() print("tween changing") end)
end
values.target.gtween:setPosition(0)
values.target.gtween:setPaused(false)
end
local function followTween(data, e)
if data.source == nil then
print("you need to provide a source for the gtween!")
return
end
data.source:setColor(math.random(0xffffff))
local duration = 1
local values = {} -- target values
values.x = e.touch.x
values.y = e.touch.y
values.alpha = 1.2
values.scaleX = data.source.sx * 1
values.scaleY = data.source.sy * 1.5
local properties = {}
properties.delay = 64
properties.ease = easing.outBack
properties.repeatCount = 1
properties.dispatchEvents = true
animate( --function GTween:init(target, duration, values, props)
{
target = data.source, -- target = source + source.gtween + source.callback
duration = duration,
values = values,
props = properties,
}
)
end
-- gtween playground
local pix = Pixel.new(math.random(0xffffff), 1, 64, 64)
pix:setAnchorPoint(0.5, 0)
pix:setPosition(application:getContentWidth()/2, application:getContentHeight()/2)
pix.gtween = nil
pix.sx = pix:getScaleX()
pix.sy = pix:getScaleY()
pix.callback = function()
print("pix tween ended", math.random(100))
pix.gtween.nextTween = GTween.new(
pix, 0.3,
{ -- target values
alpha = 1,
scaleX = pix.sx * 1.5,
scaleY = pix.sy * 0.5,
},
{
ease = easing.outBounce,
repeatCount = 1,
}
)
end
stage:addChild(pix)
stage:addEventListener(Event.TOUCHES_END, followTween, { source=pix, })
2D Blue Noise points @ai
There are two main ways to generate "Blue Noise" in Lua, depending on what you need it for:
- Poisson Disk Sampling (Bridson's Algorithm): Best for scattering objects (trees, stars, particles) evenly but randomly. This generates a list of coordinates.
- Void and Cluster (Texture Generation): Best for creating a grid of pixel values (like a dithering texture). This is significantly more complex to implement from scratch.
Below is a robust, standard implementation of Poisson Disk Sampling in pure Lua. This is the most common approach for game development and procedural generation.
-- 2D Blue Noise points ai generated (https://chat.z.ai/c/efa8ba11-749f-4460-9342-b0928a65f40f)
local function generateBlueNoise(width, height, minDistance, k)
-- Seed the random number generator
math.randomseed(os.time())
-- 1. Calculate cell size based on minimum distance
-- The grid cell size ensures that we only check a small number of neighbors
-- r / sqrt(2) is the maximum distance a point can be from the center of a cell
local cellSize = minDistance / math.sqrt(2)
-- local gridWidth = math.ceil(width / cellSize)
-- local gridHeight = math.ceil(height / cellSize)
-- 2. Initialize the Grid and Active List
-- The grid acts as a spatial hash to speed up distance checks
local grid = {}
local activeList = {}
local points = {}
-- Helper function to convert (x, y) to grid coordinates
local function toGridCoords(x, y)
return math.floor(x / cellSize) + 1, math.floor(y / cellSize) + 1
end
-- Helper to safely get a point from the grid
local function getGridPoint(gx, gy)
if grid[gx] then return grid[gx][gy] end
return nil
end
-- Helper to put a point in the grid
local function setGridPoint(gx, gy, point)
if not grid[gx] then grid[gx] = {} end
grid[gx][gy] = point
end
-- 3. Add the first random point
local firstX = math.random() * width
local firstY = math.random() * height
local firstPoint = { x = firstX, y = firstY }
table.insert(points, firstPoint)
local gx, gy = toGridCoords(firstX, firstY)
setGridPoint(gx, gy, firstPoint)
table.insert(activeList, firstPoint)
-- 4. Main Loop
while #activeList > 0 do
-- Pick a random index from the active list
local randomIndex = math.random(1, #activeList)
local parentPoint = activeList[randomIndex]
local found = false
-- Try to find a new point 'k' times (usually 30 is sufficient)
for i = 1, k do
-- Generate a random point in an annulus (donut) around the parent
local angle = math.random() * 2 * math.pi
-- Radius is between minDistance and 2 * minDistance
local radius = math.random(minDistance, 2 * minDistance)
local newX = parentPoint.x + math.cos(angle) * radius
local newY = parentPoint.y + math.sin(angle) * radius
-- Check if the point is within bounds
if newX >= 0 and newX <= width and newY >= 0 and newY <= height then
local ngx, ngy = toGridCoords(newX, newY)
local isValid = true
-- Check neighbors in the surrounding 3x3 grid cells
for ix = -1, 1 do
for iy = -1, 1 do
local neighbor = getGridPoint(ngx + ix, ngy + iy)
if neighbor then
local dx = neighbor.x - newX
local dy = neighbor.y - newY
-- If distance is less than minDistance, it's too close
if math.sqrt(dx*dx + dy*dy) < minDistance then
isValid = false
break
end
end
end
if not isValid then break end
end
-- If valid, add to lists
if isValid then
local newPoint = { x = newX, y = newY }
table.insert(points, newPoint)
setGridPoint(ngx, ngy, newPoint)
table.insert(activeList, newPoint)
found = true
break -- Found a valid point for this parent, stop trying
end
end
end
-- If we couldn't find a point after k tries, remove the parent from active list
if not found then
table.remove(activeList, randomIndex)
end
end
return points
end
-- --- Usage Example ---
local w, h = 256, 256 -- 500, 500
local minDist = 16 -- 20
local noisePoints = generateBlueNoise(w, h, minDist, 30)
print("Generated " .. #noisePoints .. " blue noise points.")
for _, v in ipairs(noisePoints) do
-- print(v.x, v.y)
local pixel = Pixel.new(math.random(0xffffff), 1, 4, 4)
pixel:setPosition(v.x, v.y)
stage:addChild(pixel)
end
3D Blue Noise points @ai
Generating 3D Blue Noise usually involves creating a "Poisson Disk Sampling" of points. This ensures that points are randomly distributed but evenly spaced (no two points are too close), which prevents clumping and creates that desirable "blue" frequency spectrum.
Here is a clean, efficient implementation in Lua using the Best Candidate Algorithm with a Spatial Hash Grid for optimization.
-- 3D Blue Noise points ai generated (https://chat.z.ai/c/efa8ba11-749f-4460-9342-b0928a65f40f)
local math = math
local random = math.random
--- Generate 3D Blue Noise points
--- @param count number: The number of points to generate
--- @param minDist number: The minimum distance between points (radius)
--- @param width number: Width of the volume
--- @param height number: Height of the volume
--- @param depth number: Depth of the volume
--- @return table: Array of points {x, y, z}
local function generate3DBlueNoise(count, minDist, width, height, depth)
local points = {}
local grid = {}
-- Optimization: Cell size determines the resolution of our spatial grid.
-- If points are at least minDist apart, we only need to check adjacent cells.
local cellSize = minDist / 1.414 -- Slightly smaller to be safe
local cellSizeInv = 1 / cellSize
-- Helper: Get a unique key for a grid cell
local function getGridKey(x, y, z)
local gx = math.floor(x * cellSizeInv)
local gy = math.floor(y * cellSizeInv)
local gz = math.floor(z * cellSizeInv)
return string.format("%d|%d|%d", gx, gy, gz)
end
-- Helper: Check if a point is valid (not too close to existing points)
local function isValid(p)
local gx = math.floor(p.x * cellSizeInv)
local gy = math.floor(p.y * cellSizeInv)
local gz = math.floor(p.z * cellSizeInv)
-- Check the 3x3x3 neighborhood around the candidate
for ix = -1, 1 do
for iy = -1, 1 do
for iz = -1, 1 do
local key = string.format("%d|%d|%d", gx + ix, gy + iy, gz + iz)
local existing = grid[key]
if existing then
local dx = p.x - existing.x
local dy = p.y - existing.y
local dz = p.z - existing.z
-- Optimization: compare squared distance to avoid square root
if (dx*dx + dy*dy + dz*dz) < (minDist * minDist) then
return false
end
end
end
end
end
return true
end
-- Helper: Find approximate distance to nearest existing point
-- (Used to pick the "best" candidate among the random tries)
local function getNearestDist(p)
local closestSq = minDist * minDist -- Initialize to max allowed
local gx = math.floor(p.x * cellSizeInv)
local gy = math.floor(p.y * cellSizeInv)
local gz = math.floor(p.z * cellSizeInv)
for ix = -1, 1 do
for iy = -1, 1 do
for iz = -1, 1 do
local key = string.format("%d|%d|%d", gx + ix, gy + iy, gz + iz)
local existing = grid[key]
if existing then
local dx = p.x - existing.x
local dy = p.y - existing.y
local dz = p.z - existing.z
local dSq = dx*dx + dy*dy + dz*dz
if dSq < closestSq then
closestSq = dSq
end
end
end
end
end
return closestSq
end
-- 1. Add the first point completely randomly
local firstPoint = {
x = random() * width,
y = random() * height,
z = random() * depth
}
table.insert(points, firstPoint)
grid[getGridKey(firstPoint.x, firstPoint.y, firstPoint.z)] = firstPoint
-- 2. Generate subsequent points
local k = 30 -- The number of candidates to try per point (Standard is 30)
for i = 2, count do
local bestCandidate = nil
local maxDistToNeighbor = -1
-- Try 'k' times to find a valid point
for attempt = 1, k do
local candidate = {
x = random() * width,
y = random() * height,
z = random() * depth
}
if isValid(candidate) then
-- Among valid candidates, pick the one furthest from others
local d = getNearestDist(candidate)
if d > maxDistToNeighbor then
maxDistToNeighbor = d
bestCandidate = candidate
end
end
end
if bestCandidate then
table.insert(points, bestCandidate)
grid[getGridKey(bestCandidate.x, bestCandidate.y, bestCandidate.z)] = bestCandidate
else
-- If we fail to find a point after 'k' tries, the volume might be full.
-- We can stop early or just print a warning.
print("Warning: Could not place point " .. i .. ". Density limit reached?")
break
end
end
return points
end
-- ==========================================
-- EXAMPLE USAGE
-- ==========================================
math.randomseed(os.time()) -- Don't forget to seed!
-- Generate 500 points in a 100x100x100 cube
-- Points must be at least 5 units apart
local noisePoints = generate3DBlueNoise(500, 5.0, 100, 100, 100)
print("Generated " .. #noisePoints .. " points.")
-- Print first 5 points to console
for i = 1, 5 do
local p = noisePoints[i]
print(string.format("Point %d: x=%.2f, y=%.2f, z=%.2f", i, p.x, p.y, p.z))
end
FILES
Get Files in Folder @xxx
note: requires the Lfs plugin
Gets all the files in a folder matching a file extension and store them in a list.
local myos = application:getDeviceInfo()
local iswin32 = if myos == "win32" then true else false
--print(iswin32)
function getFilesInFolder(xpath, xlist)
local lfs = require "lfs"
for file in lfs.dir(xpath) do
if file ~= "." and file ~= ".." then
if file:match(".png$") then -- choose your file extension here
if iswin32 then
local f = xpath.."\\"..file
-- print(f)
xlist[#xlist + 1] = f:gsub("/", "\\")
else
local f = xpath.."/"..file
-- print(f)
xlist[#xlist + 1] = f:gsub("\\", "/")
end
end
end
end
lfs = nil
end
Demo code using ButtonMonster
local myos = application:getDeviceInfo()
local iswin32 = if myos == "win32" then true else false
--print(iswin32)
local folderimages = {} -- list all images in user selected folder
local images = {} -- list all images with valid extension
local btnimagefolder = ButtonMonster.new({
pixelcolorup=0xffaa00, text="import images", textcolorup=0xaa0000, textscalex=3,
isautoscale=true,
})
btnimagefolder:setPosition(16, 16)
stage:addChild(btnimagefolder)
btnimagefolder:addEventListener("clicked", function()
local path
if iswin32 then path = application:get("openDirectoryDialog", "Select Folder", "C:\\tmp\\")
else path = application:get("openDirectoryDialog", "Select Folder", "C:/tmp/")
end -- print(path)
if path then getFilesInFolder(path, folderimages) end
if folderimages and #folderimages > 0 then
for i = 1, #folderimages do
images[i] = Bitmap.new(Texture.new(folderimages[i]), true)
images[i]:setAnchorPoint(0.5, 1)
images[i]:setPosition(8, 8)
-- ...
end
end
end)
Xml Parser @Alexander Makeev
-----------------------------------------------------------------------------------------
-- LUA only XmlParser from Alexander Makeev
-----------------------------------------------------------------------------------------
XmlParser = {}
function XmlParser:ToXmlString(value)
value = string.gsub(value, "&", "&") -- '&' -> "&"
value = string.gsub(value, "<", "<") -- '<' -> "<"
value = string.gsub(value, ">", ">") -- '>' -> ">"
--value = string.gsub(value, "'", "'") -- '\'' -> "'"
value = string.gsub(value, "\"", """) -- '"' -> """
-- replace non printable char -> "
"
value = string.gsub(value, "([^%w%&%;%p%\t% ])", function(c)
return string.format("&#x%X;", string.byte(c))
--return string.format("&#x%02X;", string.byte(c))
--return string.format("&#%02d;", string.byte(c))
end)
return value
end
function XmlParser:FromXmlString(value)
value = string.gsub(value, "&#x([%x]+)%;", function(h)
return string.char(tonumber(h,16))
end)
value = string.gsub(value, "&#([0-9]+)%;", function(h)
return string.char(tonumber(h,10))
end)
value = string.gsub(value, """, "\"")
value = string.gsub(value, "'", "'")
value = string.gsub(value, ">", ">")
value = string.gsub(value, "<", "<")
value = string.gsub(value, "&", "&")
return value
end
function XmlParser:ParseArgs(s)
local arg = {}
string.gsub(s, "(%w+)=([\"'])(.-)%2", function(w, _, a)
arg[w] = self:FromXmlString(a)
end)
return arg
end
function XmlParser:ParseXmlText(xmlText)
local stack = {}
local top = { Name=nil, Value=nil, Attributes={}, ChildNodes={} }
table.insert(stack, top)
local ni, c, label, xarg, empty
local i, j = 1, 1
while true do
ni, j, c, label, xarg, empty = string.find(xmlText, "<(%/?)([%w:]+)(.-)(%/?)>", i)
if not ni then break end
local text = string.sub(xmlText, i, ni-1)
if not string.find(text, "^%s*$") then
top.Value=(top.Value or "")..self:FromXmlString(text)
end
if empty == "/" then -- empty element tag
table.insert(top.ChildNodes,
{
Name=label, Value=nil, Attributes=self:ParseArgs(xarg), ChildNodes={}
}
)
elseif c == "" then -- start tag
top = { Name=label, Value=nil, Attributes=self:ParseArgs(xarg), ChildNodes={} }
table.insert(stack, top) -- new level
--log("openTag ="..top.Name)
else -- end tag
local toclose = table.remove(stack) -- remove top
--log("closeTag="..toclose.Name)
top = stack[#stack]
if #stack < 1 then
error("XmlParser: nothing to close with "..label)
end
if toclose.Name ~= label then
error("XmlParser: trying to close "..toclose.Name.." with "..label)
end
table.insert(top.ChildNodes, toclose)
end
i = j + 1
end
local text = string.sub(xmlText, i)
if not string.find(text, "^%s*$") then
stack[#stack].Value=(stack[#stack].Value or "")..self:FromXmlString(text)
end
if #stack > 1 then
error("XmlParser: unclosed "..stack[stack.n].Name)
end
return stack[1].ChildNodes[1]
end
function XmlParser:ParseXmlFile(xmlFileName)
local hFile, err = io.open(xmlFileName,"r")
if (not err) then
local xmlText=hFile:read("*a") -- read file content
io.close(hFile)
return self:ParseXmlText(xmlText),nil
else
return nil, err
end
end
--[[
-- Usage:
-- parse xml
local xml = XmlParser:ParseXmlText(myxmlfile)
]]
TABLE
Var Dump @xxx
function vardump(value, depth, key)
local linePrefix = ""
local spaces = ""
if key ~= nil then
linePrefix = "["..key.."] = "
end
if depth == nil then
depth = 0
else
depth = depth + 1
for i = 1, depth do
spaces = spaces .. " "
end
end
if type(value) == 'table' then
local mTable = getmetatable(value)
if mTable == nil then
print(spaces ..linePrefix.."(table) ")
else
print(spaces .."(metatable) ")
value = mTable
end
for tableKey, tableValue in pairs(value) do
vardump(tableValue, depth, tableKey)
end
elseif type(value) == 'function' or
type(value) == 'thread' or
type(value) == 'userdata' or
value == nil
then
print(spaces..tostring(value))
else
print(spaces..linePrefix.."("..type(value)..") "..tostring(value))
end
end
--[[
Usage:
foo="Hello world"
vardump(foo)
vardump( { print, 12, nil, io.stdin, math } )
foo = {
"zero",1,2,3,{1,{1,2,3,4,{1,2,{1,"cool",2},4},6},3,vardump,5,6}, 5,{Mary=10, Paul="10"},"last value"
}
vardump(foo)
--]]
More to come God's willing...