diff --git a/Libs/AudioManager.lua b/Libs/AudioManager.lua new file mode 100644 index 0000000..04d49ed --- /dev/null +++ b/Libs/AudioManager.lua @@ -0,0 +1,212 @@ +audio = {} +audio.__index = audio +function audio:new(f,t) + local obj={} + setmetatable(obj, audio) + obj.source=love.audio.newSource(f,t) + obj.f=f + obj.t=t or "stream" + obj.endEvent=multi:newLoop() + obj.endEvent.Pare=obj + obj.wasPlaying=false + obj.func={} + obj.func2={} + obj.func3={} + obj.func4={} + obj.endEvent:OnLoop(function(time,loop) + if not(loop.Pare:isPlaying()) and loop.Pare.wasPlaying==true and not(loop.Pare:isPaused()) then + for i=1,#loop.Pare.func do + loop.Pare:stop() + loop.Pare.wasPlaying=false + loop.Pare.endEvent:Pause() + loop.Pare.func[i](loop.Pare) + end + end + end) + obj.endEvent:Pause() + return obj +end +function audio:clone() + local _temp=audio:new(self.f,self.t) + _temp.source=self.source:clone() + return _temp +end +--Mutators +function audio:play() + if self:isPaused() then + for i=1,#self.func4 do + self.func4[i](self) + end + self:resume() + else + for i=1,#self.func3 do + self.func3[i](self) + end + self.source:play() + self.wasPlaying=true + self.endEvent:Resume() + end +end +function audio:stop() + self.source:stop() + self.wasPlaying=true + self.endEvent:Pause() +end +function audio:pause() + for i=1,#self.func2 do + self.func2[i](self) + end + self.source:pause() +end +function audio:resume() + self.source:resume() +end +function audio:rewind() + self.source:rewind() +end +function audio:setAttenuationDistances(r,m) + self.source:setAttenuationDistances(r,m) +end +function audio:setCone(innerAngle, outerAngle, outerVolume) + self.source:setCone(innerAngle, outerAngle, outerVolume) +end +function audio:setDirection(x, y, z) + self.source:setDirection(x, y, z) +end +function audio:setLooping(loop) + self.source:setLooping(loop) +end +function audio:setPitch(pitch) + self.source:setPitch(pitch) +end +function audio:setPosition(x, y, z) + self.source:setPosition(x, y, z) +end +function audio:setRelative(enable) + self.source:setRelative(enable) +end +function audio:setRolloff(rolloff) + self.source:setRolloff(rolloff) +end +function audio:setVelocity(x, y, z) + self.source:setVelocity(x, y, z) +end +function audio:setVolume(volume) + self.source:setVolume(volume) +end +function audio:setVolumeLimits(min, max) + self.source:setVolumeLimits(min, max) +end +function audio:seek(offset,unit) + self.source:seek(offset,unit) +end +--Assessors +function audio:isPlaying() + return self.source:isPlaying() +end +function audio:isPaused() + return self.source:isPaused() +end +function audio:isStopped() + return self.source:isStopped() +end +function audio:isLooping() + return self.source:isLooping() +end +function audio:isStatic() + return self.source:isStatic() +end +function audio:isRelative() + return self.source:isRelative() +end +function audio:getAttenuationDistances() + return self.source:getAttenuationDistances() +end +function audio:getChannels() + return self.source:getChannels() +end +function audio:getCone() + return self.source:getCone() +end +function audio:getDirection() + return self.source:getDirection() +end +function audio:getPitch() + return self.source:getPitch() +end +function audio:getPosition() + return self.source:getPosition() +end +function audio:getRolloff() + return self.source:getRolloff() +end +function audio:getVelocity() + return self.source:getVelocity() +end +function audio:getVolume() + return self.source:getVolume() +end +function audio:getVolumeLimits() + return self.source:getVolumeLimits() +end +function audio:tell(unit) + return self.source:tell(unit) +end +function audio:type() + return self.source:type() +end +function audio:typeOf() + return self.source:typeOf() +end +--Events +function audio:onResume(func) + table.insert(self.func4,func) +end +function audio:onPlay(func) + table.insert(self.func3,func) +end +function audio:onPause(func) + table.insert(self.func2,func) +end +function audio:onStop(func) + table.insert(self.func,func) +end +--[[ +Object:type |Done +Object:typeOf |Done +Source:clone |Done +Source:getAttenuationDistances |Done +Source:getChannels |Done +Source:getCone |Done +Source:getDirection |Done +Source:getPitch |Done +Source:getPosition |Done +Source:getRolloff |Done +Source:getVelocity |Done +Source:getVolume |Done +Source:getVolumeLimits |Done +Source:isLooping |Done +Source:isPaused |Done +Source:isPlaying |Done +Source:isRelative |Done +Source:isStatic |Done +Source:isStopped |Done +Source:pause |Done +Source:play |Done +Source:resume |Done +Source:rewind |Done +Source:seek |Done +Source:setAttenuationDistances |Done +Source:setCone |Done +Source:setDirection |Done +Source:setLooping |Done +Source:setPitch |Done +Source:setPosition |Done +Source:setRelative |Done +Source:setRolloff |Done +Source:setVelocity |Done +Source:setVolume |Done +Source:setVolumeLimits |Done +Source:stop |Done +Source:tell |Done +]] diff --git a/Libs/Library.lua b/Libs/Library.lua new file mode 100644 index 0000000..dc4ac82 --- /dev/null +++ b/Libs/Library.lua @@ -0,0 +1,319 @@ +if table.unpack then + unpack=table.unpack +end +function table.val_to_str ( v ) + if "string" == type( v ) then + v = string.gsub( v, "\n", "\\n" ) + if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then + return "'" .. v .. "'" + end + return '"' .. string.gsub(v,'"', '\\"' ) .. '"' + else + return "table" == type( v ) and table.tostring( v ) or + tostring( v ) + end +end + +function table.key_to_str ( k ) + if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then + return k + else + return "[" .. table.val_to_str( k ) .. "]" + end +end + +function table.tostring( tbl ) + local result, done = {}, {} + for k, v in ipairs( tbl ) do + table.insert( result, table.val_to_str( v ) ) + done[ k ] = true + end + for k, v in pairs( tbl ) do + if not done[ k ] then + table.insert( result, + table.key_to_str( k ) .. "=" .. table.val_to_str( v ) ) + end + end + return "{" .. table.concat( result, "," ) .. "}" +end +function table.merge(t1, t2) + t1,t2= t1 or {},t2 or {} + for k,v in pairs(t2) do + if type(v) == "table" then + if type(t1[k] or false) == "table" then + table.merge(t1[k] or {}, t2[k] or {}) + else + t1[k] = v + end + else + t1[k] = v + end + end + return t1 +end +Library={} +function Library.optimize(func) + local test=Library.convert(func) + rawset(test,"link",{}) + rawset(test,"last","") + rawset(test,"org",func) + test:inject(function(...) + rawset(test,"last",table.tostring({...})) + if test.link[test.last]~=nil then + return Library.forceReturn(unpack(test.link[test.last])) + end + return {...} + end,1) + test:inject(function(...) + test.link[test.last]={test.org(...)} + return test.org(...) + end) + return test +end +function Library.forceReturn(...) + return {[0]="\1\7\6\3\2\99\125",...} +end +function Library.inject(lib,dat,arg) + if type(lib)=="table" then + if type(dat)=="table" then + table.merge(lib,dat) + elseif type(dat)=="string" then + if lib.Version and dat:match("(%d-)%.(%d-)%.(%d-)") then + lib.Version={dat:match("(%d+)%.(%d+)%.(%d+)")} + elseif dat=="meta" and type(arg)=="table" then + local _mt=getmetatable(lib) or {} + local mt={} + table.merge(mt,arg) + table.merge(_mt,mt) + setmetatable(lib,_mt) + elseif dat=="compat" then + lib["getVersion"]=function(self) return self.Version[1].."."..self.Version[2].."."..self.Version[3] end + if not lib.Version then + lib.Version={1,0,0} + end + elseif dat=="inhert" then + if not(lib["!%"..arg.."%!"]) then print("Wrong Password!!") return end + lib["!%"..arg.."%!"].__index=lib["!!%"..arg.."%!!"] + end + elseif type(dat)=="function" then + for i,v in pairs(lib) do + dat(lib,i,v) + end + end + elseif type(lib)=="function" or type(lib)=="userdata" then + if lib==unpack then + print("function unpack cannot yet be injected!") + return unpack + elseif lib==pairs then + print("function pairs cannot yet be injected!") + return lib + elseif lib==ipairs then + print("function ipairs cannot yet be injected!") + return lib + elseif lib==type then + print("function type cannot yet be injected!") + return lib + end + temp={} + local mt={ + __call=function(t,...) + local consume,MainRet,init={},{},{...} + local tt={} + for i=1,#t.__Link do + tt={} + if t.__Link[i]==t.__Main then + if #consume~=0 then + MainRet={t.__Link[i](unpack(consume))} + else + MainRet={t.__Link[i](unpack(init))} + end + else + if i==1 then + consume=(t.__Link[i](unpack(init))) + else + if type(MainRet)=="table" then + table.merge(tt,MainRet) + end + if type(consume)=="table" then + table.merge(tt,consume) + end + consume={t.__Link[i](unpack(tt))} + end + if i==#t.__Link then + return unpack(consume) + end + if consume then if consume[0]=="\1\7\6\3\2\99\125" then consume[0]=nil return unpack(consume) end end + end + end + if type(MainRet)=="table" then + table.merge(tt,MainRet) + end + if type(consume)=="table" then + table.merge(tt,consume) + end + return unpack(tt) + end, + } + temp.__Link={lib} + temp.__Main=lib + temp.__self=temp + function temp:inject(func,i) + if i then + table.insert(self.__Link,i,func) + else + table.insert(self.__Link,func) + end + end + function temp:consume(func) + for i=1,#self.__Link do + if self.__Link[i]==self.__Main then + self.__Link[i]=func + self.__self.__Main=func + return true + end + end + return false + end + setmetatable(temp,mt) + Library.protect(temp,"lolz") + return temp + else + return "arg1 must be a table or a function" + end +end +function Library.parse(lib) + for i,v in pairs(lib) do + print(i,v) + end +end +function Library.protect(lib,pass) + pass=pass or "*" + local mt={} + local test={ + __index = lib, + __newindex = function(tab, key, value) + local t,b=key:find(tab["!%"..pass.."%!"].__pass,1,true) + if t then + local _k=key:sub(b+1) + rawset(tab,_k,value) + else + error("Cannot alter a protected library!") + end + end, + __metatable = false, + __pass=pass or "*" + } + local _mt=getmetatable(lib) or {} + table.merge(mt,_mt) + table.merge(mt,test) + lib["!%"..pass.."%!"]=test + lib["!!%"..pass.."%!!"]=lib + local temp=setmetatable({},mt) + for i,v in pairs(_G) do + if v==lib then + _G[i]=temp + Library(function(link) + link[i]=v + end) + end + end +end +function Library.unprotect(lib,pass) + if not(lib["!%"..pass.."%!"]) then print("Wrong Password or Library is not Protected!") return end + if lib["!%"..pass.."%!"].__pass==pass then + lib["!%"..pass.."%!"].__newindex=lib["!!%"..pass.."%!!"] + lib["!%"..pass.."%!"].__index=nil + lib["!%"..pass.."%!"].__newindex=nil + lib["!%"..pass.."%!"].__metatable = true + setmetatable(lib["!!%"..pass.."%!!"],lib["!%"..pass.."%!"]) + for i,v in pairs(_G) do + if v==lib then + _G[i]=lib["!!%"..pass.."%!!"] + end + end + lib["!!%"..pass.."%!!"]["!%"..pass.."%!"]=nil + lib["!!%"..pass.."%!!"]["!!%"..pass.."%!!"]=nil + else + print("Wrong Password!!!") + end +end +function Library.addPoll(lib,polldata,ref) + lib.__polldata={} + Library.inject(lib.__polldata,polldata) + if type(ref)=="table" then + Library.inject(ref,"meta",{__newindex=function(t,k,v) + t[k].__polldata=polldata + end}) + end +end +function Library.newPollData(t) + local temp={} + temp.__onPolled=function() end + temp.__pollData=false + temp.__advDisc="" + temp.__pollcalls=-1 -- infinte + for i,v in pairs(t) do + if type(v)=="string" then + temp.__advDisc=v + elseif type(v)=="number" then + temp.__pollcalls=v + elseif type(v)=="table" then + temp[v[1]]=v[2] + elseif type(v)=="function" then + temp.__onPolled=v + elseif type(v)=="boolean" then + temp.__pollData=v + else + temp.__userdata=v + end + end + return temp +end +function Library.convert(...) + local temp,rets={...},{} + for i=1,#temp do + if type(temp[i])=="function" then + table.insert(rets,Library.inject(temp[i])) + else + error("Takes only functions and returns in order from functions given. arg # "..i.." is not a function!!! It is a "..type(temp[i])) + end + end + return unpack(rets) +end +function Library.convertIn(...) + local temp,list={...},{} + for i=1,#temp do + if type(temp[i])=="table" then + for k,v in pairs(temp[i]) do + if type(v)=="function" then + temp[i][k]=Library.inject(temp[i][k]) + end + end + else + error("Takes only tables! Arg "..i.." isn't it is a "..type(temp[i])) + end + end +end +function Library.newInjectedFunction() + return Library.convert(function(...) return unpack{...} end) +end +function Library.capulate(lib) + Library.inject(lib,"meta",{ + __index=function(t,k,v) + for i,_v in pairs(t) do + if k:lower()==i:lower() then + return t[i] + end + end + end, + __newindex=function(t,k,v) + rawset(t,k:lower(),v) + end + }) +end +local link={MainLibrary=Library} +Library.inject(Library,"meta",{ + __Link=link, + __call=function(self,func) func(link) end, +}) +--Library.protect(Library,"N@#P!KLkk1(93320") diff --git a/Libs/MultiManager.lua b/Libs/MultiManager.lua new file mode 100644 index 0000000..0da8be3 --- /dev/null +++ b/Libs/MultiManager.lua @@ -0,0 +1,1391 @@ +if not bin then + print('Warning the \'bin\' library wasn\'t required! multi:tofile(path) and the multi:fromfile(path,int) features will not work!') +end +if table.unpack then + unpack=table.unpack +end +function table.merge(t1, t2) + for k,v in pairs(t2) do + if type(v) == 'table' then + if type(t1[k] or false) == 'table' then + table.merge(t1[k] or {}, t2[k] or {}) + else + t1[k] = v + end + else + t1[k] = v + end + end + return t1 +end +multi = {} +multi.Version={'A',0,1}-- History: EventManager,EventManager+,MultiManager <-- Current After 6.3.0 Versioning scheme was altered. A.0.0 +multi.help=[[ +For a list of features do print(multi.Features) +For a list of changes do print(multi.changelog) +For current version do print(multi.Version) +For current stage do print(multi.stage) +For help do print(multi.help) :D +]] +multi.stage='stable' +multi.Features='Current Version: '..multi.Version[1]..'.'..multi.Version[2]..'.'..multi.Version[3]..' '..multi.stage..[[ +MultiManager has 19 Objects: # indicates most commonly used 1-19 1 being the most used by me ++Events #7 ++Alarms #2 ++Loops #3 ++Steps #4 ++TSteps #6 ++Triggers #16 ++Tasks #12 ++Connections #1 -- This is a rather new feature of this library, but has become the most useful for async handling. Knowing this is already 50% of this library ++Timers #14 -- this was tricky because these make up both Alarms and TSteps, but in purly using this standalone is almost non existent ++Jobs #11 ++Process #10 ++Conditions #15 ++Ranges #8 ++Threads #13 ++Functions #5 ++Queuers #17 ++Updaters #9 ++Watchers #18 ++CustomObjects #19 + +Constructors [Runners] +---------------------- Note: multi is the main Processor Obj It cannot be paused or destroyed (kinda) +intObj=multi:newProcess([string: FILE defualt: nil]) +intObj=multi:newQueuer([string: FILE defualt: nil]) + +Constructors [ACTORS] +--------------------- Note: everything is a multiObj! +eventObj=multi:newEvent([function: TASK defualt: function() end]) +alarmObj=multi:newAlarm([number: SET defualt: 0]) +loopObj=multi:newLoop([function: FUNC]) +stepObj=multi:newStep([number: START defualt: 0],[number: RESET defualt: inf],[number: COUNT defualt: 1],[number: SKIP defualt: 0]) +tstepObj=multi:newTStep([number: START defualt: 0],[number: RESET defualt: inf],[number: COUNT defualt: 1],[number: SET defualt: 1]) +updaterObj=multi:newUpdater([number: SKIP defualt: 0]) +watcherObj=multi:newWatcher(table: NAMESPACE,string: NAME) +multiObj=multi:newCustomObject([table: OBJREF],[string: T='process']) +void=multi:newThread(string: name,function: func) + +Constructors [Semi-ACTORS] +-------------------------- +multi:newJob(function: func,[string: name]) +multi:newRange(number: a,number: b,[number: c]) +multi:newCondition(func) + +Constructors [NON-ACTORS] +------------------------- +multi:newTrigger(function: func) +multi:newTask(function: func) +multi:newConnection() +multi:newTimer() +multi:newFunction(function: func) +]] +multi.changelog=[[Changelog starts at Version A.0.0 +New in A.0.0 + Nothing really however a changelog will now be recorded! Feel free to remove this extra strings if space is a requriment + version.major.minor +New in A.1.0 + Changed: multi:newConnection(protect) method + Changed the way you are able to interact with it by adding the __call metamethod + Old usage: + + OnUpdate=multi:newConnection() + OnUpdate:connect(function(...) + print("Updating",...) + end) + OnUpdate:Fire(1,2,3) + + New usage: notice that connect is no longer needed! Both ways still work! and always will work :) + + OnUpdate=multi:newConnection() + OnUpdate(function(...) + print("Updating",...) + end) + OnUpdate:Fire(1,2,3) +]] +multi.__index = multi +multi.Mainloop={} +multi.Tasks={} +multi.Tasks2={} +multi.Garbage={} +multi.ender={} +multi.Children={} +multi.Paused={} +multi.Active=true +multi.fps=60 +multi.Id=-1 +multi.Type='mainprocess' +multi.Rest=0 +multi._type=type +multi.Jobs={} +multi.queue={} +multi.jobUS=2 +multi.clock=os.clock +multi.time=os.time +multi.LinkedPath=multi +multi.queuefinal=function(self) + self:Destroy() + if self.Parent.Mainloop[#self.Parent.Mainloop] then + self.Parent.Mainloop[#self.Parent.Mainloop]:Resume() + else + for i=1,#self.Parent.funcE do + self.Parent.funcE[i](self) + end + self.Parent:Remove() + end +end +--Do not change these ever...Any other number will not work (Unless you are using enablePriority2() then change can be made. Just ensure that Priority_Idle is the greatest and Priority_Core is 1!) +multi.Priority_Core=1 +multi.Priority_High=4 +multi.Priority_Above_Normal=16 +multi.Priority_Normal=64 +multi.Priority_Below_Normal=256 +multi.Priority_Low=1024 +multi.Priority_Idle=4096 +multi.PList={multi.Priority_Core,multi.Priority_High,multi.Priority_Above_Normal,multi.Priority_Normal,multi.Priority_Below_Normal,multi.Priority_Low,multi.Priority_Idle} +multi.PStep=1 +--^^^^ +multi.PriorityTick=1 -- Between 1 and 4 any greater and problems arise +multi.Priority=multi.Priority_Core +function multi:setDomainName(name) + self[name]={} +end +function multi:linkDomain(name) + return self[name] +end +function multi:_Pause() + self.Active=false +end +function multi:setPriority(s) + if type(s)==number then + self.Priority=s + elseif type(s)=='string' then + if s:lower()=='core' or s:lower()=='c' then + self.Priority=self.Priority_Core + elseif s:lower()=='high' or s:lower()=='h' then + self.Priority=self.Priority_High + elseif s:lower()=='above' or s:lower()=='an' then + self.Priority=self.Priority_Above_Normal + elseif s:lower()=='normal' or s:lower()=='n' then + self.Priority=self.Priority_Normal + elseif s:lower()=='below' or s:lower()=='bn' then + self.Priority=self.Priority_Below_Normal + elseif s:lower()=='low' or s:lower()=='l' then + self.Priority=self.Priority_Low + elseif s:lower()=='idle' or s:lower()=='i' then + self.Priority=self.Priority_Idle + end + end +end +-- System +function os.getOS() + if package.config:sub(1,1)=='\\' then + return 'windows' + else + return 'unix' + end +end +if os.getOS()=='windows' then + function os.sleep(n) + if n > 0 then os.execute('ping -n ' .. tonumber(n+1) .. ' localhost > NUL') end + end +else + function os.sleep(n) + os.execute('sleep ' .. tonumber(n)) + end +end +function multi:getParentProcess() + return self.Mainloop[self.CID] +end +function multi:Stop() + self.Active=false +end +function multi:condition(cond) + if not self.CD then + self:Pause() + self.held=true + self.CD=cond.condition + elseif not(cond.condition()) then + self.held=false + self:Resume() + self.CD=nil + return false + end + self.Parent:Do_Order() + return true +end +function multi:isHeld() + return self.held +end +function multi.executeFunction(name,...) + if type(_G[name])=='function' then + _G[name](...) + else + print('Error: Not a function') + end +end +function multi:waitFor(obj) + self:hold(function() return obj:isActive() end) +end +function multi:reboot(r) + local before=collectgarbage('count') + self.Mainloop={} + self.Tasks={} + self.Tasks2={} + self.Garbage={} + self.Children={} + self.Paused={} + self.Active=true + self.Id=-1 + if r then + for i,v in pairs(_G) do + if type(i)=='table' then + if i.Parent and i.Id and i.Act then + i={} + end + end + end + end + collectgarbage() + local after=collectgarbage('count') + print([[Before rebooting total Ram used was ]]..before..[[Kb +After rebooting total Ram used is ]]..after..[[ Kb +A total of ]]..(before-after)..[[Kb was cleaned up]]) +end +function multi:getChildren() + return self.Mainloop +end +--Processor +function multi:getError() + if self.error then + return self.error + end +end +function multi:Do_Order() + local Loop=self.Mainloop + _G.ID=0 + for _D=#Loop,1,-1 do + if Loop[_D] then + if Loop[_D].Active then + Loop[_D].Id=_D + self.CID=_D + Loop[_D]:Act() + end + end + end +end +function multi:enablePriority() + function self:Do_Order() + local Loop=self.Mainloop + _G.ID=0 + local PS=self + for _D=#Loop,1,-1 do + if Loop[_D] then + if (PS.PList[PS.PStep])%Loop[_D].Priority==0 then + if Loop[_D].Active then + Loop[_D].Id=_D + self.CID=_D + Loop[_D]:Act() + end + end + end + end + PS.PStep=PS.PStep+1 + if PS.PStep>7 then + PS.PStep=1 + end + end +end +function multi:enablePriority2() + function self:Do_Order() + local Loop=self.Mainloop + _G.ID=0 + local PS=self + for _D=#Loop,1,-1 do + if Loop[_D] then + if (PS.PStep)%Loop[_D].Priority==0 then + if Loop[_D].Active then + Loop[_D].Id=_D + self.CID=_D + Loop[_D]:Act() + end + end + end + end + PS.PStep=PS.PStep+1 + if PS.PStep>self.Priority_Idle then + PS.PStep=1 + end + end +end +multi.disablePriority=multi.unProtect +function multi:fromfile(path,int) + int=int or self + local test2={} + local test=bin.load(path) + local tp=test:getBlock('s') + if tp=='event' then + test2=int:newEvent(test:getBlock('f')) + local t=test:getBlock('t') + for i=1,#t do + test2:OnEvent(t[i]) + end + elseif tp=='alarm' then + test2=int:newAlarm(test:getBlock('n')) + elseif tp=='loop' then + test2=int:newLoop(test:getBlock('t')[1]) + elseif tp=='step' or tp=='tstep' then + local func=test:getBlock('t') + local funcE=test:getBlock('t') + local funcS=test:getBlock('t') + local tab=test:getBlock('t') + test2=int:newStep() + table.merge(test2,tab) + test2.funcE=funcE + test2.funcS=funcS + test2.func=func + elseif tp=='trigger' then + test2=int:newTrigger(test:getBlock('f')) + elseif tp=='connector' then + test2=int:newConnection() + test2.func=test:getBlock('t') + elseif tp=='timer' then + test2=int:newTimer() + test2.count=tonumber(test:getBlock('n')) + else + print('Error: The file you selected is not a valid multi file object!') + return false + end + return test2 +end +function multi:benchMark(sec,p) + local temp=self:newLoop(function(t,self) + if self.clock()-self.init>self.sec then + print(self.c..' steps in '..self.sec..' second(s)') + self.tt(self.sec) + self:Destroy() + else + self.c=self.c+1 + end + end) + temp.Priority=p or 1 + function temp:OnBench(func) + self.tt=func + end + self.tt=function() end + temp.sec=sec + temp.init=self.clock() + temp.c=0 + return temp +end +function multi:tofile(path) + local items=self:getChildren() + io.mkDir(io.getName(path)) + for i=1,#items do + items[i]:tofile(io.getName(path)..'\\item'..item[i]..'.dat') + end + local int=bin.new() + int:addBlock('process') + int:addBlock(io.getName(path)) + int:addBlock(#self.Mainloop) + int:addBlock(self.Active) + int:addBlock(self.Rest) + int:addBlock(self.Jobs) + int:tofile() +end +function multi.startFPSMonitior() + if not multi.runFPS then + multi.doFPS(s) + multi.runFPS=true + end +end +function multi.doFPS(s) + multi:benchMark(1):OnBench(doFPS) + if s then + multi.fps=s + end +end +--Helpers +function multi:OnMainConnect(func) + table.insert(self.func,func) +end +function multi:protect() + function self:Do_Order() + local Loop=self.Mainloop + for _D=#Loop,1,-1 do + if Loop[_D]~=nil then + Loop[_D].Id=_D + self.CID=_D + local status, err=pcall(Loop[_D].Act,Loop[_D]) + if err and not(Loop[_D].error) then + Loop[_D].error=err + self.OnError:Fire(err,Loop[_D]) + end + end + end + end +end +function multi:unProtect() + local Loop=self.Mainloop + _G.ID=0 + for _D=#Loop,1,-1 do + if Loop[_D] then + if Loop[_D].Active then + Loop[_D].Id=_D + self.CID=_D + Loop[_D]:Act() + end + end + end +end +function multi:setJobSpeed(n) + self.jobUS=n +end +function multi:hasJobs() + return #self.Jobs>0,#self.Jobs +end +function multi:getJobs() + return #self.Jobs +end +function multi:removeJob(name) + for i=#self.Jobs,1,-1 do + if self.Jobs[i][2]==name then + table.remove(self.Jobs,i) + end + end +end +function multi:FreeMainEvent() + self.func={} +end +function multi:connectFinal(func) + if self.Type=='event' then + self:OnEvent(func) + elseif self.Type=='alarm' then + self:OnRing(func) + elseif self.Type=='step' or self.Type=='tstep' then + self:OnEnd(func) + else + print("Warning!!! "..self.Type.." doesn't contain a Final Connection State! Use "..self.Type..":Break(function) to trigger it's final event!") + self:OnBreak(func) + end +end +function multi:Break() + self:Pause() + self.Active=nil + for i=1,#self.ender do + if self.ender[i] then + self.ender[i](self) + end + end +end +function multi:OnBreak(func) + table.insert(self.ender,func) +end +function multi:isPaused() + return not(self.Active) +end +function multi:isActive() + return self.Active +end +function multi:getType() + return self.Type +end +function multi:Sleep(n) + self:hold(n) +end +function multi:Pause() + if self.Type=='mainprocess' then + print("You cannot pause the main process. Doing so will stop all methods and freeze your program! However if you still want to use multi:_Pause()") + else + self.Active=false + if self.Parent.Mainloop[self.Id]~=nil then + table.remove(self.Parent.Mainloop,self.Id) + table.insert(self.Parent.Paused,self) + self.PId=#self.Parent.Paused + end + end +end +function multi:Resume() + if self.Type=='process' or self.Type=='mainprocess' then + self.Active=true + local c=self:getChildren() + for i=1,#c do + c[i]:Resume() + end + else + if self:isPaused() then + table.remove(self.Parent.Paused,self.PId) + table.insert(self.Parent.Mainloop,self) + self.Id=#self.Parent.Mainloop + self.Active=true + end + end +end +function multi:resurrect() + table.insert(self.Parent.Mainloop,self) + self.Active=true +end +function multi:Destroy() + if self.Type=='process' or self.Type=='mainprocess' then + local c=self:getChildren() + for i=1,#c do + self.OnObjectDestroyed:Fire(c[i]) + c[i]:Destroy() + end + else + for i=1,#self.Parent.Mainloop do + if self.Parent.Mainloop[i]==self then + self.Parent.OnObjectDestroyed:Fire(self) + table.remove(self.Parent.Mainloop,i) + break + end + end + self.Active=false + end +end + +function multi:hold(task) + self:Pause() + self.held=true + if type(task)=='number' then + local alarm=self.Parent:newAlarm(task) + while alarm.Active==true do + if love then + self.Parent:lManager() + else + self.Parent:Do_Order() + end + end + alarm:Destroy() + self:Resume() + self.held=false + elseif type(task)=='function' then + local env=self.Parent:newEvent(task) + env:OnEvent(function(envt) envt:Pause() envt.Active=false end) + while env.Active do + if love then + self.Parent:lManager() + else + self.Parent:Do_Order() + end + end + env:Destroy() + self:Resume() + self.held=false + else + print('Error Data Type!!!') + end +end +function multi:oneTime(func,...) + if not(self.Type=='mainprocess' or self.Type=='process') then + for _k=1,#self.Parent.Tasks2 do + if self.Parent.Tasks2[_k]==func then + return false + end + end + table.insert(self.Parent.Tasks2,func) + func(...) + return true + else + for _k=1,#self.Tasks2 do + if self.Tasks2[_k]==func then + return false + end + end + table.insert(self.Tasks2,func) + func(...) + return true + end +end +function multi:Reset(n) + self:Resume() +end +function multi:isDone() + return self.Active~=true +end +function multi:create(ref) + multi.OnObjectCreated:Fire(ref) +end +--Constructors [CORE] +function multi:newBase(ins) + if not(self.Type=='mainprocess' or self.Type=='process' or self.Type=='queue') then error('Can only create an object on multi or an interface obj') return false end + local c = {} + if self.Type=='process' or self.Type=='queue' then + setmetatable(c, self.Parent) + else + setmetatable(c, self) + end + c.Active=true + c.func={} + c.ender={} + c.Id=0 + c.PId=0 + c.Act=function() end + c.Parent=self + c.held=false + if ins then + table.insert(self.Mainloop,ins,c) + else + table.insert(self.Mainloop,c) + end + return c +end +function multi:newProcess(file) + if not(self.Type=='mainprocess') then error('Can only create an interface on the multi obj') return false end + local c = {} + setmetatable(c, self) + c.Parent=self + c.Active=true + c.func={} + c.Id=0 + c.Type='process' + c.Mainloop={} + c.Tasks={} + c.Tasks2={} + c.Garbage={} + c.Children={} + c.Paused={} + c.Active=true + c.Id=-1 + c.Rest=0 + c.Jobs={} + c.queue={} + c.jobUS=2 + function c:Start() + if self.l then + self.l:Resume() + else + self.l=self.Parent:newLoop(function(dt) c:uManager(dt) end) + end + end + function c:Pause() + if self.l then + self.l:Pause() + end + end + function c:Remove() + self:Destroy() + self.l:Destroy() + end + if file then + self.Cself=c + loadstring('local interface=multi.Cself '..io.open(file,'rb'):read('*all'))() + end + self:create(c) + return c +end +function multi:newQueuer(file) + local c=self:newProcess() + c.Type='queue' + c.last={} + c.funcE={} + function c:OnQueueCompleted(func) + table.insert(self.funcE,func) + end + if file then + self.Cself=c + loadstring('local queue=multi.Cself '..io.open(file,'rb'):read('*all'))() + end + self:create(c) + return c +end +--Constructors [ACTORS] +function multi:newCustomObject(objRef,t) + local c={} + if t=='process' then + if self.Type=='queue' then + c=self:newBase(1) + self.last=c + print("This Custom Object was created on a queue! Ensure that it has a way to end! All objects have a obj:Break() method!") + else + c=self:newBase() + end + if type(objRef)=='table' then + table.merge(c,objRef) + end + if not c.Act then + function c:Act() + -- Empty function + end + end + else + c=objRef or {} + end + if not c.Type then + c.Type='coustomObject' + end + if self.Type=='queue' then + if #self.Mainloop>1 then + c:Pause() + end + c:connectFinal(multi.queuefinal) + end + self:create(c) + return c +end +function multi:newEvent(task) + local c={} + if self.Type=='queue' then + c=self:newBase(1) + self.last=c + else + c=self:newBase() + end + c.Type='event' + c.Task=task or function() end + function c:Act() + if self.Task(self) then + self:Pause() + for _E=1,#self.func do + self.func[_E](self) + end + end + end + function c:OnEvent(func) + table.insert(self.func,func) + end + function c:tofile(path) + local m=bin.new() + m:addBlock(self.Type) + m:addBlock(self.Task) + m:addBlock(self.func) + m:addBlock(self.Active) + m:tofile(path) + end + if self.Type=='queue' then + if #self.Mainloop>1 then + c:Pause() + end + c:connectFinal(multi.queuefinal) + end + self:create(c) + return c +end +function multi:newAlarm(set) + local c={} + if self.Type=='queue' then + c=self:newBase(1) + self.last=c + else + c=self:newBase() + end + c.Type='alarm' + c.Priority=self.Priority_Low + c.timer=self:newTimer() + c.set=set or 0 + function c:tofile(path) + local m=bin.new() + m:addBlock(self.Type) + m:addBlock(self.set) + m:addBlock(self.Active) + m:tofile(path) + end + function c:Act() + if self.timer:Get()>=self.set then + self:Pause() + self.Active=false + for i=1,#self.func do + self.func[i](self) + end + end + end + function c:Resume() + self.Parent.Resume(self) + self.timer:Resume() + end + function c:Reset(n) + if n then self.set=n end + self:Resume() + self.timer:Reset() + end + function c:OnRing(func) + table.insert(self.func,func) + end + function c:Pause() + self.timer:Pause() + self.Parent.Pause(self) + end + if self.Type=='queue' then + c:Pause() + c:connectFinal(multi.queuefinal) + else + c.timer:Start() + end + self:create(c) + return c +end +function multi:newLoop(func) + local c={} + if self.Type=='queue' then + c=self:newBase(1) + self.last=c + else + c=self:newBase() + end + c.Type='loop' + c.Start=self.clock() + if func then + c.func={func} + end + function c:tofile(path) + local m=bin.new() + m:addBlock(self.Type) + m:addBlock(self.func) + m:addBlock(self.Active) + m:tofile(path) + end + function c:Act() + for i=1,#self.func do + self.func[i](self.Parent.clock()-self.Start,self) + end + end + function c:OnLoop(func) + table.insert(self.func,func) + end + if self.Type=='queue' then + if #self.Mainloop>1 then + c:Pause() + end + c:connectFinal(multi.queuefinal) + end + self:create(c) + return c +end +function multi:newUpdater(skip) + local c={} + if self.Type=='queue' then + c=self:newBase(1) + self.last=c + else + c=self:newBase() + end + c.Type='updater' + c.pos=1 + c.skip=skip or 1 + function c:Act() + if self.pos>=self.skip then + self.pos=0 + for i=1,#self.func do + self.func[i](self) + end + end + self.pos=self.pos+1 + end + function c:setSkip(n) + self.skip=n + end + c.OnUpdate=self.OnMainConnect + if self.Type=='queue' then + if #self.Mainloop>1 then + c:Pause() + end + c:connectFinal(multi.queuefinal) + end + self:create(c) + return c +end +function multi:newStep(start,reset,count,skip) + local c={} + if self.Type=='queue' then + c=self:newBase(1) + self.last=c + else + c=self:newBase() + end + think=1 + c.Type='step' + c.pos=start or 1 + c.endAt=reset or math.huge + c.skip=skip or 0 + c.spos=0 + c.count=count or 1*think + c.funcE={} + c.funcS={} + c.start=start or 1 + if start~=nil and reset~=nil then + if start>reset then + think=-1 + end + end + function c:tofile(path) + local m=bin.new() + m:addBlock(self.Type) + m:addBlock(self.func) + m:addBlock(self.funcE) + m:addBlock(self.funcS) + m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,spos=self.spos,count=self.count,start=self.start}) + m:addBlock(self.Active) + m:tofile(path) + end + function c:Act() + if self~=nil then + if self.spos==0 then + if self.pos==self.start then + for fe=1,#self.funcS do + self.funcS[fe](self) + end + end + for i=1,#self.func do + self.func[i](self.pos,self) + end + self.pos=self.pos+self.count + if self.pos-self.count==self.endAt then + self:Pause() + for fe=1,#self.funcE do + self.funcE[fe](self) + end + self.pos=self.start + end + end + end + self.spos=self.spos+1 + if self.spos>=self.skip then + self.spos=0 + end + end + c.Reset=c.Resume + function c:OnStart(func) + table.insert(self.funcS,func) + end + function c:OnStep(func) + table.insert(self.func,1,func) + end + function c:OnEnd(func) + table.insert(self.funcE,func) + end + function c:Break() + self.Active=nil + end + function c:Update(start,reset,count,skip) + self.start=start or self.start + self.endAt=reset or self.endAt + self.skip=skip or self.skip + self.count=count or self.count + self:Resume() + end + if self.Type=='queue' then + if #self.Mainloop>1 then + c:Pause() + end + c:connectFinal(multi.queuefinal) + end + self:create(c) + return c +end +function multi:newTStep(start,reset,count,set) + local c={} + if self.Type=='queue' then + c=self:newBase(1) + self.last=c + else + c=self:newBase() + end + think=1 + c.Type='tstep' + c.Priority=self.Priority_Low + c.start=start or 1 + local reset = reset or math.huge + c.endAt=reset + c.pos=start or 1 + c.skip=skip or 0 + c.count=count or 1*think + c.funcE={} + c.timer=self.clock() + c.set=set or 1 + c.funcS={} + function c:Update(start,reset,count,set) + self.start=start or self.start + self.pos=self.start + self.endAt=reset or self.endAt + self.set=set or self.set + self.count=count or self.count or 1 + self.timer=self.clock() + self:Resume() + end + function c:tofile(path) + local m=bin.new() + m:addBlock(self.Type) + m:addBlock(self.func) + m:addBlock(self.funcE) + m:addBlock(self.funcS) + m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,timer=self.timer,count=self.count,start=self.start,set=self.set}) + m:addBlock(self.Active) + m:tofile(path) + end + function c:Act() + if self.clock()-self.timer>=self.set then + self:Reset() + if self.pos==self.start then + for fe=1,#self.funcS do + self.funcS[fe](self) + end + end + for i=1,#self.func do + self.func[i](self.pos,self) + end + self.pos=self.pos+self.count + if self.pos-self.count==self.endAt then + self:Pause() + for fe=1,#self.funcE do + self.funcE[fe](self) + end + self.pos=self.start + end + end + end + function c:OnStart(func) + table.insert(self.funcS,func) + end + function c:OnStep(func) + table.insert(self.func,func) + end + function c:OnEnd(func) + table.insert(self.funcE,func) + end + function c:Break() + self.Active=nil + end + function c:Reset(n) + if n then self.set=n end + self.timer=self.clock() + self:Resume() + end + if self.Type=='queue' then + if #self.Mainloop>1 then + c:Pause() + end + c:connectFinal(multi.queuefinal) + end + self:create(c) + return c +end +function multi:newWatcher(namespace,name) + local function WatcherObj(ns,n) + if self.Type=='queue' then + print("Cannot create a watcher on a queue! Creating on 'multi' instead!") + self=multi + end + local c=self:newBase() + c.Type='watcher' + c.ns=ns + c.n=n + c.cv=ns[n] + function c:OnValueChanged(func) + table.insert(self.func,func) + end + function c:Act() + if self.cv~=self.ns[self.n] then + for i=1,#self.func do + self.func[i](self,self.cv,self.ns[self.n]) + end + self.cv=self.ns[self.n] + end + end + self:create(c) + return c + end + if type(namespace)~='table' and type(namespace)=='string' then + return WatcherObj(_G,namespace) + elseif type(namespace)=='table' and (type(name)=='string' or 'number') then + return WatcherObj(namespace,name) + else + print('Warning, invalid arguments! Nothing returned!') + end +end +function multi:newThread(name,func) + local c={} + c.ref={} + c.Name=name + c.thread=coroutine.create(func) + c.sleep=1 + c.firstRunDone=false + c.timer=multi.scheduler:newTimer() + c.ref.Globals=self:linkDomain("Globals") + function c.ref:send(name,val) + ret=coroutine.yield({Name=name,Value=val}) + self:syncGlobals(ret) + end + function c.ref:get(name) + return self.Globals[name] + end + function c.ref:kill() + err=coroutine.yield({"_kill_"}) + if err then + error("Failed to kill a thread! Exiting...") + end + end + function c.ref:sleep(n) + n = tonumber(n) or 0 + ret=coroutine.yield({"_sleep_",n}) + self:syncGlobals(ret) + end + function c.ref:syncGlobals(v) + self.Globals=v + end + table.insert(self:linkDomain("Threads"),c) + if not multi.scheduler:isActive() then + multi.scheduler:Resume() + end +end +-- Constructors [SEMI-ACTORS] +function multi:newJob(func,name) + if not(self.Type=='mainprocess' or self.Type=='process') then error('Can only create an object on multi or an interface obj') return false end + local c = {} + if self.Type=='process' then + setmetatable(c, self.Parent) + else + setmetatable(c, self) + end + c.Active=true + c.func={} + c.Id=0 + c.PId=0 + c.Parent=self + c.Type='job' + c.trigfunc=func or function() end + function c:Act() + self:trigfunc(self) + end + table.insert(self.Jobs,{c,name}) + if self.JobRunner==nil then + self.JobRunner=self:newAlarm(self.jobUS) + self.JobRunner:OnRing(function(self) + if #self.Parent.Jobs>0 then + if self.Parent.Jobs[1] then + self.Parent.Jobs[1][1]:Act() + table.remove(self.Parent.Jobs,1) + end + end + self:Reset(self.Parent.jobUS) + end) + end +end +function multi:newRange() + selflink=self + local temp={ + getN = function(self) selflink:Do_Order() self.n=self.n+self.c if self.n>self.b then self.Link.held=false self.Link:Resume() return nil end return self.n end, + } + setmetatable(temp,{ + __call=function(self,a,b,c) + self.c=c or 1 + self.n=a-self.c + self.a=a + self.b=b + self.Link=selflink.Parent.Mainloop[selflink.CID] + self.Link:Pause() + self.Link.held=true + return function() return self:getN() end + end + }) + self:create(temp) + return temp +end +function multi:newCondition(func) + local c={['condition']=func} + self:create(c) + return c +end +-- Constructors [NON-ACTORS] +function multi:newFunction(func) + local c={} + c.func=func + mt={ + __index=multi, + __call=function(self,...) if self.Active then return self:func(...) end local t={...} return "PAUSED" end + } + c.Parent=self + function c:Pause() + self.Active=false + end + function c:Resume() + self.Active=true + end + setmetatable(c,mt) + self:create(c) + return c +end +function multi:newTimer() + local c={} + c.Type='timer' + c.time=0 + c.count=0 + function c:Start() + self.time=os.clock() + end + function c:Get() + return (os.clock()-self.time)+self.count + end + c.Reset=c.Start + function c:Pause() + self.time=self:Get() + end + function c:Resume() + self.time=os.clock()-self.time + end + function c:tofile(path) + local m=bin.new() + self.count=self.count+self:Get() + m:addBlock(self.Type) + m:addBlock(self.count) + m:tofile(path) + end + self:create(c) + return c +end +function multi:newTask(func) + table.insert(self.Tasks,func) +end +function multi:newTrigger(func) + local c={} + c.Type='trigger' + c.trigfunc=func or function() end + function c:Fire(...) + self:trigfunc(self,...) + end + function c:tofile(path) + local m=bin.new() + m:addBlock(self.Type) + m:addBlock(self.trigfunc) + m:tofile(path) + end + self:create(c) + return c +end +function multi:newConnection(protect) + local c={} + setmetatable(c,{__call=function(self,...) self:connect(...) end}) + c.Type='connector' + c.func={} + c.ID=0 + c.protect=protect or true + function c:Fire(...) + local ret={} + for i=#self.func,1,-1 do + if self.protect then + local temp={pcall(self.func[i][1],...)} + if temp[1] then + table.remove(temp,1) + table.insert(ret,temp) + else + print(temp[2]) + end + else + table.insert(ret,{self.func[i][1](...)}) + end + end + return ret + end + function c:bind(t) + self.func=t + end + function c:remove() + self.func={} + end + function c:connect(func) + self.ID=self.ID+1 + table.insert(self.func,1,{func,self.ID}) + return { + Link=self.func, + ID=self.ID, + remove=function(self) + for i=1,#self.Link do + if self.Link[i][2]~=nil then + if self.Link[i][2]==self.ID then + table.remove(self.Link,i) + self.remove=function() end + self.Link=nil + self.ID=nil + return true + end + end + end + end + } + end + function c:tofile(path) + local m=bin.new() + m:addBlock(self.Type) + m:addBlock(self.func) + m:tofile(path) + end + return c +end +multi.OnObjectCreated=multi:newConnection() +multi.OnObjectDestroyed=multi:newConnection() +--Managers +function multi:mainloop() + for i=1,#self.Tasks do + self.Tasks[i](self) + end + rawset(self,'Start',self.clock()) + while self.Active do + self:Do_Order() + end + print("Did you call multi:Stop()? This method should not be used when using multi:mainloop()! You now need to restart the multiManager, by using multi:reboot() and calling multi:mainloop() again or by using multi:uManager()") +end +function multi._tFunc(self,dt) + for i=1,#self.Tasks do + self.Tasks[i](self) + end + if dt then + self.pump=true + end + self.pumpvar=dt + rawset(self,'Start',self.clock()) +end +function multi:uManager(dt) + if self.Active then + self:oneTime(self._tFunc,self,dt) + function self:uManager(dt) + self:Do_Order() + end + self:Do_Order() + end +end +--Thread Setup Stuff +multi:setDomainName("Threads") +multi:setDomainName("Globals") +-- Scheduler +multi.scheduler=multi:newUpdater() +multi.scheduler.Type="scheduler" +function multi.scheduler:setStep(n) + self.skip=tonumber(n) or 24 +end +multi.scheduler.Threads=multi:linkDomain("Threads") +multi.scheduler.Globals=multi:linkDomain("Globals") +multi.scheduler:OnUpdate(function(self) + for i=#self.Threads,1,-1 do + ret={} + if coroutine.status(self.Threads[i].thread)=="dead" then + table.remove(self.Threads,i) + else + if self.Threads[i].timer:Get()>=self.Threads[i].sleep then + if self.Threads[i].firstRunDone==false then + self.Threads[i].firstRunDone=true + self.Threads[i].timer:Start() + _,ret=coroutine.resume(self.Threads[i].thread,self.Threads[i].ref) + else + _,ret=coroutine.resume(self.Threads[i].thread,self.Globals) + end + if ret==true or ret==false then + print("Thread Ended!!!") + ret={} + end + end + if ret then + if ret[1]=="_kill_" then + table.remove(self.Threads,i) + elseif ret[1]=="_sleep_" then + self.Threads[i].timer:Reset() + self.Threads[i].sleep=ret[2] + elseif ret.Name then + self.Globals[ret.Name]=ret.Value + end + end + end + end +end) +multi.scheduler:setStep() +multi.scheduler:Pause() +multi.OnError=multi:newConnection() diff --git a/Libs/README.txt b/Libs/README.txt new file mode 100644 index 0000000..f889ee6 --- /dev/null +++ b/Libs/README.txt @@ -0,0 +1,2 @@ +These libraries Namely the MultiManager, bin, and the Library, libraries will be documented because i plan on sharing them +everything else will mostlikely not be documented \ No newline at end of file diff --git a/Libs/T1.lua b/Libs/T1.lua new file mode 100644 index 0000000..75161a8 --- /dev/null +++ b/Libs/T1.lua @@ -0,0 +1,598 @@ +require("love.timer") +require("love.system") +require("love.sound") +require("love.physics") +require("love.mouse") +require("love.math") +require("love.keyboard") +require("love.joystick") +require("love.image") +require("love.font") +require("love.filesystem") +require("love.event") +require("love.audio") +require("love.graphics") +require("love.window") +_defaultfont = love.graphics.getFont() +gui = {} +function gui.getTile(i,x,y,w,h)-- returns imagedata + if type(i)=="userdata" then + -- do nothing + else + error("getTile invalid args!!! Usage: ImageElement:getTile(x,y,w,h) or gui:getTile(imagedata,x,y,w,h)") + end + local iw,ih=i:getDimensions() + local id,_id=i:getData(),love.image.newImageData(w,h) + for _x=x,w+x-1 do + for _y=y,h+y-1 do + _id:setPixel(_x-x,_y-y,id:getPixel(_x,_y)) + end + end + return love.graphics.newImage(_id) +end +multi = {} +multi.Version="4.0.0" +multi.__index = multi +multi.Mainloop={} +multi.Tasks={} +multi.Tasks2={} +multi.Garbage={} +multi.Children={} +multi.Paused={} +multi.MasterId=0 +multi.Active=true +multi.Id=-1 +multi.Type="MainInt" +multi.Rest=0 +-- System +os.sleep=love.timer.sleep +function multi:newBase(ins) + if not(self.Type=="MainInt" or self.Type=="int") then error("Can only create an object on multi or an interface obj") return false end + local c = {} + if self.Type=="int" then + setmetatable(c, self.Parent) + else + setmetatable(c, self) + end + c.Active=true + c.func={} + c.Id=0 + c.Act=function() end + c.Parent=self + if ins then + table.insert(self.Mainloop,ins,c) + else + table.insert(self.Mainloop,c) + end + self.MasterId=self.MasterId+1 + return c +end +function multi:reboot(r) + self.Mainloop={} + self.Tasks={} + self.Tasks2={} + self.Garbage={} + self.Children={} + self.Paused={} + self.MasterId=0 + self.Active=true + self.Id=-1 + if r then + for i,v in pairs(_G) do + if type(i)=="table" then + if i.Parent and i.Id and i.Act then + i={} + end + end + end + end +end +function multi:getChildren() + return self.Mainloop +end +--Processor +function multi:Do_Order() + for _D=#self.Mainloop,1,-1 do + if self.Mainloop[_D]~=nil then + self.Mainloop[_D].Id=_D + self.Mainloop[_D]:Act() + end + if self.Mainloop[_D].rem then + table.remove(self.Mainloop,_D) + end + end + if self.Rest>0 then + os.sleep(self.Rest) + end +end +function multi:benchMark(sec) + local temp=self:newLoop(function(t,self) + if os.clock()-self.init>self.sec then + print(self.c.." steps in "..self.sec.." second(s)") + self.tt(self.sec) + self:Destroy() + else + self.c=self.c+1 + end + end) + function temp:OnBench(func) + self.tt=func + end + self.tt=function() end + temp.sec=sec + temp.init=os.clock() + temp.c=0 + return temp +end +function multi:newInterface() + if not(self.Type=="MainInt") then error("Can only create an interface on the multi obj") return false end + local c = {} + setmetatable(c, self) + c.Parent=self + c.Active=true + c.func={} + c.Id=0 + c.Type="int" + c.Mainloop={} + c.Tasks={} + c.Tasks2={} + c.Garbage={} + c.Children={} + c.Paused={} + c.MasterId=0 + c.Active=true + c.Id=-1 + c.Rest=0 + function c:Start() + if self.l then + self.l:Resume() + else + self.l=self.Parent:newLoop(function(dt) c:uManager(dt) end) + end + end + function c:Stop() + if self.l then + self.l:Pause() + end + end + function c:Remove() + self:Destroy() + self.l:Destroy() + end + return c +end +--Helpers +function multi:FreeMainEvent() + self.func={} +end +function multi:isPaused() + return not(self.Active) +end +function multi:Pause(n) + if self.Type=="int" or self.Type=="MainInt" then + self.Active=false + if not(n) then + local c=self:getChildren() + for i=1,#c do + c[i]:Pause() + end + else + self:hold(n) + end + else + if not(n) then + self.Active=false + if self.Parent.Mainloop[self.Id]~=nil then + table.remove(self.Parent.Mainloop,self.Id) + table.insert(self.Parent.Paused,self) + self.Id=#self.Parent.Paused + end + else + self:hold(n) + end + end +end +function multi:Resume() + if self.Type=="int" or self.Type=="MainInt" then + self.Active=true + local c=self:getChildren() + for i=1,#c do + c[i]:Resume() + end + else + if self:isPaused() then + self.Active=true + for i=1,#self.Parent.Paused do + if self.Parent.Paused[i]==self then + table.remove(self.Parent.Paused,i) + return + end + end + table.insert(self.Parent.Mainloop,self) + end + end +end +function multi:Destroy() + if self.Type=="int" or self.Type=="MainInt" then + local c=self:getChildren() + for i=1,#c do + c[i]:Destroy() + end + else + self.rem=true + end +end +function multi:hold(task) + self:Pause() + if type(task)=="number" then + local alarm=self:newAlarm(task) + while alarm.Active==true do + if love then + self.Parent.lManager() + else + self.Parent.Do_Order() + end + end + alarm:Destroy() + self:Resume() + elseif type(task)=="function" then + local env=self.Parent:newEvent(task) + env:OnEvent(function(envt) envt:Pause() envt:Stop() end) + while env.Active do + if love then + self.Parent.lManager() + else + self.Parent.Do_Order() + end + end + env:Destroy() + self:Resume() + else + print("Error Data Type!!!") + end +end +function multi:oneTime(func,...) + if not(self.Type=="MainInt" or self.Type=="int") then + for _k=1,#self.Parent.Tasks2 do + if self.Parent.Tasks2[_k]==func then + return false + end + end + table.insert(self.Parent.Tasks2,func) + func(...) + return true + else + for _k=1,#self.Tasks2 do + if self.Tasks2[_k]==func then + return false + end + end + table.insert(self.Tasks2,func) + func(...) + return true + end +end +--Constructors +function multi:newEvent(task) + local c=self:newBase() + c.Type="Event" + c.Task=task or function() end + function c:Act() + if self.Task(self) and self.Active==true then + self:Pause() + for _E=1,#self.func do + self.func[_E](self) + end + end + end + function c:OnEvent(func) + table.insert(self.func,func) + end + return c +end +function multi:newAlarm(set) + local c=self:newBase() + c.Type="Alarm" + c.timer=os.clock() + c.set=set or 0 + function c:Act() + if self.Active==true then + if os.clock()-self.timer>=self.set then + self:Pause() + for i=1,#self.func do + self.func[i](self) + end + end + end + end + function c:Reset(n) + if n then self.set=n end + self.timer=os.clock() + self:Resume() + end + function c:OnRing(func) + table.insert(self.func,func) + end + return c +end +function multi:newTask(func) + table.insert(self.Tasks,func) +end +function multi:newLoop(func) + local c=self:newBase() + c.Type="Loop" + if func then + c.func={func} + end + function c:Act() + if self.Active==true then + for i=1,#self.func do + self.func[i](os.clock()-self.Parent.Start,self) + end + end + end + function c:OnLoop(func) + table.insert(self.func,func) + end + return c +end +function multi:newStep(start,reset,count,skip) + local c=self:newBase() + think=1 + c.Type="Step" + c.pos=start or 1 + c.endAt=reset or math.huge + c.skip=skip or 0 + c.spos=0 + c.count=count or 1*think + c.funcE={} + c.start=start or 1 + if start~=nil and reset~=nil then + if start>reset then + think=-1 + end + end + function c:Act() + if self~=nil then + if self.spos==0 then + if self.Active==true then + for i=1,#self.func do + self.func[i](self.pos,self) + end + self.pos=self.pos+self.count + end + end + end + self.spos=self.spos+1 + if self.spos>=self.skip then + self.spos=0 + end + end + function c:OnStep(func) + table.insert(self.func,1,func) + end + function c:OnEnd(func) + table.insert(self.funcE,func) + end + function c:Update(start,reset,count,skip) + self.start=start or self.start + self.endAt=reset or self.endAt + self.skip=skip or self.skip + self.count=count or self.count + self:Resume() + end + c:OnStep(function(p,s) + if s.count>0 and s.endAt==p then + for fe=1,#s.funcE do + s.funcE[fe](s) + end + s.pos=s.start-1 + elseif s.count<0 and s.endAt==p then + for fe=1,#s.funcE do + s.funcE[fe](s) + end + s.pos=s.start-1 + end + end) + return c +end +function multi:newTStep(start,reset,count,set) + local c=self:newBase() + think=1 + c.Type="TStep" + c.start=start or 1 + local reset = reset or math.huge + c.endAt=reset + c.pos=start or 1 + c.skip=skip or 0 + c.count=count or 1*think + c.funcE={} + c.timer=os.clock() + c.set=set or 1 + function c:Update(start,reset,count,set) + self.start=start or self.start + self.pos=start + self.endAt=reset or self.endAt + self.set=set or self.set + self.count=count or self.count or 1 + self.timer=os.clock() + self:Resume() + end + function c:Act() + if self.Active then + if os.clock()-self.timer>=self.set then + self:Reset() + for i=1,#self.func do + self.func[i](self.pos,self) + end + if self.endAt==self.pos then + for fe=1,#self.funcE do + self.funcE[fe](self) + end + self.pos=self.start-1 + end + self.pos=self.pos+self.count + end + end + end + function c:OnEnd(func) + table.insert(self.funcE,func) + end + function c:Reset(n) + if n then self.set=n end + self.timer=os.clock() + self:Resume() + end + function c:OnStep(func) + table.insert(self.func,func) + end + return c +end +function multi:newTrigger(func) + local c=self:newBase() + c.Type="Trigger" + c.trigfunc=func or function() end + function c:Fire(...) + self:trigfunc(self,...) + end + return c +end +--Managers +function multi:mainloop() + for i=1,#self.Tasks do + self.Tasks[i](self) + end + self.Start=os.clock() + while self.Active do + self:Do_Order() + end +end +function multi._tFunc(self,dt) + for i=1,#self.Tasks do + self.Tasks[i](self) + end + print("once!") + if dt then + self.pump=true + end + self.pumpvar=dt + self.Start=os.clock() +end +function multi:uManager(dt) + self:oneTime(self._tFunc,self,dt) + self:Do_Order() +end +multi.drawF={} +function multi:dManager() + for ii=1,#multi.drawF do + multi.drawF[ii]() + end +end +function multi:onDraw(func) + table.insert(self.drawF,func) +end +function multi:lManager() + if love.event then + love.event.pump() + for e,a,b,c,d in love.event.poll() do + if e == "quit" then + if not love.quit or not love.quit() then + if love.audio then + love.audio.stop() + end + return nil + end + end + love.handlers[e](a,b,c,d) + end + end + if love.timer then + love.timer.step() + dt = love.timer.getDelta() + end + if love.update then love.update(dt) end + multi:uManager(dt) + if love.window and love.graphics and love.window.isCreated() then + love.graphics.clear() + love.graphics.origin() + if love.draw then love.draw() end + multi.dManager() + love.graphics.setColor(255,255,255,255) + if multi.draw then multi.draw() end + love.graphics.present() + end +end +Thread={} +Thread.Name="Thread 1" +Thread.ChannelThread = love.thread.getChannel("Easy1") +Thread.ChannelMain = love.thread.getChannel("EasyMain") +Thread.Global = {} +function Thread:packTable(G) + function escapeStr(str) + local temp="" + for i=1,#str do + temp=temp.."\\"..string.byte(string.sub(str,i,i)) + end + return temp + end + function ToStr(t) + local dat="{" + for i,v in pairs(t) do + if type(i)=="number" then + i="["..i.."]=" + else + i=i.."=" + end + if type(v)=="string" then + dat=dat..i.."\""..v.."\"," + elseif type(v)=="number" then + dat=dat..i..v.."," + elseif type(v)=="boolean" then + dat=dat..i..tostring(v).."," + elseif type(v)=="table" and not(G==v) then + dat=dat..i..ToStr(v).."," + --elseif type(v)=="table" and G==v then + -- dat=dat..i.."assert(loadstring(\"return self\"))," + elseif type(v)=="function" then + dat=dat..i.."assert(loadstring(\""..escapeStr(string.dump(v)).."\"))," + end + end + return string.sub(dat,1,-2).."}" + end + return "return "..ToStr(G) +end +function Thread:Send(name,var) + arg3="1" + if type(var)=="table" then + var=Thread:packTable(var) + arg3="table" + end + self.ChannelMain:push({name,var,arg3}) +end +function Thread:UnPackChannel() + local c=self.ChannelThread:getCount() + for i=1,c do + local temp=self.ChannelThread:pop() + if temp[1] and temp[2] then + if temp[1]=="func" and type(temp[2])=="string" then + loadstring(temp[2])(temp[3]) + elseif temp[1]=="table" then + _G[temp[3]]=loadstring(temp[2])() + else + _G[temp[1]]=temp[2] + end + end + end + if #multi:getChildren()<2 then + os.sleep(.05) + end +end +function Thread:boost(func,name) + self:Send(name,string.dump(func)) +end +function Thread.mainloop() + Thread:UnPackChannel() +end +Thread.MainThread=false +multi:newLoop():OnLoop(Thread.mainloop) +multi:mainloop() diff --git a/Libs/T2.lua b/Libs/T2.lua new file mode 100644 index 0000000..bd88eaa --- /dev/null +++ b/Libs/T2.lua @@ -0,0 +1,598 @@ +require("love.timer") +require("love.system") +require("love.sound") +require("love.physics") +require("love.mouse") +require("love.math") +require("love.keyboard") +require("love.joystick") +require("love.image") +require("love.font") +require("love.filesystem") +require("love.event") +require("love.audio") +require("love.graphics") +require("love.window") +_defaultfont = love.graphics.getFont() +gui = {} +function gui.getTile(i,x,y,w,h)-- returns imagedata + if type(i)=="userdata" then + -- do nothing + else + error("getTile invalid args!!! Usage: ImageElement:getTile(x,y,w,h) or gui:getTile(imagedata,x,y,w,h)") + end + local iw,ih=i:getDimensions() + local id,_id=i:getData(),love.image.newImageData(w,h) + for _x=x,w+x-1 do + for _y=y,h+y-1 do + _id:setPixel(_x-x,_y-y,id:getPixel(_x,_y)) + end + end + return love.graphics.newImage(_id) +end +multi = {} +multi.Version="4.0.0" +multi.__index = multi +multi.Mainloop={} +multi.Tasks={} +multi.Tasks2={} +multi.Garbage={} +multi.Children={} +multi.Paused={} +multi.MasterId=0 +multi.Active=true +multi.Id=-1 +multi.Type="MainInt" +multi.Rest=0 +-- System +os.sleep=love.timer.sleep +function multi:newBase(ins) + if not(self.Type=="MainInt" or self.Type=="int") then error("Can only create an object on multi or an interface obj") return false end + local c = {} + if self.Type=="int" then + setmetatable(c, self.Parent) + else + setmetatable(c, self) + end + c.Active=true + c.func={} + c.Id=0 + c.Act=function() end + c.Parent=self + if ins then + table.insert(self.Mainloop,ins,c) + else + table.insert(self.Mainloop,c) + end + self.MasterId=self.MasterId+1 + return c +end +function multi:reboot(r) + self.Mainloop={} + self.Tasks={} + self.Tasks2={} + self.Garbage={} + self.Children={} + self.Paused={} + self.MasterId=0 + self.Active=true + self.Id=-1 + if r then + for i,v in pairs(_G) do + if type(i)=="table" then + if i.Parent and i.Id and i.Act then + i={} + end + end + end + end +end +function multi:getChildren() + return self.Mainloop +end +--Processor +function multi:Do_Order() + for _D=#self.Mainloop,1,-1 do + if self.Mainloop[_D]~=nil then + self.Mainloop[_D].Id=_D + self.Mainloop[_D]:Act() + end + if self.Mainloop[_D].rem then + table.remove(self.Mainloop,_D) + end + end + if self.Rest>0 then + os.sleep(self.Rest) + end +end +function multi:benchMark(sec) + local temp=self:newLoop(function(t,self) + if os.clock()-self.init>self.sec then + print(self.c.." steps in "..self.sec.." second(s)") + self.tt(self.sec) + self:Destroy() + else + self.c=self.c+1 + end + end) + function temp:OnBench(func) + self.tt=func + end + self.tt=function() end + temp.sec=sec + temp.init=os.clock() + temp.c=0 + return temp +end +function multi:newInterface() + if not(self.Type=="MainInt") then error("Can only create an interface on the multi obj") return false end + local c = {} + setmetatable(c, self) + c.Parent=self + c.Active=true + c.func={} + c.Id=0 + c.Type="int" + c.Mainloop={} + c.Tasks={} + c.Tasks2={} + c.Garbage={} + c.Children={} + c.Paused={} + c.MasterId=0 + c.Active=true + c.Id=-1 + c.Rest=0 + function c:Start() + if self.l then + self.l:Resume() + else + self.l=self.Parent:newLoop(function(dt) c:uManager(dt) end) + end + end + function c:Stop() + if self.l then + self.l:Pause() + end + end + function c:Remove() + self:Destroy() + self.l:Destroy() + end + return c +end +--Helpers +function multi:FreeMainEvent() + self.func={} +end +function multi:isPaused() + return not(self.Active) +end +function multi:Pause(n) + if self.Type=="int" or self.Type=="MainInt" then + self.Active=false + if not(n) then + local c=self:getChildren() + for i=1,#c do + c[i]:Pause() + end + else + self:hold(n) + end + else + if not(n) then + self.Active=false + if self.Parent.Mainloop[self.Id]~=nil then + table.remove(self.Parent.Mainloop,self.Id) + table.insert(self.Parent.Paused,self) + self.Id=#self.Parent.Paused + end + else + self:hold(n) + end + end +end +function multi:Resume() + if self.Type=="int" or self.Type=="MainInt" then + self.Active=true + local c=self:getChildren() + for i=1,#c do + c[i]:Resume() + end + else + if self:isPaused() then + self.Active=true + for i=1,#self.Parent.Paused do + if self.Parent.Paused[i]==self then + table.remove(self.Parent.Paused,i) + return + end + end + table.insert(self.Parent.Mainloop,self) + end + end +end +function multi:Destroy() + if self.Type=="int" or self.Type=="MainInt" then + local c=self:getChildren() + for i=1,#c do + c[i]:Destroy() + end + else + self.rem=true + end +end +function multi:hold(task) + self:Pause() + if type(task)=="number" then + local alarm=self:newAlarm(task) + while alarm.Active==true do + if love then + self.Parent.lManager() + else + self.Parent.Do_Order() + end + end + alarm:Destroy() + self:Resume() + elseif type(task)=="function" then + local env=self.Parent:newEvent(task) + env:OnEvent(function(envt) envt:Pause() envt:Stop() end) + while env.Active do + if love then + self.Parent.lManager() + else + self.Parent.Do_Order() + end + end + env:Destroy() + self:Resume() + else + print("Error Data Type!!!") + end +end +function multi:oneTime(func,...) + if not(self.Type=="MainInt" or self.Type=="int") then + for _k=1,#self.Parent.Tasks2 do + if self.Parent.Tasks2[_k]==func then + return false + end + end + table.insert(self.Parent.Tasks2,func) + func(...) + return true + else + for _k=1,#self.Tasks2 do + if self.Tasks2[_k]==func then + return false + end + end + table.insert(self.Tasks2,func) + func(...) + return true + end +end +--Constructors +function multi:newEvent(task) + local c=self:newBase() + c.Type="Event" + c.Task=task or function() end + function c:Act() + if self.Task(self) and self.Active==true then + self:Pause() + for _E=1,#self.func do + self.func[_E](self) + end + end + end + function c:OnEvent(func) + table.insert(self.func,func) + end + return c +end +function multi:newAlarm(set) + local c=self:newBase() + c.Type="Alarm" + c.timer=os.clock() + c.set=set or 0 + function c:Act() + if self.Active==true then + if os.clock()-self.timer>=self.set then + self:Pause() + for i=1,#self.func do + self.func[i](self) + end + end + end + end + function c:Reset(n) + if n then self.set=n end + self.timer=os.clock() + self:Resume() + end + function c:OnRing(func) + table.insert(self.func,func) + end + return c +end +function multi:newTask(func) + table.insert(self.Tasks,func) +end +function multi:newLoop(func) + local c=self:newBase() + c.Type="Loop" + if func then + c.func={func} + end + function c:Act() + if self.Active==true then + for i=1,#self.func do + self.func[i](os.clock()-self.Parent.Start,self) + end + end + end + function c:OnLoop(func) + table.insert(self.func,func) + end + return c +end +function multi:newStep(start,reset,count,skip) + local c=self:newBase() + think=1 + c.Type="Step" + c.pos=start or 1 + c.endAt=reset or math.huge + c.skip=skip or 0 + c.spos=0 + c.count=count or 1*think + c.funcE={} + c.start=start or 1 + if start~=nil and reset~=nil then + if start>reset then + think=-1 + end + end + function c:Act() + if self~=nil then + if self.spos==0 then + if self.Active==true then + for i=1,#self.func do + self.func[i](self.pos,self) + end + self.pos=self.pos+self.count + end + end + end + self.spos=self.spos+1 + if self.spos>=self.skip then + self.spos=0 + end + end + function c:OnStep(func) + table.insert(self.func,1,func) + end + function c:OnEnd(func) + table.insert(self.funcE,func) + end + function c:Update(start,reset,count,skip) + self.start=start or self.start + self.endAt=reset or self.endAt + self.skip=skip or self.skip + self.count=count or self.count + self:Resume() + end + c:OnStep(function(p,s) + if s.count>0 and s.endAt==p then + for fe=1,#s.funcE do + s.funcE[fe](s) + end + s.pos=s.start-1 + elseif s.count<0 and s.endAt==p then + for fe=1,#s.funcE do + s.funcE[fe](s) + end + s.pos=s.start-1 + end + end) + return c +end +function multi:newTStep(start,reset,count,set) + local c=self:newBase() + think=1 + c.Type="TStep" + c.start=start or 1 + local reset = reset or math.huge + c.endAt=reset + c.pos=start or 1 + c.skip=skip or 0 + c.count=count or 1*think + c.funcE={} + c.timer=os.clock() + c.set=set or 1 + function c:Update(start,reset,count,set) + self.start=start or self.start + self.pos=start + self.endAt=reset or self.endAt + self.set=set or self.set + self.count=count or self.count or 1 + self.timer=os.clock() + self:Resume() + end + function c:Act() + if self.Active then + if os.clock()-self.timer>=self.set then + self:Reset() + for i=1,#self.func do + self.func[i](self.pos,self) + end + if self.endAt==self.pos then + for fe=1,#self.funcE do + self.funcE[fe](self) + end + self.pos=self.start-1 + end + self.pos=self.pos+self.count + end + end + end + function c:OnEnd(func) + table.insert(self.funcE,func) + end + function c:Reset(n) + if n then self.set=n end + self.timer=os.clock() + self:Resume() + end + function c:OnStep(func) + table.insert(self.func,func) + end + return c +end +function multi:newTrigger(func) + local c=self:newBase() + c.Type="Trigger" + c.trigfunc=func or function() end + function c:Fire(...) + self:trigfunc(self,...) + end + return c +end +--Managers +function multi:mainloop() + for i=1,#self.Tasks do + self.Tasks[i](self) + end + self.Start=os.clock() + while self.Active do + self:Do_Order() + end +end +function multi._tFunc(self,dt) + for i=1,#self.Tasks do + self.Tasks[i](self) + end + print("once!") + if dt then + self.pump=true + end + self.pumpvar=dt + self.Start=os.clock() +end +function multi:uManager(dt) + self:oneTime(self._tFunc,self,dt) + self:Do_Order() +end +multi.drawF={} +function multi:dManager() + for ii=1,#multi.drawF do + multi.drawF[ii]() + end +end +function multi:onDraw(func) + table.insert(self.drawF,func) +end +function multi:lManager() + if love.event then + love.event.pump() + for e,a,b,c,d in love.event.poll() do + if e == "quit" then + if not love.quit or not love.quit() then + if love.audio then + love.audio.stop() + end + return nil + end + end + love.handlers[e](a,b,c,d) + end + end + if love.timer then + love.timer.step() + dt = love.timer.getDelta() + end + if love.update then love.update(dt) end + multi:uManager(dt) + if love.window and love.graphics and love.window.isCreated() then + love.graphics.clear() + love.graphics.origin() + if love.draw then love.draw() end + multi.dManager() + love.graphics.setColor(255,255,255,255) + if multi.draw then multi.draw() end + love.graphics.present() + end +end +Thread={} +Thread.Name="Thread 2" +Thread.ChannelThread = love.thread.getChannel("Easy2") +Thread.ChannelMain = love.thread.getChannel("EasyMain") +Thread.Global = {} +function Thread:packTable(G) + function escapeStr(str) + local temp="" + for i=1,#str do + temp=temp.."\\"..string.byte(string.sub(str,i,i)) + end + return temp + end + function ToStr(t) + local dat="{" + for i,v in pairs(t) do + if type(i)=="number" then + i="["..i.."]=" + else + i=i.."=" + end + if type(v)=="string" then + dat=dat..i.."\""..v.."\"," + elseif type(v)=="number" then + dat=dat..i..v.."," + elseif type(v)=="boolean" then + dat=dat..i..tostring(v).."," + elseif type(v)=="table" and not(G==v) then + dat=dat..i..ToStr(v).."," + --elseif type(v)=="table" and G==v then + -- dat=dat..i.."assert(loadstring(\"return self\"))," + elseif type(v)=="function" then + dat=dat..i.."assert(loadstring(\""..escapeStr(string.dump(v)).."\"))," + end + end + return string.sub(dat,1,-2).."}" + end + return "return "..ToStr(G) +end +function Thread:Send(name,var) + arg3="2" + if type(var)=="table" then + var=Thread:packTable(var) + arg3="table" + end + self.ChannelMain:push({name,var,arg3}) +end +function Thread:UnPackChannel() + local c=self.ChannelThread:getCount() + for i=1,c do + local temp=self.ChannelThread:pop() + if temp[1] and temp[2] then + if temp[1]=="func" and type(temp[2])=="string" then + loadstring(temp[2])(temp[3]) + elseif temp[1]=="table" then + _G[temp[3]]=loadstring(temp[2])() + else + _G[temp[1]]=temp[2] + end + end + end + if #multi:getChildren()<2 then + os.sleep(.05) + end +end +function Thread:boost(func,name) + self:Send(name,string.dump(func)) +end +function Thread.mainloop() + Thread:UnPackChannel() +end +Thread.MainThread=false +multi:newLoop():OnLoop(Thread.mainloop) +multi:mainloop() diff --git a/Libs/T3.lua b/Libs/T3.lua new file mode 100644 index 0000000..10a1007 --- /dev/null +++ b/Libs/T3.lua @@ -0,0 +1,596 @@ +require("love.timer") +require("love.system") +require("love.sound") +require("love.physics") +require("love.mouse") +require("love.math") +require("love.keyboard") +require("love.joystick") +require("love.image") +require("love.font") +require("love.filesystem") +require("love.event") +require("love.audio") +require("love.graphics") +require("love.window") +_defaultfont = love.graphics.getFont() +gui = {} +function gui.getTile(i,x,y,w,h)-- returns imagedata + if type(i)=="userdata" then + -- do nothing + else + error("getTile invalid args!!! Usage: ImageElement:getTile(x,y,w,h) or gui:getTile(imagedata,x,y,w,h)") + end + local iw,ih=i:getDimensions() + local id,_id=i:getData(),love.image.newImageData(w,h) + for _x=x,w+x-1 do + for _y=y,h+y-1 do + _id:setPixel(_x-x,_y-y,id:getPixel(_x,_y)) + end + end + return love.graphics.newImage(_id) +end +multi = {} +multi.Version="4.0.0" +multi.__index = multi +multi.Mainloop={} +multi.Tasks={} +multi.Tasks2={} +multi.Garbage={} +multi.Children={} +multi.Paused={} +multi.MasterId=0 +multi.Active=true +multi.Id=-1 +multi.Type="MainInt" +multi.Rest=0 +-- System +os.sleep=love.timer.sleep +function multi:newBase(ins) + if not(self.Type=="MainInt" or self.Type=="int") then error("Can only create an object on multi or an interface obj") return false end + local c = {} + if self.Type=="int" then + setmetatable(c, self.Parent) + else + setmetatable(c, self) + end + c.Active=true + c.func={} + c.Id=0 + c.Act=function() end + c.Parent=self + if ins then + table.insert(self.Mainloop,ins,c) + else + table.insert(self.Mainloop,c) + end + self.MasterId=self.MasterId+1 + return c +end +function multi:reboot(r) + self.Mainloop={} + self.Tasks={} + self.Tasks2={} + self.Garbage={} + self.Children={} + self.Paused={} + self.MasterId=0 + self.Active=true + self.Id=-1 + if r then + for i,v in pairs(_G) do + if type(i)=="table" then + if i.Parent and i.Id and i.Act then + i={} + end + end + end + end +end +function multi:getChildren() + return self.Mainloop +end +--Processor +function multi:Do_Order() + for _D=#self.Mainloop,1,-1 do + if self.Mainloop[_D]~=nil then + self.Mainloop[_D].Id=_D + self.Mainloop[_D]:Act() + end + if self.Mainloop[_D].rem then + table.remove(self.Mainloop,_D) + end + end + if self.Rest>0 then + os.sleep(self.Rest) + end +end +function multi:benchMark(sec) + local temp=self:newLoop(function(t,self) + if os.clock()-self.init>self.sec then + print(self.c.." steps in "..self.sec.." second(s)") + self.tt(self.sec) + self:Destroy() + else + self.c=self.c+1 + end + end) + function temp:OnBench(func) + self.tt=func + end + self.tt=function() end + temp.sec=sec + temp.init=os.clock() + temp.c=0 + return temp +end +function multi:newInterface() + if not(self.Type=="MainInt") then error("Can only create an interface on the multi obj") return false end + local c = {} + setmetatable(c, self) + c.Parent=self + c.Active=true + c.func={} + c.Id=0 + c.Type="int" + c.Mainloop={} + c.Tasks={} + c.Tasks2={} + c.Garbage={} + c.Children={} + c.Paused={} + c.MasterId=0 + c.Active=true + c.Id=-1 + c.Rest=0 + function c:Start() + if self.l then + self.l:Resume() + else + self.l=self.Parent:newLoop(function(dt) c:uManager(dt) end) + end + end + function c:Stop() + if self.l then + self.l:Pause() + end + end + function c:Remove() + self:Destroy() + self.l:Destroy() + end + return c +end +--Helpers +function multi:FreeMainEvent() + self.func={} +end +function multi:isPaused() + return not(self.Active) +end +function multi:Pause(n) + if self.Type=="int" or self.Type=="MainInt" then + self.Active=false + if not(n) then + local c=self:getChildren() + for i=1,#c do + c[i]:Pause() + end + else + self:hold(n) + end + else + if not(n) then + self.Active=false + if self.Parent.Mainloop[self.Id]~=nil then + table.remove(self.Parent.Mainloop,self.Id) + table.insert(self.Parent.Paused,self) + self.Id=#self.Parent.Paused + end + else + self:hold(n) + end + end +end +function multi:Resume() + if self.Type=="int" or self.Type=="MainInt" then + self.Active=true + local c=self:getChildren() + for i=1,#c do + c[i]:Resume() + end + else + if self:isPaused() then + self.Active=true + for i=1,#self.Parent.Paused do + if self.Parent.Paused[i]==self then + table.remove(self.Parent.Paused,i) + return + end + end + table.insert(self.Parent.Mainloop,self) + end + end +end +function multi:Destroy() + if self.Type=="int" or self.Type=="MainInt" then + local c=self:getChildren() + for i=1,#c do + c[i]:Destroy() + end + else + self.rem=true + end +end +function multi:hold(task) + self:Pause() + if type(task)=="number" then + local alarm=self:newAlarm(task) + while alarm.Active==true do + if love then + self.Parent.lManager() + else + self.Parent.Do_Order() + end + end + alarm:Destroy() + self:Resume() + elseif type(task)=="function" then + local env=self.Parent:newEvent(task) + env:OnEvent(function(envt) envt:Pause() envt:Stop() end) + while env.Active do + if love then + self.Parent.lManager() + else + self.Parent.Do_Order() + end + end + env:Destroy() + self:Resume() + else + print("Error Data Type!!!") + end +end +function multi:oneTime(func,...) + if not(self.Type=="MainInt" or self.Type=="int") then + for _k=1,#self.Parent.Tasks2 do + if self.Parent.Tasks2[_k]==func then + return false + end + end + table.insert(self.Parent.Tasks2,func) + func(...) + return true + else + for _k=1,#self.Tasks2 do + if self.Tasks2[_k]==func then + return false + end + end + table.insert(self.Tasks2,func) + func(...) + return true + end +end +--Constructors +function multi:newEvent(task) + local c=self:newBase() + c.Type="Event" + c.Task=task or function() end + function c:Act() + if self.Task(self) and self.Active==true then + self:Pause() + for _E=1,#self.func do + self.func[_E](self) + end + end + end + function c:OnEvent(func) + table.insert(self.func,func) + end + return c +end +function multi:newAlarm(set) + local c=self:newBase() + c.Type="Alarm" + c.timer=os.clock() + c.set=set or 0 + function c:Act() + if self.Active==true then + if os.clock()-self.timer>=self.set then + self:Pause() + for i=1,#self.func do + self.func[i](self) + end + end + end + end + function c:Reset(n) + if n then self.set=n end + self.timer=os.clock() + self:Resume() + end + function c:OnRing(func) + table.insert(self.func,func) + end + return c +end +function multi:newTask(func) + table.insert(self.Tasks,func) +end +function multi:newLoop(func) + local c=self:newBase() + c.Type="Loop" + if func then + c.func={func} + end + function c:Act() + if self.Active==true then + for i=1,#self.func do + self.func[i](os.clock()-self.Parent.Start,self) + end + end + end + function c:OnLoop(func) + table.insert(self.func,func) + end + return c +end +function multi:newStep(start,reset,count,skip) + local c=self:newBase() + think=1 + c.Type="Step" + c.pos=start or 1 + c.endAt=reset or math.huge + c.skip=skip or 0 + c.spos=0 + c.count=count or 1*think + c.funcE={} + c.start=start or 1 + if start~=nil and reset~=nil then + if start>reset then + think=-1 + end + end + function c:Act() + if self~=nil then + if self.spos==0 then + if self.Active==true then + for i=1,#self.func do + self.func[i](self.pos,self) + end + self.pos=self.pos+self.count + end + end + end + self.spos=self.spos+1 + if self.spos>=self.skip then + self.spos=0 + end + end + function c:OnStep(func) + table.insert(self.func,1,func) + end + function c:OnEnd(func) + table.insert(self.funcE,func) + end + function c:Update(start,reset,count,skip) + self.start=start or self.start + self.endAt=reset or self.endAt + self.skip=skip or self.skip + self.count=count or self.count + self:Resume() + end + c:OnStep(function(p,s) + if s.count>0 and s.endAt==p then + for fe=1,#s.funcE do + s.funcE[fe](s) + end + s.pos=s.start-1 + elseif s.count<0 and s.endAt==p then + for fe=1,#s.funcE do + s.funcE[fe](s) + end + s.pos=s.start-1 + end + end) + return c +end +function multi:newTStep(start,reset,count,set) + local c=self:newBase() + think=1 + c.Type="TStep" + c.start=start or 1 + local reset = reset or math.huge + c.endAt=reset + c.pos=start or 1 + c.skip=skip or 0 + c.count=count or 1*think + c.funcE={} + c.timer=os.clock() + c.set=set or 1 + function c:Update(start,reset,count,set) + self.start=start or self.start + self.pos=start + self.endAt=reset or self.endAt + self.set=set or self.set + self.count=count or self.count or 1 + self.timer=os.clock() + self:Resume() + end + function c:Act() + if self.Active then + if os.clock()-self.timer>=self.set then + self:Reset() + for i=1,#self.func do + self.func[i](self.pos,self) + end + if self.endAt==self.pos then + for fe=1,#self.funcE do + self.funcE[fe](self) + end + self.pos=self.start-1 + end + self.pos=self.pos+self.count + end + end + end + function c:OnEnd(func) + table.insert(self.funcE,func) + end + function c:Reset(n) + if n then self.set=n end + self.timer=os.clock() + self:Resume() + end + function c:OnStep(func) + table.insert(self.func,func) + end + return c +end +function multi:newTrigger(func) + local c=self:newBase() + c.Type="Trigger" + c.trigfunc=func or function() end + function c:Fire(...) + self:trigfunc(self,...) + end + return c +end +--Managers +function multi:mainloop() + for i=1,#self.Tasks do + self.Tasks[i](self) + end + self.Start=os.clock() + while self.Active do + self:Do_Order() + end +end +function multi._tFunc(self,dt) + for i=1,#self.Tasks do + self.Tasks[i](self) + end + print("once!") + if dt then + self.pump=true + end + self.pumpvar=dt + self.Start=os.clock() +end +function multi:uManager(dt) + self:oneTime(self._tFunc,self,dt) + self:Do_Order() +end +multi.drawF={} +function multi:dManager() + for ii=1,#multi.drawF do + multi.drawF[ii]() + end +end +function multi:onDraw(func) + table.insert(self.drawF,func) +end +function multi:lManager() + if love.event then + love.event.pump() + for e,a,b,c,d in love.event.poll() do + if e == "quit" then + if not love.quit or not love.quit() then + if love.audio then + love.audio.stop() + end + return nil + end + end + love.handlers[e](a,b,c,d) + end + end + if love.timer then + love.timer.step() + dt = love.timer.getDelta() + end + if love.update then love.update(dt) end + multi:uManager(dt) + if love.window and love.graphics and love.window.isCreated() then + love.graphics.clear() + love.graphics.origin() + if love.draw then love.draw() end + multi.dManager() + love.graphics.setColor(255,255,255,255) + if multi.draw then multi.draw() end + love.graphics.present() + end +end +Thread={} +Thread.Name="Thread 3" +Thread.ChannelThread = love.thread.getChannel("Easy3") +Thread.ChannelMain = love.thread.getChannel("EasyMain") +Thread.Global = {} +function Thread:packTable(G) + function escapeStr(str) + local temp="" + for i=1,#str do + temp=temp.."\\"..string.byte(string.sub(str,i,i)) + end + return temp + end + function ToStr(t) + local dat="{" + for i,v in pairs(t) do + if type(i)=="number" then + i="["..i.."]=" + else + i=i.."=" + end + if type(v)=="string" then + dat=dat..i.."\""..v.."\"," + elseif type(v)=="number" then + dat=dat..i..v.."," + elseif type(v)=="boolean" then + dat=dat..i..tostring(v).."," + elseif type(v)=="table" and not(G==v) then + dat=dat..i..ToStr(v).."," + --elseif type(v)=="table" and G==v then + -- dat=dat..i.."assert(loadstring(\"return self\"))," + elseif type(v)=="function" then + dat=dat..i.."assert(loadstring(\""..escapeStr(string.dump(v)).."\"))," + end + end + return string.sub(dat,1,-2).."}" + end + return "return "..ToStr(G) +end +function Thread:Send(name,var) + arg3="3" + if type(var)=="table" then + var=Thread:packTable(var) + arg3="table" + end + self.ChannelMain:push({name,var,arg3}) +end +function Thread:UnPackChannel() + local c=self.ChannelThread:getCount() + for i=1,c do + local temp=self.ChannelThread:pop() + if temp[1] and temp[2] then + if temp[1]=="func" and type(temp[2])=="string" then + loadstring(temp[2])(temp[3]) + else + _G[temp[1]]=temp[2] + end + end + end + if #multi:getChildren()<2 then + os.sleep(.05) + end +end +function Thread:boost(func,name) + self:Send(name,string.dump(func)) +end +function Thread.mainloop() + Thread:UnPackChannel() +end +Thread.MainThread=false +multi:newLoop():OnLoop(Thread.mainloop) +multi:mainloop() diff --git a/Libs/T4.lua b/Libs/T4.lua new file mode 100644 index 0000000..ce78a2f --- /dev/null +++ b/Libs/T4.lua @@ -0,0 +1,596 @@ +require("love.timer") +require("love.system") +require("love.sound") +require("love.physics") +require("love.mouse") +require("love.math") +require("love.keyboard") +require("love.joystick") +require("love.image") +require("love.font") +require("love.filesystem") +require("love.event") +require("love.audio") +require("love.graphics") +require("love.window") +_defaultfont = love.graphics.getFont() +gui = {} +function gui.getTile(i,x,y,w,h)-- returns imagedata + if type(i)=="userdata" then + -- do nothing + else + error("getTile invalid args!!! Usage: ImageElement:getTile(x,y,w,h) or gui:getTile(imagedata,x,y,w,h)") + end + local iw,ih=i:getDimensions() + local id,_id=i:getData(),love.image.newImageData(w,h) + for _x=x,w+x-1 do + for _y=y,h+y-1 do + _id:setPixel(_x-x,_y-y,id:getPixel(_x,_y)) + end + end + return love.graphics.newImage(_id) +end +multi = {} +multi.Version="4.0.0" +multi.__index = multi +multi.Mainloop={} +multi.Tasks={} +multi.Tasks2={} +multi.Garbage={} +multi.Children={} +multi.Paused={} +multi.MasterId=0 +multi.Active=true +multi.Id=-1 +multi.Type="MainInt" +multi.Rest=0 +-- System +os.sleep=love.timer.sleep +function multi:newBase(ins) + if not(self.Type=="MainInt" or self.Type=="int") then error("Can only create an object on multi or an interface obj") return false end + local c = {} + if self.Type=="int" then + setmetatable(c, self.Parent) + else + setmetatable(c, self) + end + c.Active=true + c.func={} + c.Id=0 + c.Act=function() end + c.Parent=self + if ins then + table.insert(self.Mainloop,ins,c) + else + table.insert(self.Mainloop,c) + end + self.MasterId=self.MasterId+1 + return c +end +function multi:reboot(r) + self.Mainloop={} + self.Tasks={} + self.Tasks2={} + self.Garbage={} + self.Children={} + self.Paused={} + self.MasterId=0 + self.Active=true + self.Id=-1 + if r then + for i,v in pairs(_G) do + if type(i)=="table" then + if i.Parent and i.Id and i.Act then + i={} + end + end + end + end +end +function multi:getChildren() + return self.Mainloop +end +--Processor +function multi:Do_Order() + for _D=#self.Mainloop,1,-1 do + if self.Mainloop[_D]~=nil then + self.Mainloop[_D].Id=_D + self.Mainloop[_D]:Act() + end + if self.Mainloop[_D].rem then + table.remove(self.Mainloop,_D) + end + end + if self.Rest>0 then + os.sleep(self.Rest) + end +end +function multi:benchMark(sec) + local temp=self:newLoop(function(t,self) + if os.clock()-self.init>self.sec then + print(self.c.." steps in "..self.sec.." second(s)") + self.tt(self.sec) + self:Destroy() + else + self.c=self.c+1 + end + end) + function temp:OnBench(func) + self.tt=func + end + self.tt=function() end + temp.sec=sec + temp.init=os.clock() + temp.c=0 + return temp +end +function multi:newInterface() + if not(self.Type=="MainInt") then error("Can only create an interface on the multi obj") return false end + local c = {} + setmetatable(c, self) + c.Parent=self + c.Active=true + c.func={} + c.Id=0 + c.Type="int" + c.Mainloop={} + c.Tasks={} + c.Tasks2={} + c.Garbage={} + c.Children={} + c.Paused={} + c.MasterId=0 + c.Active=true + c.Id=-1 + c.Rest=0 + function c:Start() + if self.l then + self.l:Resume() + else + self.l=self.Parent:newLoop(function(dt) c:uManager(dt) end) + end + end + function c:Stop() + if self.l then + self.l:Pause() + end + end + function c:Remove() + self:Destroy() + self.l:Destroy() + end + return c +end +--Helpers +function multi:FreeMainEvent() + self.func={} +end +function multi:isPaused() + return not(self.Active) +end +function multi:Pause(n) + if self.Type=="int" or self.Type=="MainInt" then + self.Active=false + if not(n) then + local c=self:getChildren() + for i=1,#c do + c[i]:Pause() + end + else + self:hold(n) + end + else + if not(n) then + self.Active=false + if self.Parent.Mainloop[self.Id]~=nil then + table.remove(self.Parent.Mainloop,self.Id) + table.insert(self.Parent.Paused,self) + self.Id=#self.Parent.Paused + end + else + self:hold(n) + end + end +end +function multi:Resume() + if self.Type=="int" or self.Type=="MainInt" then + self.Active=true + local c=self:getChildren() + for i=1,#c do + c[i]:Resume() + end + else + if self:isPaused() then + self.Active=true + for i=1,#self.Parent.Paused do + if self.Parent.Paused[i]==self then + table.remove(self.Parent.Paused,i) + return + end + end + table.insert(self.Parent.Mainloop,self) + end + end +end +function multi:Destroy() + if self.Type=="int" or self.Type=="MainInt" then + local c=self:getChildren() + for i=1,#c do + c[i]:Destroy() + end + else + self.rem=true + end +end +function multi:hold(task) + self:Pause() + if type(task)=="number" then + local alarm=self:newAlarm(task) + while alarm.Active==true do + if love then + self.Parent.lManager() + else + self.Parent.Do_Order() + end + end + alarm:Destroy() + self:Resume() + elseif type(task)=="function" then + local env=self.Parent:newEvent(task) + env:OnEvent(function(envt) envt:Pause() envt:Stop() end) + while env.Active do + if love then + self.Parent.lManager() + else + self.Parent.Do_Order() + end + end + env:Destroy() + self:Resume() + else + print("Error Data Type!!!") + end +end +function multi:oneTime(func,...) + if not(self.Type=="MainInt" or self.Type=="int") then + for _k=1,#self.Parent.Tasks2 do + if self.Parent.Tasks2[_k]==func then + return false + end + end + table.insert(self.Parent.Tasks2,func) + func(...) + return true + else + for _k=1,#self.Tasks2 do + if self.Tasks2[_k]==func then + return false + end + end + table.insert(self.Tasks2,func) + func(...) + return true + end +end +--Constructors +function multi:newEvent(task) + local c=self:newBase() + c.Type="Event" + c.Task=task or function() end + function c:Act() + if self.Task(self) and self.Active==true then + self:Pause() + for _E=1,#self.func do + self.func[_E](self) + end + end + end + function c:OnEvent(func) + table.insert(self.func,func) + end + return c +end +function multi:newAlarm(set) + local c=self:newBase() + c.Type="Alarm" + c.timer=os.clock() + c.set=set or 0 + function c:Act() + if self.Active==true then + if os.clock()-self.timer>=self.set then + self:Pause() + for i=1,#self.func do + self.func[i](self) + end + end + end + end + function c:Reset(n) + if n then self.set=n end + self.timer=os.clock() + self:Resume() + end + function c:OnRing(func) + table.insert(self.func,func) + end + return c +end +function multi:newTask(func) + table.insert(self.Tasks,func) +end +function multi:newLoop(func) + local c=self:newBase() + c.Type="Loop" + if func then + c.func={func} + end + function c:Act() + if self.Active==true then + for i=1,#self.func do + self.func[i](os.clock()-self.Parent.Start,self) + end + end + end + function c:OnLoop(func) + table.insert(self.func,func) + end + return c +end +function multi:newStep(start,reset,count,skip) + local c=self:newBase() + think=1 + c.Type="Step" + c.pos=start or 1 + c.endAt=reset or math.huge + c.skip=skip or 0 + c.spos=0 + c.count=count or 1*think + c.funcE={} + c.start=start or 1 + if start~=nil and reset~=nil then + if start>reset then + think=-1 + end + end + function c:Act() + if self~=nil then + if self.spos==0 then + if self.Active==true then + for i=1,#self.func do + self.func[i](self.pos,self) + end + self.pos=self.pos+self.count + end + end + end + self.spos=self.spos+1 + if self.spos>=self.skip then + self.spos=0 + end + end + function c:OnStep(func) + table.insert(self.func,1,func) + end + function c:OnEnd(func) + table.insert(self.funcE,func) + end + function c:Update(start,reset,count,skip) + self.start=start or self.start + self.endAt=reset or self.endAt + self.skip=skip or self.skip + self.count=count or self.count + self:Resume() + end + c:OnStep(function(p,s) + if s.count>0 and s.endAt==p then + for fe=1,#s.funcE do + s.funcE[fe](s) + end + s.pos=s.start-1 + elseif s.count<0 and s.endAt==p then + for fe=1,#s.funcE do + s.funcE[fe](s) + end + s.pos=s.start-1 + end + end) + return c +end +function multi:newTStep(start,reset,count,set) + local c=self:newBase() + think=1 + c.Type="TStep" + c.start=start or 1 + local reset = reset or math.huge + c.endAt=reset + c.pos=start or 1 + c.skip=skip or 0 + c.count=count or 1*think + c.funcE={} + c.timer=os.clock() + c.set=set or 1 + function c:Update(start,reset,count,set) + self.start=start or self.start + self.pos=start + self.endAt=reset or self.endAt + self.set=set or self.set + self.count=count or self.count or 1 + self.timer=os.clock() + self:Resume() + end + function c:Act() + if self.Active then + if os.clock()-self.timer>=self.set then + self:Reset() + for i=1,#self.func do + self.func[i](self.pos,self) + end + if self.endAt==self.pos then + for fe=1,#self.funcE do + self.funcE[fe](self) + end + self.pos=self.start-1 + end + self.pos=self.pos+self.count + end + end + end + function c:OnEnd(func) + table.insert(self.funcE,func) + end + function c:Reset(n) + if n then self.set=n end + self.timer=os.clock() + self:Resume() + end + function c:OnStep(func) + table.insert(self.func,func) + end + return c +end +function multi:newTrigger(func) + local c=self:newBase() + c.Type="Trigger" + c.trigfunc=func or function() end + function c:Fire(...) + self:trigfunc(self,...) + end + return c +end +--Managers +function multi:mainloop() + for i=1,#self.Tasks do + self.Tasks[i](self) + end + self.Start=os.clock() + while self.Active do + self:Do_Order() + end +end +function multi._tFunc(self,dt) + for i=1,#self.Tasks do + self.Tasks[i](self) + end + print("once!") + if dt then + self.pump=true + end + self.pumpvar=dt + self.Start=os.clock() +end +function multi:uManager(dt) + self:oneTime(self._tFunc,self,dt) + self:Do_Order() +end +multi.drawF={} +function multi:dManager() + for ii=1,#multi.drawF do + multi.drawF[ii]() + end +end +function multi:onDraw(func) + table.insert(self.drawF,func) +end +function multi:lManager() + if love.event then + love.event.pump() + for e,a,b,c,d in love.event.poll() do + if e == "quit" then + if not love.quit or not love.quit() then + if love.audio then + love.audio.stop() + end + return nil + end + end + love.handlers[e](a,b,c,d) + end + end + if love.timer then + love.timer.step() + dt = love.timer.getDelta() + end + if love.update then love.update(dt) end + multi:uManager(dt) + if love.window and love.graphics and love.window.isCreated() then + love.graphics.clear() + love.graphics.origin() + if love.draw then love.draw() end + multi.dManager() + love.graphics.setColor(255,255,255,255) + if multi.draw then multi.draw() end + love.graphics.present() + end +end +Thread={} +Thread.Name="Thread 4" +Thread.ChannelThread = love.thread.getChannel("Easy4") +Thread.ChannelMain = love.thread.getChannel("EasyMain") +Thread.Global = {} +function Thread:packTable(G) + function escapeStr(str) + local temp="" + for i=1,#str do + temp=temp.."\\"..string.byte(string.sub(str,i,i)) + end + return temp + end + function ToStr(t) + local dat="{" + for i,v in pairs(t) do + if type(i)=="number" then + i="["..i.."]=" + else + i=i.."=" + end + if type(v)=="string" then + dat=dat..i.."\""..v.."\"," + elseif type(v)=="number" then + dat=dat..i..v.."," + elseif type(v)=="boolean" then + dat=dat..i..tostring(v).."," + elseif type(v)=="table" and not(G==v) then + dat=dat..i..ToStr(v).."," + --elseif type(v)=="table" and G==v then + -- dat=dat..i.."assert(loadstring(\"return self\"))," + elseif type(v)=="function" then + dat=dat..i.."assert(loadstring(\""..escapeStr(string.dump(v)).."\"))," + end + end + return string.sub(dat,1,-2).."}" + end + return "return "..ToStr(G) +end +function Thread:Send(name,var) + arg3="4" + if type(var)=="table" then + var=Thread:packTable(var) + arg3="table" + end + self.ChannelMain:push({name,var,arg3}) +end +function Thread:UnPackChannel() + local c=self.ChannelThread:getCount() + for i=1,c do + local temp=self.ChannelThread:pop() + if temp[1] and temp[2] then + if temp[1]=="func" and type(temp[2])=="string" then + loadstring(temp[2])(temp[3]) + else + _G[temp[1]]=temp[2] + end + end + end + if #multi:getChildren()<2 then + os.sleep(.05) + end +end +function Thread:boost(func,name) + self:Send(name,string.dump(func)) +end +function Thread.mainloop() + Thread:UnPackChannel() +end +Thread.MainThread=false +multi:newLoop():OnLoop(Thread.mainloop) +multi:mainloop() diff --git a/Libs/ThreadManager.lua b/Libs/ThreadManager.lua new file mode 100644 index 0000000..ff364fb --- /dev/null +++ b/Libs/ThreadManager.lua @@ -0,0 +1,158 @@ +Thread={} +Thread.ChannelT1 = love.thread.getChannel("Easy1") +Thread.ChannelT2 = love.thread.getChannel("Easy2") +Thread.ChannelT3 = love.thread.getChannel("Easy3") +Thread.ChannelT4 = love.thread.getChannel("Easy4") +Thread.ChannelMain = love.thread.getChannel("EasyMain") +Thread.Name = "Thread Main" +Thread.n=0 +Thread.count=1 +function Thread:packTable(G) + function escapeStr(str) + local temp="" + for i=1,#str do + temp=temp.."\\"..string.byte(string.sub(str,i,i)) + end + return temp + end + function ToStr(t) + local dat="{" + for i,v in pairs(t) do + if type(i)=="number" then + i="["..i.."]=" + else + i=i.."=" + end + if type(v)=="string" then + dat=dat..i.."\""..v.."\"," + elseif type(v)=="number" then + dat=dat..i..v.."," + elseif type(v)=="boolean" then + dat=dat..i..tostring(v).."," + elseif type(v)=="table" and not(G==v) then + dat=dat..i..bin.ToStr(v).."," + --elseif type(v)=="table" and G==v then + -- dat=dat..i.."assert(loadstring(\"return self\"))," + elseif type(v)=="function" then + dat=dat..i.."assert(loadstring(\""..escapeStr(string.dump(v)).."\"))," + end + end + return string.sub(dat,1,-2).."}" + end + return ToStr(G) +end +Thread.last={} +function Thread:GetStatus() + print(self.n.." Threads Exist!!!") + for i=1,self.n do + print("\tThread "..i.." Running: "..tostring(self["Thread"..i]:isRunning())) + if not(self["Thread"..i]:isRunning()) then + print("\t\t"..self["Thread"..i]:getError()) + end + end +end +function Thread:Start(n) + local x=love.system.getProcessorCount() + if x>1 then + x=x-1 + else + x=1 + end + n=n or x + if n<1 then + print("Must be atleast 1 thread running!!!") + return + end + if n>4 then + print("Must be no more than 4 threads running!!!") + return + end + for i=1,n do + self["Thread"..i]=love.thread.newThread("Libs/T"..i..".lua") + self["Thread"..i]:start() + end + Thread.n=n +end +function Thread:RestartBroken() + for i=1,self.n do + if self["Thread"..i]:isRunning()==false then + self["Thread"..i]:start() + end + Thread:Boost(Thread.last[1],Thread.last[2]) + end +end +function Thread:Send(name,var,arg3) + if self.n>0 then + if type(var)=="table" then + var=Thread:packTable(var) + arg3=name + name="table" + end + self["ChannelT"..((self.count-1)%self.n)+1]:push({name,var,arg3}) + self.count=self.count+1 + end +end +function Thread:SendAll(name,var,arg3) + if self.n>0 then + for i=1,self.n do + if type(var)=="table" then + var=Thread:packTable(var) + arg3=name + name="table" + end + self["ChannelT"..i]:push({name,var,arg3}) + end + end +end +function Thread:UnPackChannel() + local c=self.ChannelMain:getCount() + for i=1,c do + local temp=self.ChannelMain:pop() + if temp[3]=="table" then + _G[temp[1]]=assert(loadstring(temp[2]))() + else + if Thread.OnDataRecieved then + Thread.OnDataRecieved(temp[1],temp[2],temp[3]) + end + _G[temp[1]]=temp[2] + end + end +end +function Thread:Boost(func,name) + if Thread.last[1]==nil then + return + end + Thread.last={func,name} + name=name or "nil" + if self.n>0 then + self:Send("func",string.dump(func),name) + end +end +function Thread:SendLibs(func,name) + name=name or "nil" + if self.n>0 then + self:SendAll("func",string.dump(func),name) + end +end +function Thread.mainloop() + if Thread.n>0 then + Thread:UnPackChannel() + end +end +Thread.MainThread=true +local loop = multi:newLoop() +loop:OnLoop(Thread.mainloop) +OnThreadError=multi:newConnection() +function love.threaderror(thread, errorstr) + Thread:GetStatus() + Thread:RestartBroken() + Thread:GetStatus() + OnThreadError:Fire(thread,errorstr) +end +multi:newTask(function() + math.randomseed(math.floor(os.time()/2)) + for i=1,Thread.n do + Thread["ChannelT"..i]:push({"randseed",math.random(-1000000,1000000)}) + Thread["ChannelT"..i]:push({"func",string.dump(function() math.randomseed(randseed) end),"randomizing"}) + end +end) diff --git a/Libs/Utils.lua b/Libs/Utils.lua new file mode 100644 index 0000000..5b2b486 --- /dev/null +++ b/Libs/Utils.lua @@ -0,0 +1,797 @@ +-- os Additions +function os.getSystemBit() + if (os.getenv('PROCESSOR_ARCHITEW6432')=='AMD64' or os.getenv('PROCESSOR_ARCHITECTURE')=='AMD64') then + return 64 + else + return 32 + end +end +function os.sleep(n) + if not n then n=0 end + local t0 = os.clock() + while os.clock() - t0 <= n do end +end +function os.pause(msg) + if msg ~= nil then + print(msg) + end + io.read() +end +function os.batCmd(cmd) + io.mkFile('temp.bat',cmd) + local temp = os.execute([[temp.bat]]) + io.delFile('temp.bat') + return temp +end +function os._getOS() + if package.config:sub(1,1)=='\\' then + return 'windows' + else + return 'unix' + end +end +function os.getOS(t) + if not t then + return os._getOS() + end + if os._getOS()=='unix' then + fh,err = io.popen('uname -o 2>/dev/null','r') + if fh then + osname = fh:read() + end + if osname then return osname end + end + local winver='Unknown Version' + local a,b,c=os.capture('ver'):match('(%d+).(%d+).(%d+)') + local win=a..'.'..b..'.'..c + if type(t)=='string' then + win=t + end + if win=='4.00.950' then + winver='95' + elseif win=='4.00.1111' then + winver='95 OSR2' + elseif win=='4.00.1381' then + winver='NT 4.0' + elseif win=='4.10.1998' then + winver='98' + elseif win=='4.10.2222' then + winver='98 SE' + elseif win=='4.90.3000' then + winver='ME' + elseif win=='5.00.2195' then + winver='2000' + elseif win=='5.1.2600' then + winver='XP' + elseif win=='5.2.3790' then + winver='Server 2003' + elseif win=='6.0.6000' then + winver='Vista/Windows Server 2008' + elseif win=='6.0.6002' then + winver='Vista SP2' + elseif win=='6.1.7600' then + winver='7/Windows Server 2008 R2' + elseif win=='6.1.7601' then + winver='7 SP1/Windows Server 2008 R2 SP1' + elseif win=='6.2.9200' then + winver='8/Windows Server 2012' + elseif win=='6.3.9600' then + winver='8.1/Windows Server 2012' + elseif win=='6.4.9841' then + winver='10 Technical Preview 1' + elseif win=='6.4.9860' then + winver='10 Technical Preview 2' + elseif win=='6.4.9879' then + winver='10 Technical Preview 3' + elseif win=='10.0.9926' then + winver='10 Technical Preview 4' + end + return 'Windows '..winver +end +function os.getLuaArch() + return (#tostring({})-7)*4 +end +if os.getOS()=='windows' then + function os.sleep(n) + if n > 0 then os.execute('ping -n ' .. tonumber(n+1) .. ' localhost > NUL') end + end +else + function os.sleep(n) + os.execute('sleep ' .. tonumber(n)) + end +end +function os.capture(cmd, raw) + local f = assert(io.popen(cmd, 'r')) + local s = assert(f:read('*a')) + f:close() + if raw then return s end + s = string.gsub(s, '^%s+', '') + s = string.gsub(s, '%s+$', '') + s = string.gsub(s, '[\n\r]+', ' ') + return s +end +function os.getCurrentUser() + return os.getenv('$USER') or os.getenv('USERNAME') +end +-- string Additions +function string.trim(s) + local from = s:match"^%s*()" + return from > #s and "" or s:match(".*%S", from) +end +function string.random(n) + local str = '' + strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'} + for i=1,n do + h = math.random(1,#strings) + str = str..''..strings[h] + end + return str +end +function string.linesToTable(s) + local t = {} + local i = 0 + while true do + i = string.find(s, '\n', i+1) + if i == nil then return t end + table.insert(t, i) + end +end +function string.lines(str) + local t = {} + local function helper(line) table.insert(t, line) return '' end + helper((str:gsub('(.-)\r?\n', helper))) + return t +end +function string.split(str, pat) + local t = {} -- NOTE: use {n = 0} in Lua-5.0 + local fpat = '(.-)' .. pat + local last_end = 1 + local s, e, cap = str:find(fpat, 1) + while s do + if s ~= 1 or cap ~= '' then + table.insert(t,cap) + end + last_end = e+1 + s, e, cap = str:find(fpat, last_end) + end + if last_end <= #str then + cap = str:sub(last_end) + table.insert(t, cap) + end + return t +end +function string.shuffle(inputStr) + math.randomseed(os.time()); + local outputStr = ''; + local strLength = string.len(inputStr); + while (strLength ~=0) do + local pos = math.random(strLength); + outputStr = outputStr..string.sub(inputStr,pos,pos); + inputStr = inputStr:sub(1, pos-1) .. inputStr:sub(pos+1); + strLength = string.len(inputStr); + end + return outputStr; +end +function string.genKeys(chars,a,f,s,GG) + if GG then + chars=string.rep(chars,a) + end + if s then + chars=string.shuffle(chars) + end + b=#chars + if a==0 then return end + local taken = {} local slots = {} + for i=1,a do slots[i]=0 end + for i=1,b do taken[i]=false end + local index = 1 + local tab={} + for i=1,#chars do + table.insert(tab,chars:sub(i,i)) + end + while index > 0 do repeat + repeat slots[index] = slots[index] + 1 + until slots[index] > b or not taken[slots[index]] + if slots[index] > b then + slots[index] = 0 + index = index - 1 + if index > 0 then + taken[slots[index]] = false + end + break + else + taken[slots[index]] = true + end + if index == a then + local tt={} + for i=1,a do + table.insert(tt,tab[slots[i]]) + end + f(table.concat(tt)) + taken[slots[index]] = false + break + end + index = index + 1 + until true end +end +-- io Additions +function io.getInput(msg) + if msg ~= nil then + io.write(msg) + end + return io.read() +end +function io.scanDir(directory) + directory=directory or io.getDir() + local i, t, popen = 0, {}, io.popen + if os.getOS()=='unix' then + for filename in popen('ls -a \''..directory..'\''):lines() do + i = i + 1 + t[i] = filename + end + else + for filename in popen('dir \''..directory..'\' /b'):lines() do + i = i + 1 + t[i] = filename + end + end + return t +end +function io.buildFromTree(tbl, indent,folder) + if not indent then indent = 0 end + if not folder then folder = '' end + for k, v in pairs(tbl) do + formatting = string.rep(' ', indent) .. k .. ':' + if type(v) == 'table' then + if not(io.dirExists(folder..string.sub(formatting,1,-2))) then + io.mkDir(folder..string.sub(formatting,1,-2)) + end + io.buildFromTree(v,0,folder..string.sub(formatting,1,-2)..'\\') + else + a=string.find(tostring(v),':',1,true) + if a then + file=string.sub(tostring(v),1,a-1) + data=string.sub(tostring(v),a+1) + io.mkFile(folder..file,data,'wb') + else + io.mkFile(folder..v,'','wb') + end + end + end +end +function io.cpFile(path,topath) + if os.getOS()=='unix' then + os.execute('cp '..file1..' '..file2) + else + os.execute('Copy '..path..' '..topath) + end +end +function io.delDir(directoryname) + if os.getOS()=='unix' then + os.execute('rm -rf '..directoryname) + else + os.execute('rmdir '..directoryname..' /s /q') + end +end +function io.delFile(path) + os.remove(path) +end +function io.mkDir(dirname) + os.execute('mkdir "' .. dirname..'"') +end +function io.mkFile(filename,data,tp) + if not(tp) then tp='wb' end + if not(data) then data='' end + file = io.open(filename, tp) + if file==nil then return end + file:write(data) + file:close() +end +function io.movFile(path,topath) + io.cpFile(path,topath) + io.delFile(path) +end +function io.listFiles(dir) + if not(dir) then dir='' end + local f = io.popen('dir \''..dir..'\'') + if f then + return f:read('*a') + else + print('failed to read') + end +end +function io.getDir(dir) + if not dir then return io.getWorkingDir() end + if os.getOS()=='unix' then + return os.capture('cd '..dir..' ; cd') + else + return os.capture('cd '..dir..' & cd') + end +end +function io.getWorkingDir() + return io.popen'cd':read'*l' +end +function io.fileExists(path) + g=io.open(path or '','r') + if path =='' then + p='empty path' + return nil + end + if g~=nil and true or false then + p=(g~=nil and true or false) + end + if g~=nil then + io.close(g) + else + return false + end + return p +end +function io.fileCheck(file_name) + if not file_name then print('No path inputed') return false end + local file_found=io.open(file_name, 'r') + if file_found==nil then + file_found=false + else + file_found=true + end + return file_found +end +function io.dirExists(strFolderName) + strFolderName = strFolderName or io.getDir() + local fileHandle, strError = io.open(strFolderName..'\\*.*','r') + if fileHandle ~= nil then + io.close(fileHandle) + return true + else + if string.match(strError,'No such file or directory') then + return false + else + return true + end + end +end +function io.getAllItems(dir) + local t=os.capture("cd \""..dir.."\" & dir /a-d | find",true):lines() + return t +end +function io.listItems(dir) + if io.dirExists(dir) then + temp=io.listFiles(dir) -- current directory if blank + if io.getDir(dir)=='C:\\\n' then + a,b=string.find(temp,'C:\\',1,true) + a=a+2 + else + a,b=string.find(temp,'..',1,true) + end + temp=string.sub(temp,a+2) + list=string.linesToTable(temp) + temp=string.sub(temp,1,list[#list-2]) + slist=string.lines(temp) + table.remove(slist,1) + table.remove(slist,#slist) + temp={} + temp2={} + for i=1,#slist do + table.insert(temp,string.sub(slist[i],40,-1)) + end + return temp + else + return nil + end +end +function io.getDirectories(dir,l) + if dir then + dir=dir..'\\' + else + dir='' + end + local temp2=io.scanDir(dir) + for i=#temp2,1,-1 do + if io.fileExists(dir..temp2[i]) then + table.remove(temp2,i) + elseif l then + temp2[i]=dir..temp2[i] + end + end + return temp2 +end +function io.getFiles(dir,l) + if dir then + dir=dir..'\\' + else + dir='' + end + local temp2=io.scanDir(dir) + for i=#temp2,1,-1 do + if io.dirExists(dir..temp2[i]) then + table.remove(temp2,i) + elseif l then + temp2[i]=dir..temp2[i] + end + end + return temp2 +end +function io.getFullName(name) + local temp=name or arg[0] + if string.find(temp,'\\',1,true) or string.find(temp,'/',1,true) then + temp=string.reverse(temp) + a,b=string.find(temp,'\\',1,true) + if not(a) or not(b) then + a,b=string.find(temp,'/',1,true) + end + return string.reverse(string.sub(temp,1,b-1)) + end + return temp +end +function io.getName(file) + local name=io.getFullName(file) + name=string.reverse(name) + a,b=string.find(name,'.',1,true) + name=string.sub(name,a+1,-1) + return string.reverse(name) +end +function io.readFile(file) + local f = io.open(file, 'rb') + local content = f:read('*all') + f:close() + return content +end +function io.getExtension(file) + local file=io.getFullName(file) + file=string.reverse(file) + local a,b=string.find(file,'.',0,true) + local temp=string.sub(file,1,b) + return string.reverse(temp) +end +function io.pathToTable(path) + local p=io.splitPath(path) + local temp={} + temp[p[1]]={} + local last=temp[p[1]] + for i=2,#p do + snd=last + last[p[i]]={} + last=last[p[i]] + end + return temp,last,snd +end +function io.splitPath(str) + return string.split(str,'[\\/]+') +end + +function io.parseDir(dir,t) + io.tempFiles={} + function _p(dir) + local dirs=io.getDirectories(dir,true) + local files=io.getFiles(dir,true) + for i=1,#files do + p,l,s=io.pathToTable(files[i]) + if t then + s[io.getFullName(files[i])]=io.readFile(files[i]) + else + s[io.getFullName(files[i])]=io.open(files[i],'r+') + end + table.merge(io.tempFiles,p) + end + for i=1,#dirs do + table.merge(io.tempFiles,io.pathToTable(dirs[i])) + _p(dirs[i],t) + end + end + _p(dir) + return io.tempFiles +end +function io.parsedir(dir,f) + io.tempFiles={} + function _p(dir,f) + local dirs=io.getDirectories(dir,true) + local files=io.getFiles(dir,true) + for i=1,#files do + if not f then + table.insert(io.tempFiles,files[i]) + else + f(files[i]) + end + end + for i=1,#dirs do + _p(dirs[i],f) + end + end + _p(dir,f) + return io.tempFiles +end +function io.driveReady(drive) + drive=drive:upper() + if not(drive:find(':',1,true)) then + drive=drive..':' + end + drives=io.getDrives() + for i=1,#drives do + if drives[i]==drive then + return true + end + end + return false +end +function io.getDrives() + if os.getOS()=='windows' then + local temp={} + local t1=os.capture('wmic logicaldisk where drivetype=2 get deviceid, volumename',true) + local t2=os.capture('wmic logicaldisk where drivetype=3 get deviceid, volumename',true) + for drive,d2 in t1:gmatch('(.:)%s-(%w+)') do + if #d2>1 then + table.insert(temp,drive) + end + end + for drive in t2:gmatch('(.:)') do + table.insert(temp,drive) + end + return temp + end + error('Command is windows only!') +end +-- table Additions +function table.dump(t,indent) + local names = {} + if not indent then indent = '' end + for n,g in pairs(t) do + table.insert(names,n) + end + table.sort(names) + for i,n in pairs(names) do + local v = t[n] + if type(v) == 'table' then + if(v==t) then + print(indent..tostring(n)..': <-') + else + print(indent..tostring(n)..':') + table.dump(v,indent..' ') + end + else + if type(v) == 'function' then + print(indent..tostring(n)..'()') + else + print(indent..tostring(n)..': '..tostring(v)) + end + end + end +end +function table.alphanumsort(o) + local function padnum(d) local dec, n = string.match(d, '(%.?)0*(.+)') + return #dec > 0 and ('%.12f'):format(d) or ('%s%03d%s'):format(dec, #n, n) + end + table.sort(o, function(a,b) return tostring(a):gsub('%.?%d+',padnum)..('%3d'):format(#b)< tostring(b):gsub('%.?%d+',padnum)..('%3d'):format(#a) end) + return o +end +function table.foreach(t,f) + for i,v in pairs(t) do + f(v) + end +end +function table.merge(t1, t2) + for k,v in pairs(t2) do + if type(v) == 'table' then + if type(t1[k] or false) == 'table' then + table.merge(t1[k] or {}, t2[k] or {}) + else + t1[k] = v + end + else + t1[k] = v + end + end + return t1 +end +function table.print(tbl, indent) + if not indent then indent = 0 end + for k, v in pairs(tbl) do + formatting = string.rep(' ', indent) .. k .. ': ' + if type(v) == 'table' then + print(formatting) + table.print(v, indent+1) + else + print(formatting .. tostring(v)) + end + end +end +function table.merge(t1, t2) + for k,v in pairs(t2) do + if type(v) == 'table' then + if type(t1[k] or false) == 'table' then + table.merge(t1[k] or {}, t2[k] or {}) + else + t1[k] = v + end + else + t1[k] = v + end + end + return t1 +end +function table.clear(t) + for k in pairs (t) do + t[k] = nil + end +end +function table.copy(t) + function deepcopy(orig) + local orig_type = type(orig) + local copy + if orig_type == 'table' then + copy = {} + for orig_key, orig_value in next, orig, nil do + copy[deepcopy(orig_key)] = deepcopy(orig_value) + end + setmetatable(copy, deepcopy(getmetatable(orig))) + else -- number, string, boolean, etc + copy = orig + end + return copy + end + return deepcopy(t) +end +function table.swap(tab,i1,i2) + tab[i1],tab[i2]=tab[i2],tab[i1] +end +function table.append(t1, ...) + t1,t2= t1 or {},{...} + for k,v in pairs(t2) do + t1[#t1+1]=t2[k] + end + return t1 +end +function table.compare(t1, t2,d) + if d then + return table.deepCompare(t1,t2) + end + --if #t1 ~= #t2 then return false end + if #t2>#t1 then + for i=1,#t2 do + if t1[i] ~= t2[i] then + return false,t2[i] + end + end + else + for i=1,#t1 do + if t1[i] ~= t2[i] then + return false,t2[i] + end + end + end + return true +end +function table.deepCompare(t1,t2) + if t1==t2 then return true end + if (type(t1)~='table') then return false end + local mt1 = getmetatable(t1) + local mt2 = getmetatable(t2) + if( not table.deepCompare(mt1,mt2) ) then return false end + for k1,v1 in pairs(t1) do + local v2 = t2[k1] + if( not table.deepCompare(v1,v2) ) then return false end + end + for k2,v2 in pairs(t2) do + local v1 = t1[k2] + if( not table.deepCompare(v1,v2) ) then return false end + end + return true +end +function table.has(t,_v) + for i,v in pairs(t) do + if v==_v then + return true + end + end + return false +end +function table.reverse(tab) + local size = #tab + local newTable = {} + for i,v in ipairs (tab) do + newTable[size-i] = v + end + for i=1,#newTable do + tab[i]=newTable[i] + end +end +-- Math Additions +local Y = function(g) local a = function(f) return f(f) end return a(function(f) return g(function(x) local c=f(f) return c(x) end) end) end +local F = function(f) return function(n)if n == 0 then return 1 else return n*f(n-1) end end end +math.factorial = Y(F) +math.fib={} +math.fib.fibL={} +setmetatable(math.fib,{__call=function(self,n) + if n<=2 then + return 1 + else + if self.fibL[n] then + return self.fibL[n] + else + local t=math.fib(n-1)+math.fib(n-2) + self.fibL[n]=t + return t + end + end +end}) +local floor,insert = math.floor, table.insert +function math.basen(n,b) + n = floor(n) + if not b or b == 10 then return tostring(n) end + local digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' + local t = {} + local sign = '' + if n < 0 then + sign = '-' + n = -n + end + repeat + local d = (n % b) + 1 + n = floor(n / b) + insert(t, 1, digits:sub(d,d)) + until n == 0 + return sign .. table.concat(t,'') +end +function math.convbase(n,b,tb) + return math.basen(tonumber(tostring(n),b),tb) +end +if BigNum then + function BigNum.mod(a,b) + return a-((a/b)*b) + end + local floor,insert = math.floor, table.insert + function math.basen(n,b) + n = BigNum.new(n) + if not b or b == 10 then return tostring(n) end + local digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' + local t = {} + local sign = '' + if n < BigNum.new(0) then + sign = '-' + n = -n + end + repeat + local d = BigNum.mod(n , b) + 1 + n = n/b + d=tonumber(tostring(d)) + insert(t, 1, digits:sub(d,d)) + until tonumber(tostring(n)) == 0 + return sign .. table.concat(t,'') + end + function math.to10(n,b) + local num=tostring(n) + local sum=BigNum.new() + local digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' + for i=1,#num do + local v=digits:find(num:sub(i,i),1,true) + sum=sum+BigNum.new(tonumber(v)-1)*BigNum.pow(BigNum.new(b),BigNum.new(#num-i)) + end + return sum + end + function math.convbase(n,b,tb) + return math.basen(math.to10(n,b),tb) + end +end +function math.numfix(n,x) + local str=tostring(n) + if #str olleh +nil = binobj:flipbits() -- flips the binary bits +nil** = binobj:segment(a,b) -- gets a segment of the binobj data works just like string.sub(a,b) without str +nil* = binobj:insert(a,i) -- inserts i (string or number(converts into string)) in position a +nil* = binobj:parseN(n) -- removes ever (nth) byte of data +nil = binobj:getlength() -- gets length or size of binary data +nil* = binobj:shift(n) -- shift the binary data by n positive --> negitive <-- +nil* = binobj:delete(a,b) -- deletes part of a binobj data Usage: binobj:delete(#) deletes at pos # binobj:delete(#1,#2) deletes from #1 to #2 binobj:delete('string') deletes all instances of 'byte' as a string Use string.char(#) or '\#' to get byte as a string +nil* = binobj:encrypt(seed) -- encrypts data using a seed, seed may be left blank +nil* = binobj:decrypt(seed) -- decrypts data encrypted with encrypt(seed) +nil* = binobj:shuffle() -- Shuffles the data randomly Note: there is no way to get it back!!! If original is needed clone beforehand +nil** = binobj:mutate(a,i) -- changes position a's value to i +nil = binobj:merge(o,t) -- o is the binobj you are merging if t is true it merges the new data to the left of the binobj EX: b:merge(o,true) b='yo' o='data' output: b='datayo' b:merge(o) b='yo' o='data' output: b='yodata' +nil* = binobj:parseA(n,a,t) -- n is every byte where you add, a is the data you are adding, t is true or false true before false after +nil = binobj:getHEX(a,b) -- returns the HEX of the bytes between a,b inclusive +nil = binobj:cryptM() -- a mirrorable encryptor/decryptor +nil = binobj:addBlock(d,n) -- adds a block of data to a binobj s is size d is data e is a bool if true then encrypts string values. if data is larger than 'n' then data is lost. n is the size of bytes the data is Note: n is no longer needed but you must use getBlock(type) to get it back +nil = binobj:getBlock(t,n) -- gets block of code by type +nil = binobj:seek(n) -- used with getBlock EX below with all 3 +nil* = binobj:morph(a,b,d) -- changes data between point a and b, inclusive, to d +nil = binobj:fill(n,d) -- fills binobj with data 'd' for n +nil = binobj:fillrandom(n) -- fills binobj with random data for n +nil = binobj:shiftbits(n) -- shifts all bits by n amount +nil = binobj:shiftbit(n,i) -- shifts a bit ai index i by n +nil# = binobj:streamwrite(d,n) -- writes to the streamable binobj d data n position +nil# = binobj:open() -- opens the streamable binobj +nil# = binobj:close() -- closes the streamable binobj +nil = binobj:wipe() -- erases all data in the file +nil* = binobj:tackB(d) -- adds data to the beginning of a file +nil = binobj:tackE(d) -- adds data to the end of a file +nil = binobj:parse(n,f) -- loops through each byte calling function 'f' with the args(i,binobj,data at i) +nil = binobj:flipbit(i) -- flips the binary bit at position i +nil* = binobj:gsub() -- just like string:gsub(), but mutates self +nil = blockWriter:addNamedBlock(name,value) -- writes a named block to the file with name 'name' and the value 'value' + +Note: numbers are written in Big-endian use bin.endianflop(d) to filp to Little-endian + +Note: binobj:tonumber() returns big,little endian so if printing do: b,l=binobj:tonumber() print(l) print(b) + +nil = bitobj:add(i) -- adds i to the bitobj i can be a number (base 10) or a bitobj +nil = bitobj:sub(i) -- subs i to the bitobj i can be a number (base 10) or a bitobj +nil = bitobj:multi(i) -- multiplys i to the bitobj i can be a number (base 10) or a bitobj +nil = bitobj:div(i) -- divides i to the bitobj i can be a number (base 10) or a bitobj +nil = bitobj:flipbits() -- filps the bits 1 --> 0, 0 --> 1 +string = bitobj:getBin() -- returns 1's & 0's of the bitobj + +# stream objects only +* not compatible with stream files +** works but do not use with large files or it works to some degree +*** in stream objects all changes are made directly to the file, so there is no need to do tofile() +]] + +bin.Changelog=[[ +Version.Major.Minor +------------------------- +1.0.0 : initial release load/new/tofile/clone/closeto/compare/sub/reverse/flip/segment/insert/insert/parseN/getlength/shift +1.0.1 : update Delete/tonumber/getbyte/ +1.0.2 : update Changed how delete works. Added encrypt/decrypt/shuffle +1.0.3 : update Added bits class, Added in bin: tobit/mutate/parseA Added in bits: add/sub/multi/div/isover/tobyte/tonumber/flip +1.0.4 : update Changed tobyte() to tobytes()/flipbit() to flipbits() and it now returns a binobj not str Added bin:merge +1.0.5 : update Changed bin.new() now hex data can be inserted EX: bin.new('0xFFC353D') Added in bin: getHEX/cryptM/addBlock/getBlock/seek +1.0.6 : update Added bin.NumtoHEX/bin:getHEX/bin.HEXtoBin/bin.HEXtoStr/bin.tohex/bin.fromhex +1.0.7 : update Added bin:morph/bin.endianflop/bin:scan/bin.ToStr +1.0.8 : update Added bin:fill/bin:fillrandom +1.1.0 : update Added bin.packLLIB/bin.unpackLLIB +1.2.0 : update Updated llib files +1.3.0 : Update Changed bin.unpackLLIB and bin.load() Added: bin.fileExist +1.4.0 : Update Changed bin.unpackLLIB bin.packLLIB Added: bin:shiftbits(n) bin:shiftbit(n,i) + +Woot!!! Version 2 +2.0.0 HUGE UPDATE Added Streamable files!!! lua 5.1, 5.2 and 5.3 compatable!!! +#binobj is the same as binobj:getlength() but only works in 5.2 and 5.3, in 5.1 just use getlength() or getSize() for compatibility +Now you can work with gigabyte sized data without memory crashes(streamable files[WIP]). + +Stream Compatible methods: + sub(a,b) + getlength() + tofile(filename) + flipbits() + tonumber(a,b) + getbyte(n) + segment(a,b) + parse(n,f) + tobits(i) + reverse() + flipbit(i) + cryptM() + getBlock(t,n) + addBlock(d,n) + shiftbits(n) + shiftbit(n,i) + getHEX(a,b) + +Added functions in this version: + binobj:streamwrite(d,n) + binobj:open() + binobj:close() + binobj:tackB(d) + binobj:tackE(d) + binobj:parse(n,f) + binobj:flipbit(i) + bin.stream(file) + binobj:streamData(a,b) + bin.getVersion() + bin.escapeStr(str) + binobj:streamread(a,b) + binobj:canStreamWrite() + binobj:wipe() + +Woot!!! Version 3 +3.0.0 HUGE UPDATE!!! + Added: bin.newVFS() bin.loadVFS() bin.textToBinary(txt) bin.decodeBits(bindata) bitobj:getBin() + Updated: bin.addBlock() <-- Fixed error with added features to the bits.new() function that allow for new functions to work + Notice: The bin library now requires the utils library!!! Put utils.lua in the lua/ directory +3.1.0 + Added: bin.newTempFile(data) binobj:setEndOfFile(n) bin.randomName(n,ext) + Updated: bin:tackE() bin:fill() bin:fillrandom() are now stream compatible! + Notice: bin:setEndOfFile() only works on streamable files! +3.1.1 + Added: bin.trimNul(s) bin:gsub() +3.1.2 + Added: log(data,name,fmt) + In secret something is brewing... + +3.1.3 + Added: bin:getHash(n) +3.2.1 + Added: bin.encryptA(data,seed), bin.decryptA(data,seed), bin.encryptB(data,seed), bin.decryptB(data,seed), bin:flush() + Updated: bin:encrypt(seed) and bin:decrypt(seed) + Fixed: bin:shiftbit() not working right with streamable files +3.2.2 + Fixed: bits.new() -- minor mistake huge error +3.2.3 + General bug fixes + Changed how bin.ToStr(t) -- functions are no longer valid data types +3.3.0 + Added: + bin:getSize() -- same as bin:getlength() just makes more sense. bin:getlength() is still valid and always will be. + bin.newLink() -- creates a link to a file object... Its like opening a file without opening it... Lua can only open a maximum of 200 files so use links if you will be going beyond that or make sure to close your files + bin.getHash2(h,n) -- 'h' hash size 8bit,16bit,32bit,64bit, 128bit, 100000bit whatever. is a number 'n' is the segmentation size defualt is 1024 greater numbers result in faster hashes but eaiser to forge hashes +3.4.1:(7/22/2016) NOTE: I started to add dates so I can see my work flow + Added: + binobj:getData() -- returns bin object as a string + bin:newDataBuffer(s) + Fixed: binobj:tonumber(a,b) +4.0.0:(7/23/2016) + Added: + bin.bufferToBin(b) + bin.binToBuffer(b) + bin.getLuaVersion() + bin.newNamedBlock(indexSize) + bin.newStreamedNamedBlock(indexSize,path) + bin.loadNamedBlock(path) + bin.getIndexSize(tab) + bits.numToBytes(num,occ) +4.1.0:(11/2/2016) NOTE: I took quite a long break due to college lol + Added: + bin.namedBlockManager(name) + Allows for a new way to use NamedBlocks + Example usage: + test=bin.namedBlockManager() + test["name"]="Ryan" -- My name lol + test["age"]=21 -- my age lol + test:tofile("test.dat") + --Now lets load the data we created + test2=bin.namedBlockManager("test.dat") + print(test2["name"]) + print(test2["age"]) + Changed: + bin.newNamedBlock(indexSize) + Now allows for indexSize to be nil and dynamacally adds to the size of the index + Fixed: + bin.loadNamedBlock(name) + Issue with indexing + TODO: + Allow streamed files to have expanding indexes +]] +bin.data='' +bin.t='bin' +bin.__index = bin +bin.__tostring=function(self) return self.data end +bin.__len=function(self) return self:getlength() end +bits={} +bits.data='' +bits.t='bits' +bits.__index = bits +bits.__tostring=function(self) return self.data end +bits.__len=function(self) return (#self.data)/8 end +bin.lastBlockSize=0 +--[[---------------------------------------- +Links +------------------------------------------]] +function bin.newLink(path) + if not path then + error("Must include a path when using a link!") + end + local c={} + c.path=path + c.tempfile={} + local mt={ + __tostring=function(self) + if self:getlength()>2048 then + -- + end + end, + __len=function(self) + return self:getlength() + end + } + function c:getlength() + -- + end +end + + +--[[---------------------------------------- +utils +------------------------------------------]] +function cleanName(name) + name=name:gsub("\\","") + name=name:gsub("/","") + name=name:gsub(":","") + name=name:gsub("*","") + name=name:gsub("%?","") + name=name:gsub("\"","''") + name=name:gsub("<","") + name=name:gsub(">","") + name=name:gsub("|","") + return name +end +function math.numfix(n,x) + local str=tostring(n) + if #str/dev/null','r') + if fh then + osname = fh:read() + end + if osname then return osname end + end + local winver='Unknown Version' + local a,b,c=os.capture('ver'):match('(%d+).(%d+).(%d+)') + local win=a..'.'..b..'.'..c + if type(t)=='string' then + win=t + end + if win=='4.00.950' then + winver='95' + elseif win=='4.00.1111' then + winver='95 OSR2' + elseif win=='4.00.1381' then + winver='NT 4.0' + elseif win=='4.10.1998' then + winver='98' + elseif win=='4.10.2222' then + winver='98 SE' + elseif win=='4.90.3000' then + winver='ME' + elseif win=='5.00.2195' then + winver='2000' + elseif win=='5.1.2600' then + winver='XP' + elseif win=='5.2.3790' then + winver='Server 2003' + elseif win=='6.0.6000' then + winver='Vista/Windows Server 2008' + elseif win=='6.0.6002' then + winver='Vista SP2' + elseif win=='6.1.7600' then + winver='7/Windows Server 2008 R2' + elseif win=='6.1.7601' then + winver='7 SP1/Windows Server 2008 R2 SP1' + elseif win=='6.2.9200' then + winver='8/Windows Server 2012' + elseif win=='6.3.9600' then + winver='8.1/Windows Server 2012' + elseif win=='6.4.9841' then + winver='10 Technical Preview 1' + elseif win=='6.4.9860' then + winver='10 Technical Preview 2' + elseif win=='6.4.9879' then + winver='10 Technical Preview 3' + elseif win=='10.0.9926' then + winver='10 Technical Preview 4' + end + return 'Windows '..winver +end +function os.capture(cmd, raw) + local f = assert(io.popen(cmd, 'r')) + local s = assert(f:read('*a')) + f:close() + if raw then return s end + s = string.gsub(s, '^%s+', '') + s = string.gsub(s, '%s+$', '') + s = string.gsub(s, '[\n\r]+', ' ') + return s +end +function io.scanDir(directory) + directory=directory or io.getDir() + local i, t, popen = 0, {}, io.popen + if os.getOS()=='unix' then + for filename in popen('ls -a "'..directory..'"'):lines() do + i = i + 1 + t[i] = filename + end + else + for filename in popen('dir "'..directory..'" /b'):lines() do + i = i + 1 + t[i] = filename + end + end + return t +end +function io.getDir(dir) + if not dir then return io.getWorkingDir() end + if os.getOS()=='unix' then + return os.capture('cd '..dir..' ; cd') + else + return os.capture('cd '..dir..' & cd') + end +end +function string.split(str, pat) + local t = {} -- NOTE: use {n = 0} in Lua-5.0 + local fpat = '(.-)' .. pat + local last_end = 1 + local s, e, cap = str:find(fpat, 1) + while s do + if s ~= 1 or cap ~= '' then + table.insert(t,cap) + end + last_end = e+1 + s, e, cap = str:find(fpat, last_end) + end + if last_end <= #str then + cap = str:sub(last_end) + table.insert(t, cap) + end + return t +end +function io.fileExists(path) + g=io.open(path or '','r') + if path =='' then + p='empty path' + return nil + end + if g~=nil and true or false then + p=(g~=nil and true or false) + end + if g~=nil then + io.close(g) + else + return false + end + return p +end +function io.getDirectories(dir,l) + if dir then + dir=dir..'\\' + else + dir='' + end + local temp2=io.scanDir(dir) + for i=#temp2,1,-1 do + if io.fileExists(dir..temp2[i]) then + table.remove(temp2,i) + elseif l then + temp2[i]=dir..temp2[i] + end + end + return temp2 +end +function io.getFiles(dir,l) + if dir then + dir=dir..'\\' + else + dir='' + end + local temp2=io.scanDir(dir) + for i=#temp2,1,-1 do + if io.dirExists(dir..temp2[i]) then + table.remove(temp2,i) + elseif l then + temp2[i]=dir..temp2[i] + end + end + return temp2 +end +function io.readFile(file) + local f = io.open(file, 'rb') + local content = f:read('*all') + f:close() + return content +end +function table.print(tbl, indent) + if not indent then indent = 0 end + for k, v in pairs(tbl) do + formatting = string.rep(' ', indent) .. k .. ': ' + if type(v) == 'table' then + print(formatting) + table.print(v, indent+1) + else + print(formatting .. tostring(v)) + end + end +end +function io.dirExists(strFolderName) + strFolderName = strFolderName or io.getDir() + local fileHandle, strError = io.open(strFolderName..'\\*.*','r') + if fileHandle ~= nil then + io.close(fileHandle) + return true + else + if string.match(strError,'No such file or directory') then + return false + else + return true + end + end +end +function io.getFullName(name) + local temp=name or arg[0] + if string.find(temp,'\\',1,true) or string.find(temp,'/',1,true) then + temp=string.reverse(temp) + a,b=string.find(temp,'\\',1,true) + if not(a) or not(b) then + a,b=string.find(temp,'/',1,true) + end + return string.reverse(string.sub(temp,1,b-1)) + end + return temp +end +function table.merge(t1, t2) + for k,v in pairs(t2) do + if type(v) == 'table' then + if type(t1[k] or false) == 'table' then + table.merge(t1[k] or {}, t2[k] or {}) + else + t1[k] = v + end + else + t1[k] = v + end + end + return t1 +end +function io.splitPath(str) + return string.split(str,'[\\/]+') +end +function io.pathToTable(path) + local p=io.splitPath(path) + local temp={} + temp[p[1]]={} + local last=temp[p[1]] + for i=2,#p do + snd=last + last[p[i]]={} + last=last[p[i]] + end + return temp,last,snd +end +function io.parseDir(dir,t) + io.tempFiles={} + function _p(dir) + local dirs=io.getDirectories(dir,true) + local files=io.getFiles(dir,true) + for i=1,#files do + p,l,s=io.pathToTable(files[i]) + if t then + s[io.getFullName(files[i])]=io.readFile(files[i]) + else + s[io.getFullName(files[i])]=io.open(files[i],'r+') + end + table.merge(io.tempFiles,p) + end + for i=1,#dirs do + table.merge(io.tempFiles,io.pathToTable(dirs[i])) + _p(dirs[i],t) + end + end + _p(dir) + return io.tempFiles +end +function io.parsedir(dir,f) + io.tempFiles={} + function _p(dir,f) + local dirs=io.getDirectories(dir,true) + local files=io.getFiles(dir,true) + for i=1,#files do + if not f then + table.insert(io.tempFiles,files[i]) + else + f(files[i]) + end + end + for i=1,#dirs do + _p(dirs[i],f) + end + end + _p(dir,f) + return io.tempFiles +end +--[[---------------------------------------- +Random +Not all of this is mine +------------------------------------------]] +--[[------------------------------------ +RandomLua v0.3.1 +Pure Lua Pseudo-Random Numbers Generator +Under the MIT license. +copyright(c) 2011 linux-man +--]]------------------------------------ + +local math_floor = math.floor + +local function normalize(n) --keep numbers at (positive) 32 bits + return n % 0x80000000 +end + +local function bit_and(a, b) + local r = 0 + local m = 0 + for m = 0, 31 do + if (a % 2 == 1) and (b % 2 == 1) then r = r + 2^m end + if a % 2 ~= 0 then a = a - 1 end + if b % 2 ~= 0 then b = b - 1 end + a = a / 2 b = b / 2 + end + return normalize(r) +end + +local function bit_or(a, b) + local r = 0 + local m = 0 + for m = 0, 31 do + if (a % 2 == 1) or (b % 2 == 1) then r = r + 2^m end + if a % 2 ~= 0 then a = a - 1 end + if b % 2 ~= 0 then b = b - 1 end + a = a / 2 b = b / 2 + end + return normalize(r) +end + +local function bit_xor(a, b) + local r = 0 + local m = 0 + for m = 0, 31 do + if a % 2 ~= b % 2 then r = r + 2^m end + if a % 2 ~= 0 then a = a - 1 end + if b % 2 ~= 0 then b = b - 1 end + a = a / 2 b = b / 2 + end + return normalize(r) +end + +local function seed() + --return normalize(tonumber(tostring(os.time()):reverse())) + return normalize(os.time()) +end + +--Mersenne twister +mersenne_twister = {} +mersenne_twister.__index = mersenne_twister + +function mersenne_twister:randomseed(s) + if not s then s = seed() end + self.mt[0] = normalize(s) + for i = 1, 623 do + self.mt[i] = normalize(0x6c078965 * bit_xor(self.mt[i-1], math_floor(self.mt[i-1] / 0x40000000)) + i) + end +end + +function mersenne_twister:random(a, b) + local y + if self.index == 0 then + for i = 0, 623 do + --y = bit_or(math_floor(self.mt[i] / 0x80000000) * 0x80000000, self.mt[(i + 1) % 624] % 0x80000000) + y = self.mt[(i + 1) % 624] % 0x80000000 + self.mt[i] = bit_xor(self.mt[(i + 397) % 624], math_floor(y / 2)) + if y % 2 ~= 0 then self.mt[i] = bit_xor(self.mt[i], 0x9908b0df) end + end + end + y = self.mt[self.index] + y = bit_xor(y, math_floor(y / 0x800)) + y = bit_xor(y, bit_and(normalize(y * 0x80), 0x9d2c5680)) + y = bit_xor(y, bit_and(normalize(y * 0x8000), 0xefc60000)) + y = bit_xor(y, math_floor(y / 0x40000)) + self.index = (self.index + 1) % 624 + if not a then return y / 0x80000000 + elseif not b then + if a == 0 then return y + else return 1 + (y % a) + end + else + return a + (y % (b - a + 1)) + end +end + +function twister(s) + local temp = {} + setmetatable(temp, mersenne_twister) + temp.mt = {} + temp.index = 0 + temp:randomseed(s) + return temp +end + +--Linear Congruential Generator +linear_congruential_generator = {} +linear_congruential_generator.__index = linear_congruential_generator + +function linear_congruential_generator:random(a, b) + local y = (self.a * self.x + self.c) % self.m + self.x = y + if not a then return y / 0x10000 + elseif not b then + if a == 0 then return y + else return 1 + (y % a) end + else + return a + (y % (b - a + 1)) + end +end + +function linear_congruential_generator:randomseed(s) + if not s then s = seed() end + self.x = normalize(s) +end + +function lcg(s, r) + local temp = {} + setmetatable(temp, linear_congruential_generator) + temp.a, temp.c, temp.m = 1103515245, 12345, 0x10000 --from Ansi C + if r then + if r == 'nr' then temp.a, temp.c, temp.m = 1664525, 1013904223, 0x10000 --from Numerical Recipes. + elseif r == 'mvc' then temp.a, temp.c, temp.m = 214013, 2531011, 0x10000 end--from MVC + end + temp:randomseed(s) + return temp +end + +-- Multiply-with-carry +multiply_with_carry = {} +multiply_with_carry.__index = multiply_with_carry + +function multiply_with_carry:random(a, b) + local m = self.m + local t = self.a * self.x + self.c + local y = t % m + self.x = y + self.c = math_floor(t / m) + if not a then return y / 0x10000 + elseif not b then + if a == 0 then return y + else return 1 + (y % a) end + else + return a + (y % (b - a + 1)) + end +end + +function multiply_with_carry:randomseed(s) + if not s then s = seed() end + self.c = self.ic + self.x = normalize(s) +end + +function mwc(s, r) + local temp = {} + setmetatable(temp, multiply_with_carry) + temp.a, temp.c, temp.m = 1103515245, 12345, 0x10000 --from Ansi C + if r then + if r == 'nr' then temp.a, temp.c, temp.m = 1664525, 1013904223, 0x10000 --from Numerical Recipes. + elseif r == 'mvc' then temp.a, temp.c, temp.m = 214013, 2531011, 0x10000 end--from MVC + end + temp.ic = temp.c + temp:randomseed(s) + return temp +end +-- Little bind for the methods: My code starts +randomGen={} +randomGen.__index=randomGen +function randomGen:new(s) + local temp={} + setmetatable(temp,randomGen) + temp[1]=twister() + temp[2]=lcg() + temp[3]=mwc() + temp.pos=1 + for i=1,3 do + temp[i]:randomseed(s) + end + return temp +end +function randomGen:randomseed(s) + self.pos=1 + self[1]:randomseed(s) + self[2]:randomseed(s) + self[3]:randomseed(s) +end +function randomGen:randomInt(a,b) + local t=self[self.pos]:random(a,b) + self.pos=self.pos+1 + if self.pos>3 then + self.pos=1 + end + return t +end +function randomGen:newND(a,b,s) + if not(a) or not(b) then error('You must include a range!') end + local temp=randomGen:new(s) + temp.a=a + temp.b=b + temp.range=b-a+1 + temp.dups={no=0} + function temp:nextInt() + local t=self:randomInt(self.a,self.b) + if self.dups[t]==nil then + self.dups[t]=true + self.dups.no=self.dups.no+1 + else + return self:nextInt() + end + if self.dups.no==self.range then + function self:nextInt() + return 1,true + end + return t + else + return t + end + end + function temp:nextIInt() + return function() return self:nextInt() end + end + return temp +end +--[[---------------------------------------- +BIN +------------------------------------------]] +function bin:getSize() + return self:getlength() +end +function bin.getVersion() + return bin.Version[1]..'.'..bin.Version[2]..'.'..bin.Version[3] +end +function bin:gsub(...) + self.data=self.data:gsub(...) +end +-- +function bin:trim() + self.data=self.data:match'^()%s*$' and '' or self.data:match'^%s*(.*%S)' +end +function bin._trim(str) + return str:match'^()%s*$' and '' or str:match'^%s*(.*%S)' +end +function bin:fullTrim(empty) + local t=self:lines() + for i=#t,1,-1 do + t[i]=bin._trim(t[i]) + if empty then + if t[i]=="" then + table.remove(t,i) + end + end + end + self.data = table.concat(t,"\n") +end +function bin:lines() + local t = {} + local function helper(line) table.insert(t, line) return '' end + helper((self.data:gsub('(.-)\r?\n', helper))) + return t +end +function bin._lines(str) + local t = {} + local function helper(line) table.insert(t, line) return '' end + helper((str:gsub('(.-)\r?\n', helper))) + return t +end +-- +function bin:find(...) + return self.data:find(...) +end +function bin.fromhex(str) + print(str) + return (str:gsub('..', function (cc) + return string.char(tonumber(cc, 16)) + end)) +end +if table.unpack==nil then + table.unpack=unpack +end +function bin.tohex(str) + return (str:gsub('.', function (c) + return string.format('%02X', string.byte(c)) + end)) +end +function bin:streamData(a,b) + if type(a)=='table' then + a,b,t=table.unpack(a) + end + if type(a)=='number' and type(b)=='string' then + return bin.load(self.file,a,b),bin.load(self.file,a,b).data + else + error('Invalid args!!! Is do you have a valid stream handle or is this a streamable object?') + end +end +function bin.new(data,hex) + data=data or "" + data=tostring(data) + local c = {} + setmetatable(c, bin) + if string.sub(data,1,2)=='0x' and hex==true then + data=string.sub(data,3) + data=bin.fromhex(data) + end + c.data=data + c.t='bin' + c.Stream=false + return c +end +function bin.stream(file,l) + local c=bin.new() + if bin.fileExist(file) then + c.file=file + c.lock = l + c.workingfile=io.open(file,'rb+') + else + c.file=file + c.lock = l + c.workingfile=io.open(file,'w') + io.close(c.workingfile) + c.workingfile=io.open(file,'rb+') + end + c.Stream=true + return c +end +function bin:streamwrite(d,n) + if self:canStreamWrite() then + if n then + self.workingfile:seek('set',n) + else + self.workingfile:seek('set',self.workingfile:seek('end')) + end + self.workingfile:write(d) + end +end +function bin:streamread(a,b) + a=tonumber(a) + b=tostring(b) + return bin.load(self.file,a,b).data +end +function bin:close() + if self:canStreamWrite() then + self.workingfile:close() + end +end +function bin:flush() + if self:canStreamWrite() then + self.workingfile:flush() + else + self:tofile(self.filepath) + end +end +function bin:open() + if self:canStreamWrite() then + self.workingfile=io.open(self.file,'r+') + end +end +function bin:canStreamWrite() + return (self.Stream==true and self.lock==false) +end +function bin.load(file,s,r) + if not(s) or not(r) then + local f = io.open(file, 'rb') + local content = f:read('*a') + f:close() + return bin.new(content) + end + s=s or 0 + r=r or -1 + if type(r)=='number' then + r=r+s-1 + elseif type(r)=='string' then + r=tonumber(r) or -1 + end + local f = io.open(file, 'rb') + f:seek('set',s) + local content = f:read((r+1)-s) + f:close() + local temp=bin.new(content) + temp.filepath=file + return temp +end +function bin:tofile(filename) + if not(filename) or self.Stream then return nil end + io.mkFile(filename,self.data) +end +function bin.trimNul(s) + for i=1,#s do + if s:sub(i,i)=='\0' then + return s:sub(1,i-1) + end + end + return s +end +function bin:match(pat) + return self.data:match(pat) +end +function bin:gmatch(pat) + return self.data:gmatch(pat) +end +function bin:getHash(n) + if self:getlength()==0 then + return "NaN" + end + n=(n or 32)/2 + local rand = randomGen:newND(1,self:getlength(),self:getlength()) + local h,g={},0 + for i=1,n do + g=rand:nextInt() + table.insert(h,bin.tohex(self:sub(g,g))) + end + return table.concat(h,'') +end +function bin:newDataBuffer(s) + local c={} + local mt={ + __index=function(t,k,v) + if k<=t.maxBuffer then + if t.dataS[k] then + return string.byte(t.dataS[k]) + else + return "NOINDEX" + end + else + return + end + end, + __newindex=function(t,k,v) + t.dataS[k]=string.char(v) + end, + __tostring=function(t) + return t:getBuffer() + end + } + c.dataS={} + if s then + if type(s)=="number" then + c.maxBuffer=s + s=string.rep("\0",s) + else + c.maxBuffer=math.huge + end + for i=1,#s do + c.dataS[i]=s:sub(i,i) + end + else + c.maxBuffer=math.huge + end + function c:getBuffer(a,b) + if a and b then + return table.concat(self.dataS,""):sub(a,b) + else + return table.concat(self.dataS,"") + end + end + function c:getBufferTable() + return self.dataS + end + function c:getBufferSize() + if self.maxBuffer~=math.huge then + return self.maxBuffer + end + return #self:getBuffer() + end + function c:getlength() + return #self:getBuffer(a,b) + end + c.getSize=c.getlength + function c:fillBuffer(s,a) + for i=0,#s-1 do + if i+a<=self.maxBuffer then + c.dataS[i+a]=s:sub(i+1,i+1) + else + return "Buffer Overflow!" + end + end + return a,a+#s-1 + end + setmetatable(c,mt) + return c +end +function bin:getHash2(h,n) + n=n or 1024 + h=(h or 32) + local temp=bin.new() + local len=self:getSize() + local seg=math.ceil(len/n) + temp:fill("\0",h) + for i=1,seg do + local s=bin.new(self:sub(n*(i-1)+1,n*i)):getHash(h) + for i=1,h do + temp:shiftbit(string.byte(s:sub(i,i)),i) + end + end + return temp:getHEX() +end +function bin.encryptA(data,seed) + seed=seed or 1 + local d=bin.new(data) + local r=randomGen:newND(1,#d.data,seed) + for i=1,#d.data do + d:shiftbit(r:nextInt(),i) + end + return bin.tohex(d.data) +end +function bin.decryptA(data,seed) + seed=seed or 1 + local d=bin.new('0x'..data) + local r=randomGen:newND(1,#d.data,seed) + for i=1,#d.data do + d:shiftbit(-r:nextInt(),i) + end + return d.data +end +function bin.encryptB(data,seed) + seed=seed or 'abcdefghijklmnopqrstuvwxyz' + seed=tostring(seed) + local d=bin.new(data) + local r,mr=1,#seed + for i=1,#d.data do + d:shiftbit(string.byte(seed:sub(r,r)),i) + r=r+1 + if r>mr then + r=1 + end + end + return bin.tohex(d.data) +end +function bin.decryptB(data,seed) + seed=seed or 'abcdefghijklmnopqrstuvwxyz' + seed=tostring(seed) + local d=bin.new('0x'..data) + local r,mr=1,#seed + for i=1,#d.data do + d:shiftbit(-string.byte(seed:sub(r,r)),i) + r=r+1 + if r>mr then + r=1 + end + end + return d.data +end +function bin:encrypt(seed) + seed=seed or 'abcdefghijklmnopqrstuvwxyz' + seed=tostring(seed) + local r,mr=1,#seed + for i=1,self:getlength() do + self:shiftbit(string.byte(seed:sub(r,r)),i) + r=r+1 + if r>mr then + r=1 + end + end +end +function bin:decrypt(seed) + seed=seed or 'abcdefghijklmnopqrstuvwxyz' + seed=tostring(seed) + local r,mr=1,#seed + for i=1,self:getlength() do + self:shiftbit(-string.byte(seed:sub(r,r)),i) + r=r+1 + if r>mr then + r=1 + end + end +end +function bin.randomName(n,ext) + n=n or math.random(7,15) + if ext then + a,b=ext:find('.',1,true) + if a and b then + ext=ext:sub(2) + end + end + local str,h = '',0 + strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'} + for i=1,n do + h = math.random(1,#strings) + str = str..''..strings[h] + end + return str..'.'..(ext or 'tmp') +end +function bin.newTempFile(data) + data=data or '' + local name=bin.randomName() + bin.new():tofile(name) + local tempfile=bin.stream(name,false) + tempfile:streamwrite(data,0) + tempfile:setEndOfFile(#data) + return tempfile +end +function bin:wipe() + if self:canStreamWrite() then + self:close() + os.remove(self.file) + self:open() + else + self.data='' + end +end +function bin:setEndOfFile(n) + if self:canStreamWrite() then + local name=bin.randomName() + local tempfile=bin.stream(name,false) + tempfile:streamwrite(self:sub(0,n-1)) + self:close() + os.remove(self.file) + tempfile:close() + os.rename(name,self.file) + self:open() + tempfile=nil + else + self.data=self.data:sub(1,n) + end +end +function bin:reverse() + if self:canStreamWrite() then + local x,f,b=self:getlength(),0,0 + for i=0,math.floor((x-1)/2) do + self:streamwrite(self:sub(i+1,i+1),x-i-1) + self:streamwrite(self:sub(x-i,x-i),i) + end + elseif self.Stream==false then + self.data=string.reverse(self.data) + end +end +function bin:flipbits() + if self:canStreamWrite() then + for i=0,self:getlength()-1 do + self:streamwrite(string.char(255-string.byte(self:streamread(i,i))),i) + end + elseif self.Stream==false then + local temp={} + for i=1,#self.data do + table.insert(temp,string.char(255-string.byte(string.sub(self.data,i,i)))) + end + self.data=table.concat(temp,'') + end +end +function bin:flipbit(i) + if self:canStreamWrite() then + self:streamwrite(string.char(255-string.byte(self:streamread(i-1,i-1))),i-1) + elseif self.Stream==false then + self:mutate(string.char(255-string.byte(string.sub(self.data,i,i))),i) + end +end +function bin:segment(a,b) -- needs to be updated!!! + if self:canStreamWrite() then + --[[local pos=1 + for i=a,b do + self:streamwrite(self:sub(i,i),b-a-i) + end]] + local temp=self:sub(a,b) + self:close() + local f=io.open(self.file,'w') + f:write(temp) + io.close(f) + self:open() + elseif self.Stream==false then + self.data=string.sub(self.data,a,b) + end +end +function bin:insert(i,a) + if self:canStreamWrite() then + -- do something + elseif self.Stream==false then + if type(i)=='number' then i=string.char(i) end + self.data=string.sub(self.data,1,a)..i..string.sub(self.data,a+1) + end +end +function bin:parseN(n) + if self:canStreamWrite() then + -- do something + elseif self.Stream==false then + local temp={} + for i=1,#self.data do + if i%n==0 then + table.insert(temp,string.sub(self.data,i,i)) + end + end + self.data=table.concat(temp,'') + end +end +function bin:parse(n,f) + local f = f + local n=n or 1 + if not(f) then return end + for i=1,self:getlength() do + if i%n==0 then + f(i,self,self:sub(i,i)) + end + end +end +function bin.copy(file,tofile,s) + if not(s) then + bin.load(file):tofile(tofile) + else + rf=bin.stream(file) + wf=bin.stream(tofile,false) + for i=1,rf:getlength(),s do + wf:streamwrite(rf:sub(i,i-1+s)) + end + end +end +function bin:getlength() + if self.Stream then + if self.workingfile==nil then print("Error getting size of file!") return 0 end + local current = self.workingfile:seek() -- get current position + local size = self.workingfile:seek('end') -- get file size + self.workingfile:seek('set', current) -- restore position + return size + elseif self.Stream==false then + return #self.data + end +end +function bin:sub(a,b) + if self.Stream then + return bin.load(self.file,a-1,tostring(b-1)).data + elseif self.Stream==false then + return string.sub(self.data,a,b) + end +end +function bin:tackB(d) + if self:canStreamWrite() then + -- do something don't know if possible + elseif self.Stream==false then + self.data=d..self.data + end +end +function bin:tackE(d) + if type(d)=='table' then + if d:canStreamWrite() then + d=d:sub(1,d:getlength()) + else + d=d.data + end + return + end + if self:canStreamWrite() then + self:streamwrite(d) + elseif self.Stream==false then + self.data=self.data..d + end +end +function bin:clone(filename) + if self:canStreamWrite() then + -- do something + elseif self.Stream==false then + return bin.new(self.data) + end +end +function bin.closeto(a,b,v) + if self:canStreamWrite() then + -- do something + elseif self.Stream==false then + if type(a)~=type(b) then + error('Attempt to compare unlike types') + elseif type(a)=='number' and type(b)=='number' then + return math.abs(a-b)<=v + elseif type(a)=='table' and type(b)=='table' then + if a.data and b.data then + return (math.abs(string.byte(a.data)-string.byte(b.data)))<=v + else + error('Attempt to compare non-bin data') + end + elseif type(a)=='string' and type(b)=='string' then + return math.abs(string.byte(a)-string.byte(b))<=v + else + error('Attempt to compare non-bin data') + end + end +end +function bin:compare(_bin,t) + if self:canStreamWrite() then + -- do something + elseif self.Stream==false then + t=t or 1 + local tab={} + local a,b=self:getlength(),_bin:getlength() + if not(a==b) then + print('Unequal Lengths!!! Equalizing...') + if a>b then + _bin.data=_bin.data..string.rep(string.char(0),a-b) + else + self.data=self.data..string.rep(string.char(0),b-a) + end + end + if t==1 then + for i=1,self:getlength() do + table.insert(tab,self:sub(i,i)==_bin:sub(i,i)) + end + else + for i=1,self:getlength() do + table.insert(tab,bin.closeto(self:sub(i,i),_bin:sub(i,i),t)) + end + end + local temp=0 + for i=1,#tab do + if tab[i]==true then + temp=temp+1 + end + end + return (temp/#tab)*100 + end +end +function bin:shift(n) + if self:canStreamWrite() then + local a,b,x,p='','',self:getlength(),0 + for i=1,x do + if i+n>x then + p=(i+n)-(x) + else + p=i+n + end + end + elseif self.Stream==false then + n=n or 0 + local s=#self.data + if n>0 then + self.data = string.sub(self.data,s-n+1)..string.sub(self.data,1,s-n) + elseif n<0 then + n=math.abs(n) + self.data = string.sub(self.data,n+1)..string.sub(self.data,1,n*1) + end + end +end +function bin:delete(a,b) + if self:canStreamWrite() then + -- do something + elseif self.Stream==false then + if type(a)=='string' then + local tab={} + for i=1,self:getlength() do + if self:getbyte(i)~=string.byte(a) then + table.insert(tab,self:sub(i,i)) + end + end + self.data=table.concat(tab) + elseif a and not(b) then + self.data=self:sub(1,a-1)..self:sub(a+1) + elseif a and b then + self.data=self:sub(1,a-1)..self:sub(b+1) + else + self.data='' + end + end +end +function bin:tonumber(a,b) + local temp={} + if a then + temp.data=self:sub(a,b) + else + temp=self + end + local l,r=0,0 + local g=#temp.data + for i=1,g do + r=r+(256^(g-i))*string.byte(string.sub(temp.data,i,i)) + l=l+(256^(i-1))*string.byte(string.sub(temp.data,i,i)) + end + return r,l +end +function bin:getbyte(n) + return string.byte(self:sub(n,n)) +end +function bin:shuffle(s) + if self:canStreamWrite() then + -- do something + elseif self.Stream==false then + s=tonumber(s) or 4546 + math.randomseed(s) + local t={} + for i=1,self:getlength() do + table.insert(t,self:sub(i,i)) + end + local n = #t + while n >= 2 do + local k = math.random(n) + t[n], t[k] = t[k], t[n] + n = n - 1 + end + self.data=table.concat(t) + end +end +function bin:tobits(i) + return bits.new(self:getbyte(i)) +end +function bin:mutate(a,i) + if self:canStreamWrite() then + self:streamwrite(a,i-1) + elseif self.Stream==false then + self:delete(i) + self:insert(a,i-1) + end +end +function bin:parseA(n,a,t) + if self:canStreamWrite() then + -- do something + elseif self.Stream==false then + local temp={} + for i=1,#self.data do + if i%n==0 then + if t then + table.insert(temp,a) + table.insert(temp,string.sub(self.data,i,i)) + else + table.insert(temp,string.sub(self.data,i,i)) + table.insert(temp,a) + end + else + table.insert(temp,string.sub(self.data,i,i)) + end + end + self.data=table.concat(temp,'') + end +end +function bin:merge(o,t) + if self:canStreamWrite() then + self:close() + self.workingfile=io.open(self.file,'a+') + self.workingfile:write(o.data) + self:close() + self:open() + elseif self.Stream==false then + if not(t) then + self.data=self.data..o.data + else + seld.data=o.data..self.data + end + end +end +function bin:cryptM() + self:flipbits() + self:reverse() +end +function bin.escapeStr(str) + local temp='' + for i=1,#str do + temp=temp..'\\'..string.byte(string.sub(str,i,i)) + end + return temp +end + +function bin.ToStr(val, name, skipnewlines, depth) + skipnewlines = skipnewlines or false + depth = depth or 0 + local tmp = string.rep(" ", depth) + if name then + if type(name) == "string" then + tmp = tmp .. "[\""..name.."\"] = " + else + tmp = tmp .. "["..(name or "").."] = " + end + end + if type(val) == "table" then + tmp = tmp .. "{" .. (not skipnewlines and " " or "") + for k, v in pairs(val) do + tmp = tmp .. bin.ToStr(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and " " or "") + end + tmp = tmp .. string.rep(" ", depth) .. "}" + elseif type(val) == "number" then + tmp = tmp .. tostring(val) + elseif type(val) == "string" then + tmp = tmp .. string.format("%q", val) + elseif type(val) == "boolean" then + tmp = tmp .. (val and "true" or "false") + else + tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\"" + end + return tmp +end +function bin:addBlock(d,n,e) + local temp={} + if type(d)=='table' then + if d.t=='bin' then + temp=d + elseif d.t=='bit' then + temp=bin.new(d:tobytes()) + else + self:addBlock('return '..bin.ToStr(d)) + return + end + elseif type(d)=='string' then + temp=bin.new(d) + if e or not(n) then + temp.data=temp.data..'_EOF' + temp:flipbits() + end + elseif type(d)=='function' then + temp=bin.new(string.dump(d)) + if e or not(n) then + temp.data=temp.data..'_EOF' + temp:flipbits() + end + elseif type(d)=='number' then + local nn=tostring(d) + if nn:find('.',1,true) then + temp=bin.new(nn) + temp.data=temp.data..'_EOF' + temp:flipbits() + else + temp=bits.new(d):tobytes() + if not n then + temp.data=temp.data..'_EOF' + temp:flipbits() + end + end + elseif type(d)=='boolean' then + n=1 + if d then + temp=bits.new(math.random(0,127)):tobytes() + else + temp=bits.new(math.random(128,255)):tobytes() + end + end + if n then + if temp:getlength()n then + temp:segment(1,n) + end + end + self:merge(temp) +end +function bin:getBlock(t,n,se) + if not(self.Block) then + self.Block=1 + end + local x=self.Block + local temp=bin.new() + if n then + temp=bin.new(self:sub(x,x+n-1)) + self.Block=self.Block+n + end + if se then + self.Block=self.Block+se + end + if t=='stringe' or t=='stre' or t=='se' and n then + temp:flipbits() + bin.lastBlockSize=#temp + return temp.data + elseif t=='string' or t=='str' or t=='s' and n then + bin.lastBlockSize=#temp + return temp.data + elseif t=='number' or t=='num' or t=='n' and n then + bin.lastBlockSize=n + return self:tonumber(x,x+n-1) + elseif t=='boolean' or t=='bool' or t=='b' then + self.Block=self.Block+1 + bin.lastBlockSize=1 + return self:tonumber(x,x)<127 + elseif t=='stringe' or t=='stre' or t=='se' or t=='string' or t=='str' or t=='s' then + local a,b=self:scan('_EOF',self.Block,true) + if not(b) then return nil end + local t=bin.new(self:sub(self.Block,b-4)) + bin.lastBlockSize=t:getlength() + t:flipbits() + self.Block=self.Block+t:getlength()+4 + return tostring(t) + elseif t=='table' or t=='tab' or t=='t' then + temp=self:getBlock('s') + bin.lastBlockSize=#temp + return assert(loadstring(temp))() + elseif t=='function' or t=='func' or t=='f' then + local temp=self:getBlock('s') + bin.lastBlockSize=#temp + return assert(loadstring(temp)) + elseif t=='number' or t=='num' or t=='n' then + local num=bin.new(self:getBlock('s')) + bin.lastBlockSize=#num + if tonumber(num.data) then + return tonumber(num.data) + end + local a,b=num:tonumber() + return a + elseif n then + -- C data + else + print('Invalid Args!!!') + end +end +function bin:seek(n) + self.Block=self.Block+n +end +function bin.NumtoHEX(num) + local hexstr = '0123456789ABCDEF' + local s = '' + while num > 0 do + local mod = math.fmod(num, 16) + s = string.sub(hexstr, mod+1, mod+1) .. s + num = math.floor(num / 16) + end + if s == '' then + s = '0' + end + return s +end +function bin:getHEX(a,b,e) + a=a or 1 + local temp = self:sub(a,b) + if e then temp=string.reverse(temp) end + return bin.tohex(temp) +end +function bin.HEXtoBin(hex,e) + if e then + return bin.new(string.reverse(bin.fromhex(hex))) + else + return bin.new(bin.fromhex(hex)) + end +end +function bin.HEXtoStr(hex,e) + if e then + return string.reverse(bin.fromhex(hex)) + else + return bin.fromhex(hex) + end +end +function bin:morph(a,b,d) + if self:canStreamWrite() then + local len=self:getlength() + local temp=bin.newTempFile(self:sub(b+1,self:getlength())) + self:streamwrite(d,a-1) + print(temp:sub(1,temp:getlength())) + self:setEndOfFile(len+(b-a)+#d) + self:streamwrite(temp:sub(1,temp:getlength()),a-1) + temp:remove() + elseif self.Stream==false then + if a and b then + self.data=self:sub(1,a-1)..d..self:sub(b+1) + else + print('error both arguments must be numbers and the third a string') + end + end +end +function bin.endianflop(data) + return string.reverse(data) +end +function bin:scan(s,n,f) + n=n or 1 + if self.Stream then + for i=n,self:getlength() do + if f then + local temp=bin.new(self:sub(i,i+#s-1)) + temp:flipbits() + if temp.data==s then + return i,i+#s-1 + end + else + if self:sub(i,i+#s-1)==s then + return i,i+#s-1 + end + end + end + elseif self.Stream==false then + if f then + s=bin.new(s) + s:flipbits() + s=s.data + end + n=n or 1 + local a,b=string.find(self.data,s,n,true) + return a,b + end +end +function bin:fill(s,n) + if self:canStreamWrite() then + self:streamwrite(string.rep(s,n),0) + self:setEndOfFile(n*#s) + elseif self.Stream==false then + self.data=string.rep(s,n) + end +end +function bin:fillrandom(n) + if self:canStreamWrite() then + local t={} + for i=1,n do + table.insert(t,string.char(math.random(0,255))) + end + self:streamwrite(table.concat(t),0) + self:setEndOfFile(n) + elseif self.Stream==false then + local t={} + for i=1,n do + table.insert(t,string.char(math.random(0,255))) + end + self.data=table.concat(t) + end +end +function bin.packLLIB(name,tab,ext) + local temp=bin.new() + temp:addBlock('³Šž³–') + temp:addBlock(bin.getVersion()) + temp:addBlock(tab) + for i=1,#tab do + temp:addBlock(tab[i]) + temp:addBlock(bin.load(tab[i]).data) + end + temp:addBlock('Done') + temp:tofile(name.. ('.'..ext or '.llib')) +end +function bin.unpackLLIB(name,exe,todir,over,ext) + local temp=bin.load(name..('.'..ext or '.llib')) + local name='' + Head=temp:getBlock('s') + ver=temp:getBlock('s') + infiles=temp:getBlock('t') + if ver~=bin.getVersion() then + print('Incompatable llib file') + return nil + end + local tab={} + while name~='Done' do + name,data=temp:getBlock('s'),bin.new(temp:getBlock('s')) + if string.find(name,'.lua',1,true) then + table.insert(tab,data.data) + else + if not(bin.fileExist((todir or '')..name) and not(over)) then + data:tofile((todir or '')..name) + end + end + end + os.remove((todir or '')..'Done') + if exe then + for i=1,#tab do + assert(loadstring(tab[i]))() + end + end + return infiles +end +function bin.fileExist(path) + g=io.open(path or '','r') + if path =='' then + p='empty path' + return nil + end + if g~=nil and true or false then + p=(g~=nil and true or false) + end + if g~=nil then + io.close(g) + else + return false + end + return p +end +function bin:shiftbits(n) + if self:canStreamWrite() then + n=n or 0 + if n>=0 then + for i=0,self:getlength() do + print(string.byte(self:sub(i,i))+n%256) + self:streamwrite(string.char(string.byte(self:sub(i,i))+n%256),i-1) + end + else + n=math.abs(n) + for i=0,self:getlength() do + self:streamwrite(string.char((string.byte(self:sub(i,i))+(256-n%256))%256),i-1) + end + end + elseif self.Stream==false then + n=n or 0 + if n>=0 then + for i=1,self:getlength() do + self:morph(i,i,string.char(string.byte(self:sub(i,i))+n%256)) + end + else + n=math.abs(n) + for i=1,self:getlength() do + self:morph(i,i,string.char((string.byte(self:sub(i,i))+(256-n%256))%256)) + end + end + end +end +function bin:shiftbit(n,i) + if self:canStreamWrite() then + i=i-1 + n=n or 0 + if n>=0 then + print((string.byte(self:sub(i,i))+n)%256,n) + self:streamwrite(string.char((string.byte(self:sub(i,i))+n)%256),i-1) + else + n=math.abs(n) + print((string.byte(self:sub(i,i))+(256-n))%256,n) + self:streamwrite(string.char((string.byte(self:sub(i,i))+(256-n%256))%256),i-1) + end + elseif self.Stream==false then + n=n or 0 + if n>=0 then + self:morph(i,i,string.char((string.byte(self:sub(i,i))+n)%256)) + else + n=math.abs(n) + self:morph(i,i,string.char((string.byte(self:sub(i,i))+(256-n%256))%256)) + end + end +end +function bin.decodeBits(par) + if type(par)=='table' then + if par.t=='bit' then + return bin.new(par:toSbytes()) + end + else + if par:find(' ') then + par=par:gsub(' ','') + end + local temp=bits.new() + temp.data=par + return bin.new((temp:toSbytes()):reverse()) + end +end +function bin.textToBinary(txt) + return bin.new(bits.new(txt:reverse()):getBin()) +end +function bin:getData() + return self.data +end +function bin.getLuaVersion() + if type(jit)=="table" then + if jit.version then + return "JIT",jit.version + end + end + return "PUC",_VERSION:match("(%d-)%.(%d+)") +end +function bin.binToBuffer(b) + return bin:newDataBuffer(b.data) +end +function bin.bufferToBin(b) + return bin.new(b:getBuffer()) +end +function bin.newNamedBlock(indexSize) + local c={} + c.data=bin.new() + c.lastLoc=0 + if indexSize then + indexSize=indexSize+4 + end + c.index=bin:newDataBuffer(indexSize) + c.conv={ + ["n"]="\1", + ["b"]="\2", + ["s"]="\3", + ["t"]="\4", + ["f"]="\5" + } + if indexSize then + c.index:fillBuffer(bits.numToBytes(indexSize,4),1) + c.lastLoc=4 + else + --c.index:fillBuffer(bits.numToBytes(2048,4),1) + end + function c:tofile(path) + bin.new(self:tostring()):tofile(path) + end + function c:tostring() + c.index:fillBuffer(bits.numToBytes(c.index:getSize()-4,4),1) + return self.index:getBuffer()..self.data.data + end + function c:setPointer(name,data,t) + t=c.conv[t] + data=t..data + local dSize=#data + local index=bin:newDataBuffer() + local nLen=#name + local test="" + index:fillBuffer(bits.numToBytes(self.data:getSize()+1,4),1) + index:fillBuffer(name,5) + self.data:tackE(data) + test=self.index:fillBuffer(index:getBuffer().."\31",self.lastLoc+1) + self.lastLoc=self.lastLoc+1+index:getBufferSize() + if test=="Buffer Overflow!" then + error("Increase Index size!") + end + end + function c:addNamedBlock(name,value) + local bSize=#name + local ftype={} + if type(value)=="number" then + local dat=bits.numToBytes(value,8) -- makes 64 bit version of lua compatable + self:setPointer(name,dat,"n") + elseif type(value)=="boolean" then + if value then + self:setPointer(name,"1","b") + else + self:setPointer(name,"0","b") + end + elseif type(value)=="string" then + self:setPointer(name,value,"s") + elseif type(value)=="table" then + local str=bin.ToStr(value) + self:setPointer(name,str,"t") + elseif type(value)=="function" then + local ver,verM,verm=bin.getLuaVersion() + local data=string.dump(value) + if ver=="JIT" then + ftype=bin:newDataBuffer(bits.numToBytes(0,4)) -- luajit version + else + ftype=bin:newDataBuffer(bits.numToBytes(tonumber(verM..verm),4)) -- lua version with MajorMinor data + end + local fdata=bin.new() + fdata:tackE(ftype:getBuffer()..data) + self:setPointer(name,fdata.data,"f") + elseif type(value)=="userdata" then + error("Userdata cannot be put into a block!") + end + end + if not indexSize then + c:addNamedBlock("__UNBOUNDEDINDEX__",true) + end + return c +end +function bin.newStreamedNamedBlock(indexSize,path,update) + local c={} + c.data=bin.stream(path,false) + c.lastLoc=4 + c.conv={ + ["n"]="\1", + ["b"]="\2", + ["s"]="\3", + ["t"]="\4", + ["f"]="\5" + } + if not update then + c.data:tackE(bin:newDataBuffer(indexSize+4 or 2052):getBuffer()) + if indexSize then + c.data:mutate(bits.numToBytes(indexSize,4),1) + else + c.data:mutate(bits.numToBytes(2048,4),1) + end + c.indexSize=indexSize+4 or 2052 + else + c.indexSize=c.data:tonumber(1,4) + local i=bin.new(c.data:sub(5,c.indexSize+4)).data + local last=0 + for b=#i,1,-1 do + if i:sub(b,b)=="\31" then + last=b+4 + break + end + end + c.lastLoc=last + end + function c:tofile(path) + --No need when using a streamed block + end + function c:tostring() + return self.index:getBuffer()..self.data.data + end + function c:setPointer(name,data,t) + t=c.conv[t] + data=t..data + local dSize=#data + local index=bin:newDataBuffer() + local nLen=#name + local test="" + index:fillBuffer(bits.numToBytes((self.data:getSize()+1)-self.indexSize,4),1) + index:fillBuffer(name,5) + local test=self.data:mutate(index:getBuffer().."\31",self.lastLoc+1) + self.lastLoc=self.lastLoc+1+index:getBufferSize() + self.data:tackE(data) + if test=="Buffer Overflow!" then + error("Increase Index size!") + end + end + function c:addNamedBlock(name,value) + local bSize=#name + local ftype={} + if type(value)=="number" then + local dat=bits.numToBytes(value,8) -- makes 64 bit version of lua compatable + self:setPointer(name,dat,"n") + elseif type(value)=="boolean" then + if value then + self:setPointer(name,"1","b") + else + self:setPointer(name,"0","b") + end + elseif type(value)=="string" then + self:setPointer(name,value,"s") + elseif type(value)=="table" then + local str=bin.ToStr(value) + self:setPointer(name,str,"t") + elseif type(value)=="function" then + local ver,verM,verm=bin.getLuaVersion() + local data=string.dump(value) + if ver=="JIT" then + ftype=bin:newDataBuffer(bits.numToBytes(0,4)) -- luajit version + else + ftype=bin:newDataBuffer(bits.numToBytes(tonumber(verM..verm),4)) -- lua version with MajorMinor data + end + local fdata=bin.new() + fdata:tackE(ftype:getBuffer()..data) + self:setPointer(name,fdata.data,"f") + elseif type(value)=="userdata" then + error("Userdata cannot be put into a block!") + end + end + function c:close() + self:addNamedBlock("",false) + self.data:close() + end + return c +end +function bin.loadNamedBlock(path) + local c={} + c.data=bin.stream(path) + c.iSize=c.data:tonumber(1,4) + c.index=bin.new(c.data:sub(5,c.iSize+4)) + c.sData=bin.new(c.data:sub(c.iSize+5,-1)) + function c:CheckRestOfIndex(name) + local a,b=self.index:scan(name) + local d=self.index:tonumber(b+2,b+5) + if d==0 or b+5>self.iSize then + return -1 + end + return d + end + function c:getIndexes() + local tab={} + ind=5 + while ind do + local a=self.index:find("\31",ind) + if not a then break end + local b=self.index:sub(ind,a-1) + table.insert(tab,b) + ind=a+5 + end + return tab + end + function c:getBlock(name) + local a,b=self.index:scan(name) + if not a then return "index not found" end + local dloc=self.index:tonumber(a-4,a-1) + local dindex=bin:newDataBuffer(self.sData:sub(dloc,dloc)) + if dindex[1]==0x01 then -- type number + return self.sData:tonumber(dloc+1,dloc+8) + elseif dindex[1]==0x02 then -- type bool + return ({[1]=true,[0]=false})[tonumber(self.sData:sub(dloc+1,dloc+1))] + elseif dindex[1]==0x03 then -- type string + local dend=self:CheckRestOfIndex(name)--self.index:tonumber(b+2,b+5) + return self.sData:sub(dloc+1,dend-1) + elseif dindex[1]==0x04 then -- type table + local dend=self.index:tonumber(b+2,b+5) + return loadstring("return "..self.sData:sub(dloc+1,dend-1))() + elseif dindex[1]==0x05 then -- type function + local dend=self:CheckRestOfIndex(name)--self.index:tonumber(b+2,b+5) + local _ver=self.sData:tonumber(dloc+1,dloc+4) + local ver,verM,verm=bin.getLuaVersion() + if tonumber(verM..verm)==_ver then + return loadstring(self.sData:sub(dloc+5,dend-1)) + else + return function() error("This lua function is not compatible with the current version of lua!") end + end + end + end + return c +end +function bin.namedBlockManager(name) + if type(name)=="string" then + local i={} + local data=bin.loadNamedBlock(name) + local mt={ + __index=function(t,k) + return data:getBlock(k) + end, + } + setmetatable(i,mt) + return i + else + local i={} + local data=bin.newNamedBlock(name) + local mt={ + __newindex=function(t,k,v) + data:addNamedBlock(k,v) + end, + __index=data + } + setmetatable(i,mt) + return i + end +end +function bin.getIndexSize(tab) + size=0 + for i=1,#tab do + size=size+#tab[i]+5 + end + return size+5 +end +--[[---------------------------------------- +VFS +------------------------------------------]] +local _require = require +function require(path,vfs) + if bin.fileExist(path..'.lvfs') then + local data = bin.loadVFS(path..'.lvfs') + if data:fileExist(vsf) then + loadstring(data:readFile(vfs))() + end + else + return _require(path) + end +end +function bin.loadVFS(path) + local vfs=bin.newVFS() + local temp=bin.stream(path,false) + local files=temp:getBlock("t") + local size=0 + for i=1,#files do + local p,len=files[i]:match("(.-)|(.+)") + len=tonumber(len) + size=size+bin.lastBlockSize + local dat=temp:sub(size+5,size+len+4) + bin.lastBlockSize=len + vfs:mkfile(p:gsub("%./",""),dat) + end + return vfs +end +function bin.copyDir(dir,todir) + local vfs=bin.newVFS(dir,true) + vfs:toFS(todir) + vfs=nil +end +function bin.newVFS(t,l) + l=l or true + if type(t)=='string' then + t=io.parseDir(t,l) + end + local c={} + c.FS= t or {} + function c:merge(vfs) + bin.newVFS(table.merge(self.FS,vfs.FS)) + end + function c:mirror(file) + self:mkfile(file,file) + end + function c:mkdir(path) + table.merge(self.FS,io.pathToTable(path)) + end + function c:scanDir(path) + path=path or '' + local tab={} + if path=='' then + for i,v in pairs(self.FS) do + tab[#tab+1]=i + end + return tab + end + spath=io.splitPath(path) + local last=self.FS + for i=1,#spath-1 do + last=last[spath[i]] + end + return last[spath[#spath]] + end + function c:getFiles(path) + if not self:dirExist(path) then return end + path=path or '' + local tab={} + if path=='' then + for i,v in pairs(self.FS) do + if self:fileExist(i) then + tab[#tab+1]=i + end + end + return tab + end + spath=io.splitPath(path) + local last=self.FS + for i=1,#spath-1 do + last=last[spath[i]] + end + local t=last[spath[#spath]] + for i,v in pairs(t) do + if self:fileExist(path..'/'..i) then + tab[#tab+1]=path..'/'..i + end + end + return tab + end + function c:getDirectories(path) + if not self:dirExist(path) then return end + path=path or '' + local tab={} + if path=='' then + for i,v in pairs(self.FS) do + if self:dirExist(i) then + tab[#tab+1]=i + end + end + return tab + end + spath=io.splitPath(path) + local last=self.FS + for i=1,#spath-1 do + last=last[spath[i]] + end + local t=last[spath[#spath]] + for i,v in pairs(t) do + if self:dirExist(path..'/'..i) then + tab[#tab+1]=path..'/'..i + end + end + return tab + end + function c:mkfile(path,data) + local name=io.getFullName(path) + local temp=path:reverse() + local a,b=temp:find('/') + if not a then + a,b=temp:find('\\') + end + if a then + temp=temp:sub(a+1):reverse() + path=temp + local t,l=io.pathToTable(path) + l[name]=data + table.merge(self.FS,t) + else + self.FS[name]=data + end + end + function c:remove(path) + if path=='' or path==nil then return end + spath=io.splitPath(path) + local last=self.FS + for i=1,#spath-1 do + last=last[spath[i]] + end + last[spath[#spath]]=nil + end + function c:readFile(path) + spath=io.splitPath(path) + local last=self.FS + for i=1,#spath do + last=last[spath[i]] + end + if type(last)=='userdata' then + last=last:read('*all') + end + return last + end + function c:copyFile(p1,p2) + self:mkfile(p2,self:readFile(p1)) + end + function c:moveFile(p1,p2) + self:copyFile(p1,p2) + self:remove(p1) + end + function c:fileExist(path) + return type(self:readFile(path))=='string' + end + function c:dirExist(path) + if path=='' or path==nil then return end + spath=io.splitPath(path) + local last=self.FS + for i=1,#spath-1 do + last=last[spath[i]] + end + if last[spath[#spath]]~=nil then + if type(last[spath[#spath]])=='table' then + return true + end + end + return false + end + function c:_getHierarchy() + local ord={} + local datlink=bin.new() + local function toStr(val, name, skipnewlines, depth, path) + skipnewlines = skipnewlines or false + path=path or "." + depth = depth or 0 + local tmp = string.rep(" ", depth) + if name then + if type(name) == "string" then + tmp = tmp .. "[\""..name.."\"] = " + else + tmp = tmp .. "["..(name or "").."] = " + end + end + if type(val) == "table" then + tmp = tmp .. "{" .. (not skipnewlines and "\n" or "") + for k, v in pairs(val) do + tmp = tmp .. toStr(v, k, skipnewlines, depth + 1,path.."/"..k) .. "," .. (not skipnewlines and "\n" or "") + end + tmp = tmp .. string.rep(" ", depth) .. "}" + elseif type(val) == "string" then + tmp = tmp .. #val + datlink:tackE(val) + ord[#ord+1]=path.."|"..#val + end + return tmp + end + return toStr(self.FS),ord,datlink + end + function c:getHierarchy() + local ord={} + local function toStr(val, name, skipnewlines, depth, path) + skipnewlines = skipnewlines or false + path=path or "." + depth = depth or 0 + local tmp = string.rep(" ", depth) + if name then + if type(name) == "string" then + tmp = tmp .. "[\""..name.."\"] = " + else + tmp = tmp .. "["..(name or "").."] = " + end + end + if type(val) == "table" then + tmp = tmp .. "{" .. (not skipnewlines and "\n" or "") + for k, v in pairs(val) do + tmp = tmp .. toStr(v, k, skipnewlines, depth + 1,path.."/"..k) .. "," .. (not skipnewlines and "\n" or "") + end + tmp = tmp .. string.rep(" ", depth) .. "}" + elseif type(val) == "string" then + tmp = tmp .. ";" + ord[#ord+1]=path.."|"..#val + end + return tmp + end + return toStr(self.FS),ord + end + function c:tofile(path) + local temp=bin.new() + local h,o,link=self:_getHierarchy() + temp:addBlock(o) + temp:merge(link) + temp:tofile(path) + end + function c:toFS(path) + if path then + if path:sub(-1,-1)~='\\' then + path=path..'\\' + elseif path:find('/') then + path=path:gsub('/','\\') + end + io.mkDir(path) + else + path='' + end + function build(tbl, indent, folder) + if not indent then indent = 0 end + if not folder then folder = '' end + for k, v in pairs(tbl) do + formatting = string.rep(' ', indent) .. k .. ':' + if type(v) == 'table' then + if v.t~=nil then + io.mkFile(folder..k,tostring(v),'wb') + else + if not(io.dirExists(path..folder..string.sub(formatting,1,-2))) then + io.mkDir(folder..string.sub(formatting,1,-2)) + end + build(v,0,folder..string.sub(formatting,1,-2)..'\\') + end + elseif type(v)=='string' then + io.mkFile(folder..k,v,'wb') + elseif type(v)=='userdata' then + io.mkFile(folder..k,v:read('*all'),'wb') + end + end + end + build(self.FS,0,path) + end + function c:print() + table.print(self.FS) + end + return c +end +--[[---------------------------------------- +BITS +------------------------------------------]] +function bits.new(n) + if type(n)=='string' then + local t=tonumber(n,2) + if not t then + t={} + for i=#n,1,-1 do + table.insert(t,bits:conv(string.byte(n,i))) + end + n=table.concat(t) + else + n=t + end + end + local temp={} + temp.t='bit' + setmetatable(temp, bits) + if type(n)~='string' then + local tab={} + while n>=1 do + table.insert(tab,n%2) + n=math.floor(n/2) + end + local str=string.reverse(table.concat(tab)) + if #str%8~=0 then + str=string.rep('0',8-#str%8)..str + end + temp.data=str + else + temp.data=n + end + setmetatable({__tostring=function(self) return self.data end},temp) + return temp +end +function bits.numToBytes(n,fit) + local num=bits.new(n):toSbytes() + num=bin.endianflop(num) + if fit then + if fit<#num then + print("Warning: attempting to store a number that takes up more space than allotted!") + return num:sub(1,fit) + elseif fit==#num then + return num + else + return string.rep("\0",fit-#num)..num + end + else + return num + end +end +function bits:conv(n) + local tab={} + while n>=1 do + table.insert(tab,n%2) + n=math.floor(n/2) + end + local str=string.reverse(table.concat(tab)) + if #str%8~=0 then + str=string.rep('0',8-#str%8)..str + end + return str +end +function bits:add(i) + if type(i)=='number' then + i=bits.new(i) + end + self.data=self:conv(tonumber(self.data,2)+tonumber(i.data,2)) +end +function bits:sub(i) + if type(i)=='number' then + i=bits.new(i) + end + self.data=self:conv(tonumber(self.data,2)-tonumber(i.data,2)) +end +function bits:multi(i) + if type(i)=='number' then + i=bits.new(i) + end + self.data=self:conv(tonumber(self.data,2)*tonumber(i.data,2)) +end +function bits:div(i) + if type(i)=='number' then + i=bits.new(i) + end + self.data=self:conv(tonumber(self.data,2)/tonumber(i.data,2)) +end +function bits:tonumber(s) + if type(s)=='string' then + return tonumber(self.data,2) + end + s=s or 1 + return tonumber(string.sub(self.data,(8*(s-1))+1,8*s),2) or error('Bounds!') +end +function bits:isover() + return #self.data>8 +end +function bits:flipbits() + tab={} + for i=1,#self.data do + if string.sub(self.data,i,i)=='1' then + table.insert(tab,'0') + else + table.insert(tab,'1') + end + end + self.data=table.concat(tab) +end +function bits:tobytes() + local tab={} + for i=self:getbytes(),1,-1 do + table.insert(tab,string.char(self:tonumber(i))) + end + return bin.new(table.concat(tab)) +end +function bits:toSbytes() + local tab={} + for i=self:getbytes(),1,-1 do + table.insert(tab,string.char(self:tonumber(i))) + end + return table.concat(tab) +end +function bits:getBin() + return self.data +end +function bits:getbytes() + return #self.data/8 +end diff --git a/Libs/lovebind.lua b/Libs/lovebind.lua new file mode 100644 index 0000000..09a90a8 --- /dev/null +++ b/Libs/lovebind.lua @@ -0,0 +1,103 @@ +os.sleep=love.timer.sleep +function bin.load(file,s,r) + content, size = love.filesystem.read(file) + local temp=bin.new(content) + temp.filepath=file + return temp +end +function bin:tofile(filename) + if not(filename) or self.Stream then return nil end + love.filesystem.write(filename,self.data) +end +function bin.stream(file,l) + error("Sorry streaming is not available when using love2d :(, I am looking for a solution though :)") +end +function love.run() + if love.math then + love.math.setRandomSeed(os.time()) + end + if love.event then + love.event.pump() + end + if love.load then love.load(arg) end + if love.timer then love.timer.step() end + local dt = 0 + while true do + -- Process events. + if love.event then + love.event.pump() + for e,a,b,c,d in love.event.poll() do + if e == "quit" then + if not love.quit or not love.quit() then + if love.audio then + love.audio.stop() + end + return + end + end + love.handlers[e](a,b,c,d) + end + end + if love.timer then + love.timer.step() + dt = love.timer.getDelta() + end + if love.update then love.update(dt) end + if multi.boost then + for i=1,multi.boost-1 do + multi:uManager(dt) + end + end + multi:uManager(dt) + if love.window and love.graphics and love.window.isCreated() then + love.graphics.clear() + love.graphics.origin() + if love.draw then love.draw() end + multi.dManager() + love.graphics.setColor(255,255,255,255) + if multi.draw then multi.draw() end + love.graphics.present() + end + end +end +multi.drawF={} +function multi:dManager() + for ii=1,#multi.drawF do + multi.drawF[ii]() + end +end +function multi:onDraw(func,i) + i=i or 1 + table.insert(self.drawF,i,func) +end +function multi:lManager() + if love.event then + love.event.pump() + for e,a,b,c,d in love.event.poll() do + if e == "quit" then + if not love.quit or not love.quit() then + if love.audio then + love.audio.stop() + end + return nil + end + end + love.handlers[e](a,b,c,d) + end + end + if love.timer then + love.timer.step() + dt = love.timer.getDelta() + end + if love.update then love.update(dt) end + multi:uManager(dt) + if love.window and love.graphics and love.window.isCreated() then + love.graphics.clear() + love.graphics.origin() + if love.draw then love.draw() end + multi.dManager() + love.graphics.setColor(255,255,255,255) + if multi.draw then multi.draw() end + love.graphics.present() + end +end diff --git a/Libs/test.dat b/Libs/test.dat new file mode 100644 index 0000000..8b9302a Binary files /dev/null and b/Libs/test.dat differ diff --git a/Libs/test.lua b/Libs/test.lua new file mode 100644 index 0000000..18ec8fb --- /dev/null +++ b/Libs/test.lua @@ -0,0 +1,18 @@ +require("Library") +local clock = os.clock +function sleep(n) -- seconds + local t0 = clock() + while clock() - t0 <= n do end +end +function tester(test) + sleep(1) + return test*10 +end +--~ require("bin") +--~ test=bin.namedBlockManager() +--~ test["name"]="Ryan" +--~ test["age"]=21 +--~ test:tofile("test.dat") +--~ test2=bin.namedBlockManager("test.dat") +--~ print(test2["name"]) +--~ print(test2["age"]) diff --git a/engine/OpenAL32.dll b/engine/OpenAL32.dll new file mode 100644 index 0000000..f012fb9 Binary files /dev/null and b/engine/OpenAL32.dll differ diff --git a/engine/SDL2.dll b/engine/SDL2.dll new file mode 100644 index 0000000..2dd42bb Binary files /dev/null and b/engine/SDL2.dll differ diff --git a/engine/changes.txt b/engine/changes.txt new file mode 100644 index 0000000..fff2207 --- /dev/null +++ b/engine/changes.txt @@ -0,0 +1,1031 @@ +LOVE 0.10.1 [Super Toast] +------------------------- + +Released: 2016-02-14 + + * Added a new love.conf flag t.externalstorage, which determines whether files are saved in internal or external storage on Android devices. + * Added a new variant of love.graphics.arc which can draw different types of arcs ("pie", "open", or "closed"). + * Added "lighten" and "darken" BlendModes. They can only be used with the "premultiplied" BlendAlphaMode. + * Added the "lighten" GraphicsFeature constant. + * Added the ability to avoid clearing specific Canvases when calling love.graphics.clear, if multiple Canvases are active at once via love.graphics.setCanvas. + * Added Text:getDimensions. + * Added optional "collideconnected" argument to love.physics.newMotorJoint. + + * Fixed a Lua error in the no-game screen if the window's height is too small. + * Fixed the default error handler to reset the mouse Cursor. + * Fixed love.filesystem functions crashing when called if liblove is used directly without calling love.filesystem.init. + * Fixed audio on Android to pause when the app is inactive, and resume when the app becomes active again. + * Fixed Source:setRelative to set the relative state of playing Sources to the passed value rather than an old cached one. + * Fixed the Video decoding thread hanging after Video:seek or when a Video finishes. + * Fixed Video:isPlaying to always return false after it finishes playing. + * Fixed RandomGenerator:random crashing if a nil 'self' is used. + * Fixed loading BMFont files which have characters with 0 width or height (a space character, for example). + * Fixed love.graphics.newFont causing crashes if FileData is passed in. + * Fixed love.graphics.clear(colortable) causing crashes on OpenGL ES 2 systems when a Canvas is active. + * Fixed a driver bug on some Android devices which caused all objects to show up as black. + * Fixed a driver bug on Windows with AMD graphics cards where love.graphics.clear would not always work. + * Fixed Shader:sendColor incorrectly converting alpha values from sRGB to linear RGB when gamma-correct rendering is enabled. + * Fixed love.graphics.newMesh(vertices) double-converting colors from sRGB to linear RGB when gamma-correct rendering is enabled. + * Fixed love.graphics.new* crashing when there is no graphics context/window. + + * Updated the Windows executable to prefer the high-powered AMD graphics card on systems which have switchable Intel+AMD GPUs. + * Updated love.touch.getTouches to return the list of IDs in the relative order that the touches initially happened, instead of being in a random order. + * Updated the error messages caused by invalid or bad arguments to ImageData and SoundData methods to be more descriptive. + + +LOVE 0.10.0 [Super Toast] +------------------------- + +Released: 2015-12-22 + + * Added an iOS port. + * Added an Android port. + * Added the flag t.accelerometerjoystick to love.conf. Disables accelerometer-as-joystick functionality on mobile devices when false. + * Added the flag t.gammacorrect to love.conf (replaces t.window.srgb.) Enabling it globally enables gamma-correct rendering, when supported. + * Added video playback support for Ogg Theora videos, via love.graphics.newVideo and Video objects. + * Added love.video module. It is not used for displaying videos on-screen, only decoding them. + * Added love.touch module. Note that it has important differences from the touch implementation in the LÖVE 0.9 Android and iOS ports. + * Added love.touchpressed, love.touchreleased, and love.touchmoved. + * Added love.system.vibrate. + * Added love.filesystem.setRequirePath and love.filesystem.getRequirePath. + * Added an optional program exit argument to love.event.quit. + * Added love.filedropped and love.directorydropped event callback functions. + * Added love.lowmemory event callback function, called when the app is running out of memory on mobile operating systems. + * Added love.textedited event callback function, called when the user is compositing text (e.g. via an IME.) + * Added love.wheelmoved event callback function (replaces "wu" and "wd" constants for love.mousepressed.) + * Added love.mouse.hasCursor. + * Added a boolean argument to love.mousepressed and love.mousereleased indicating whether the button event originated from a touch press. + * Added optional x/y/width/height arguments to love.keyboard.setTextInput. They tell the system where text will show up so on-screen keyboards can avoid that area. + * Added Source:getType (replaces Source:isStatic.) + * Added Source:getDuration and Decoder:getDuration. + * Added an optional string argument containing raw pixel byte data to the width/height variant of love.image.newImageData. + * Added love.graphics.ellipse. + * Added rounded-rectangle support to love.graphics.rectangle. + * Added love.graphics.points (replaces love.graphics.point.) + * Added love.graphics.intersectScissor. + * Added an optional argument to love.graphics.setBlendMode which indicates whether to treat the colors of drawn objects as having pre-multiplied alpha. + * Added love.graphics.getSupported (replaces love.graphics.isSupported.) + * Added love.graphics.getSystemLimits (replaces love.graphics.getSystemLimit.) + * Added love.graphics.stencil and love.graphics.set/getStencilTest (replaces love.graphics.setStencil.) + * Added love.graphics.isActive. + * Added color arguments to love.graphics.clear. It no longer always uses the background color value. + * Added love.graphics.discard. + * Added love.graphics.isGammaCorrect. + * Added the "clampzero" WrapMode. + * Added the ability to specify custom mipmaps when creating an image, via love.graphics.newImage(filename, {mipmaps={mip1, mip2, ...}}) + * Added optional x/y/width/height arguments to Image:refresh and Canvas:newImageData. + * Added Image:getFlags. + * Added one- and two-channel Canvas formats: r8, rg8, r16f, rg16f, r32f, and rg32f. + * Added support for different formats in each Canvas when using multi-canvas rendering. Added the "multicanvasformats" Graphics Feature constant. + * Added support for OpenGL ES 2 and 3. + * Added support for loading ETC, EAC, PVRTC, and ASTC compressed textures on systems that support them. + * Added custom vertex attribute support for Meshes via new variants of love.graphics.newMesh. + * Added Mesh:setVertexAttribute and Mesh:getVertexAttribute. + * Added Mesh:getVertexFormat. + * Added Mesh:flush. + * Added an optional 'startvertex' argument to Mesh:setVertices. + * Added the ability for love.graphics.newMesh and Mesh:setVertices to accept a Data object. + * Added Mesh:setAttributeEnabled and Mesh:isAttributeEnabled. + * Added Mesh:attachAttribute. + * Added SpriteBatch:attachAttribute. + * Added Shader:sendColor. + * Added new shader functions: gammaCorrectColor, gammaToLinear, and linearToGamma. The functions also have 'precise' and 'fast' variants. + * Added Text objects and love.graphics.newText. + * Added per-character color support to love.graphics.print/printf and to Text objects. + * Added BMFont bitmap font file support to love.graphics.newFont and love.font. + * Added kerning support for TrueType/OpenType and BMFont Fonts. + * Added an optional font hinting argument to love.graphics.newFont when loading TrueType fonts. + * Added an optional spacing argument to love.graphics.newImageFont, which applies additional spacing to all rendered glyphs. + * Added Font:setFallbacks. + * Added love.window.maximize. + * Added love.window.close. + * Added love.window.requestAttention. + * Added love.window.setDisplaySleepEnabled and love.window.isDisplaySleepEnabled. + * Added BezierCurve:renderSegment and BezierCurve:removeControlPoint. + * Added BezierCurve:getSegment. + * Added love.math.compress and love.math.decompress. + * Added Channel:performAtomic. + + * Changed love.mousepressed, love.mousereleased, and love.mouse.isDown to use button numbers instead of named button constants. + * Changed love.keypressed to be love.keypressed(key, scancode, isrepeat). + * Changed love.keyreleased to be love.keyreleased(key, scancode). + * Changed Font:getWrap's second return value to be a table containing the text split into lines. + * Changed love.graphics.newImage's optional second argument to be a table of flags (flags are "mipmaps" and "linear".) + * Changed the arguments for the standard variants of love.graphics.newMesh to newMesh(vertices [, drawmode, usage]) and newMesh(vertexcount [, drawmode, usage]). + * Changed ImageData:encode to return a FileData object. ImageData:encode's first parameter is now the format to encode to, and the second parameter is an optional filename to write to. + + * Renamed the "normal" Fullscreen Type to "exclusive". + * Renamed the DistanceModel constants "inverse clamped", "linear clamped", and "exponent clamped" to "inverseclamped", "linearclamped", and "exponentclamped". + * Renamed the "additive", "subtractive", and "multiplicative" BlendModes to "add", "subtract", and "multiply". + * Renamed the KeyConstant and Scancode representing the spacebar from " " to "space". + * Renamed File:eof to File:isEOF. + * Renamed Canvas:getImageData to Canvas:newImageData. + * Renamed love.image's CompressedData type to CompressedImageData. + + * Removed callback variant of love.filesystem.getDirectoryItems. + * Removed the "wu" and "wd" constants for love.mousepressed (replaced by love.wheelmoved.) + * Removed the named mouse button constants (replaced by button numbers.) + * Removed Source:isStatic (replaced by Source:getType.) + * Removed image loading support for all (non-compressed texture) file formats except for PNG, JPEG, TGA, and BMP. + * Removed JPEG encoding support from ImageData:encode. + * Removed love.graphics.point (replaced by love.graphics.points.) + * Removed love.graphics.setPointStyle and love.graphics.getPointStyle. + * Removed love.graphics.isSupported (replaced by love.graphics.getSupported.) + * Removed love.graphics.getSystemLimit (replaced by love.graphics.getSystemLimits.) + * Removed love.graphics.setStencil (replaced by love.graphics.stencil and love.graphics.setStencilTest.) + * Removed the "canvas", "shader", "npot", "subtractive", and "mipmap" Graphics Feature constants (the features always have guaranteed support now.) + * Removed the "multicanvas" Graphics Feature constant (use love.graphics.getSystemLimits instead.) + * Removed the "srgb" Graphics Feature constant (use love.graphics.isGammaCorrect() or love.graphics.getCanvasFormats().srgb instead.) + * Removed the "srgb" flag in love.window.setMode and in the t.window table in love.conf (Replaced by t.gammacorrect.) + * Removed the "premultiplied" blend mode (love.graphics.setBlendMode("alpha", "premultiplied") now does the same thing.) + * Removed Canvas:getPixel (use Canvas:newImageData instead.) + * Removed Canvas:clear (use love.graphics.clear instead.) + * Removed Mesh:getVertices. + * Removed Mesh:setVertexColors and Mesh:hasVertexColors (use Mesh:setAttributeEnabled("VertexColor", enable) instead.) + * Removed functions deprecated in LOVE 0.9.1 and 0.9.2: + * Removed love.graphics.getMaxImageSize and love.graphics.getMaxPointSize (replaced by love.graphics.getSystemLimits.) + * Removed Mesh:set/getImage, SpriteBatch:set/getImage, and ParticleSystem:set/getImage (replaced by set/getTexture.) + * Removed SpriteBatch:bind/unbind. + * Removed Canvas:getFSAA and the "fsaa" flag in love.conf and love.window.setMode (replaced by Canvas:getMSAA and "msaa".) + * Removed the "dxt" and "bc5" Graphics Feature constant (replaced by love.graphics.getCompressedImageFormats.) + * Removed the "hdrcanvas" Graphics Feature constant (replaced by love.graphics.getCanvasFormats.) + * Removed love.window.getWidth/getHeight/getDimensions (use love.graphics.getWidth/getHeight/getDimensions or love.window.getMode instead.) + + * Fixed utf8.char. + * Fixed detection of fused love games. + * Fixed World:getCallbacks and World:getContactFilter when used in coroutines. + * Fixed crashes when objects which store Lua callback functions are garbage collected after being used in coroutines. + * Fixed memory leaks in love.physics if World:destroy is never called. When a World is GCed it now destroys all objects it owns. + * Fixed love.keyboard.getKeyFromScancode crashing when an invalid scancode is given. + * Fixed decoding of 8-bit WAV files. + * Fixed a crash issue when rewinding streaming ogg Sources, when certain versions of libvorbis are used. + * Fixed love.audio.stop() not rewinding streaming Sources. + * Fixed the stencil buffer in Canvases when an unsupported MSAA value is used to create the Canvas. + * Fixed Canvas:renderTo to restore the previous Canvas if an error occurs in the passed function. + * Fixed love.graphics.draw(canvas) to cause an error if that Canvas is the active one. + * Fixed Mesh:getVertexMap to return nil rather than an empty table, if no vertex map has been set. + * Fixed love.graphics.getColorMask. + * Fixed the default offset for particles when ParticleSystem:setQuads or ParticleSystem:setTexture is used. + * Fixed love.graphics.shear resetting all love.graphics transformations. + * Fixed the "add" and "subtract" blend modes to no longer modify the alpha of the Canvas / screen. + + * Improved the performance of World:rayCast and World:queryBoundingBox. + * Improved the performance of love.graphics.line and other line drawing functions, when the "smooth" LineStyle is used. + * Improved the performance of Shader:send when matrices are used. + * Improved the performance of ImageData and SoundData methods when LuaJIT's JIT compiler is enabled, by using efficient FFI code. + * Improved the performance of love.math.noise, love.math.gammaToLinear, love.math.linearToGamma, love.math.random, and RandomGenerator:random when LuaJIT's JIT compiler is enabled. + + * Updated the compatibility warning notice to use a message box and to show the version specified in love.conf. + * Updated the compatibility warning notice to display before main.lua is loaded. + * Updated the __tostring metamethod of love objects to output the pointer value, similar to tostring(table). + * Updated World:setCallbacks, World:setContactFilter, World:queryBoundingBox, and World:rayCast to have improved argument type checking. + * Updated threads to load love.filesystem automatically. + * Updated love.filesystem to enable symlinks by default. + * Updated love.math.setRandomSeed and RandomGenerator:setSeed to produce better results for the first few random() calls. + * Updated love.math.random and RandomGenerator:random to produce slightly better results in general. + * Updated Source methods that deal with spatial audio to error rather than failing silently if the Source isn't mono. + * Updated the 3D and 4D variants of love.math.noise to use Perlin noise rather than Simplex noise, to avoid patent issues. + * Updated ImageFonts to no longer treat separator pixels as spacing. + * Updated the default font to use less memory. + * Updated the behavior of text wrapping with love.graphics.printf and Font:getWrap to work better. + * Updated love.graphics.print and love.graphics.printf to no longer automatically round the x and y position arguments. + * Updated some error messages for love.graphics.newImage to be more descriptive. + * Updated love.graphics color functions to automatically apply love.math.gammaToLinear to color values when gamma-correct rendering is enabled. + * Updated the 'normal' Canvas format to internally use 'srgb' rather than 'rgba8' when gamma-correct rendering is enabled. + * Updated love.graphics.setColor to affect all drawn objects, including ParticleSystems, SpriteBatches, and Meshes. + * Updated the default fullscreen type to be "desktop" rather than "exclusive". + * Updated the minimum runtime system requirements of LOVE to require OpenGL 2.1 or OpenGL ES 2 support. + * Updated the pixel shader effect function so screen_coords.y is 0 at the top of the screen instead of the bottom. + * Updated Images to require setting the mipmaps flag to true on creation in order to use mipmaps. + * Updated Images to allow mipmaps for non-power-of-two sizes. + +LOVE 0.9.2 [Baby Inspector] +--------------------------- + + Released: 2015-02-14 + + * Added Lua 5.3's UTF-8 module (via utf8 = require("utf8")). + * Added Shader:getExternVariable. + * Added several new canvas texture formats. + * Added love.graphics.getCanvasFormats. + * Added love.graphics.getCompressedImageFormats. + * Added ParticleSystem:setQuads. + * Added ParticleSystem:setLinearDamping. + * Added SpriteBatch:flush. + * Added love.graphics.getStats. + * Added "mirroredrepeat" wrap mode. + * Added love.audio.setDopplerScale and love.audio.getDopplerScale. + * Added optional duration argument to Joystick:setVibration. + * Added love.joystick.loadGamepadMappings and love.joystick.saveGamepadMappings. + * Added Joint:setUserData and Joint:getUserData. + * Added Joint:getBodies. + * Added GearJoint:getJoints. + * Added Contact:getFixtures and Body:getContactList. + * Added Body:getWorld. + * Added Body:getJointList. + * Added Body/Contact/Fixture/Joint/World:isDestroyed. + * Added love.mousemoved event callback. + * Added love.mouse.setRelativeMode and love.mouse.getRelativeMode. + * Added Scancode enums, love.keyboard.getKeyFromScancode, and love.keyboard.getScancodeFromKey. + * Added love.window.getDisplayName. + * Added love.window.minimize. + * Added love.window.showMessageBox. + * Added 'refreshrate' field to the table returned by love.window.getMode. + * Added love.window.toPixels and love.window.fromPixels. + * Added love.window.setPosition and love.window.getPosition, and 'x' and 'y' fields to love.window.setMode and t.window in love.conf. + * Added love.filesystem.isSymlink, love.filesystem.setSymlinksEnabled, and love.filesystem.areSymlinksEnabled. + * Added love.filesystem.getRealDirectory. + + * Deprecated SpriteBatch:bind and SpriteBatch:unbind. + * Deprecated all uses of the name 'FSAA' in favor of 'MSAA'. + * Deprecated the 'hdrcanvas' graphics feature enum in favor of getCanvasFormats. + * Deprecated the 'dxt' and 'bc5' graphics feature enums in favor of getCompressedImageFormats. + + * Fixed crashes when love objects are used in multiple threads. + * Fixed love.filesystem.setIdentity breaking in some situations when called multiple times. + * Fixed the default love.filesystem identity when in Fused mode in Windows. + * Fixed love.system.openURL sometimes blocking indefinitely on Linux. + * Fixed love.joystick.setGamepadMapping. + * Fixed the order of vertices in ChainShapes. + * Fixed love.mouse.getPosition returning outdated values if love.mouse.setPosition is used in the same frame. + * Fixed love.graphics.newFont to error when given an invalid size argument. + * Fixed the filename and backtrace given when love.graphics.print errors. + * Fixed a small memory leak if love.graphics.newCanvas errors. + * Fixed shader:getWarnings returning unnecessary information. + * Fixed some cases of noncompliant shader code not properly erroring on some nvidia drivers. + * Fixed a potential crash when Shader objects are garbage collected. + * Fixed a potential small memory leak triggered when love.graphics.newShader errors. + * Fixed love.graphics.newMesh(vertexcount, ...) causing the Mesh to do instanced rendering. + * Fixed Mesh:getVertexMap. + * Fixed Image:refresh generating mipmaps multiple times if mipmap filtering is enabled. + * Fixed Image:setMipmapFilter to not keep bad state around if it errors. + * Fixed Mesh:setDrawRange when the Mesh has a vertex map set. + * Fixed internal detection of the 'position' and 'effect' shader functions. + * Fixed Texture memory leak when Meshes are garbage collected. + * Fixed the default line join mode to be 'miter' instead of an undefined value. + * Fixed the default error handler text size when highdpi mode is enabled on a Retina monitor. + * Fixed the default error handler background color when sRGB mode is enabled for the window. + * Fixed love.window.setMode to fall back to the largest available mode if a width or height greater than the largest supported is specified and fullscreen is used. + * Fixed the state of wireframe mode when love.window.setMode is called. + * Fixed Canvas:getPixel to error if the coordinates are not within the Canvas' size. + * Fixed detection of compressed textures to work regardless of the file's extension. + + * Renamed all cases of FSAA to MSAA. The FSAA names still exist for backward-compatibility. + + * Updated the Windows executable to automatically prefer the higher performance GPU on nvidia Optimus systems. + * Updated the --console command-line argument in Windows to open the console before conf.lua is loaded. + * Updated t.console and the --console command-line argument in Windows to use the existing Console window, if love was launched from one. + * Updated the love executable to verify that the love library's version matches. + * Updated the Lua wrapper code for modules to avoid crashes when the module's instance is created, deleted, and recreated. + * Updated internal code for handling garbage collection of love objects to be more efficient. + * Updated love's initialization code to trigger a Lua error if love.conf has an error in it. + * Updated the paths returned by love.filesystem.getSaveDirectory and friends to strip double-slashes from the string. + * Updated the error message when love.filesystem.write or File:open fails because the directory doesn't exist. + * Updated the error message when love.math.setRandomseed(0) is attempted. + * Updated the error message when invalid UTF-8 strings are used in love functions that expect UTF-8. + * Updated love.physics.newPolygonShape and love.physics.newChainShape to accept a table of vertices. + * Updated love.physics.newChainShape to error if the number of arguments is invalid. + * Updated love.thread.newThread to accept a literal string of code directly. + * Updated love-created threads to use names visible in external debuggers. + * Updated SpriteBatch:unbind to use less VRAM if the SpriteBatch has the static usage hint. + * Updated love.graphics.newImage, love.image.newImageData, etc. to leave less Lua-owned memory around. + * Updated love.graphics.push to accept different stack types to push. Current types are "transform" and "all". + * Updated love shaders to accept GLSL ES precision qualifiers on variables, although they do nothing. + * Updated the error message for love.graphics.newShader to be less cryptic if an invalid filename is given. + * Updated compressed texture loading code to allow BC6 and BC7 compressed textures (if the graphics driver supports them.) + +LOVE 0.9.1 [Baby Inspector] +--------------------------- + + Released: 2014-04-01 + + * Added Source:clone. + * Added blend mode "screen". + * Added ParticleSystem:clone. + * Added ParticleSystem:moveTo, has smoother emitter movement compared to setPosition. + * Added ParticleSystem:setRelativeRotation. + * Added love.graphics.setWireframe for debugging. + * Added Mesh:setDrawRange and Mesh:getDrawRange. + * Added CircleShape:getPoint and CircleShape:setPoint. + * Added Mesh/SpriteBatch/ParticleSystem:setTexture, accepts Canvases and Images. + * Added high-dpi window support for Retina displays in OS X, via the 'highdpi' window flag. + * Added love.window.getPixelScale. + * Added love.graphics.getSystemLimit. + * Added antialiasing support to Canvases. + * Added Canvas:getFSAA. + * Added 'love_ScreenSize' built-in variable in shaders. + * Added love.getVersion. + * Added support for gamma-correct rendering. + * Added love.graphics.isSupported("srgb"). + * Added love.math.gammaToLinear and love.math.linearToGamma. + * Added RandomGenerator:getState and RandomGenerator:setState. + * Added Body:setUserData and Body:getUserData. + * Added some missing obscure key constants. + * Added optional callback function argument to love.filesystem.getDirectoryItems. + * Added love.system.openURL. + + * Deprecated Mesh/SpriteBatch/ParticleSystem:setImage. + * Deprecated love.graphics.getMaxImageSize and love.graphics.getMaxPointSize. + + * Fixed love.graphics.scale with negative values causing incorrect line widths. + * Fixed Joystick:isDown using 0-based button index arguments. + * Fixed Source:setPitch to error when infinity or NaN is given. + * Fixed love.graphics.setCanvas() to restore the proper viewport and scissor rectangles. + * Fixed TrueType font glyphs which request a monochrome bitmap pixel mode. + * Fixed love.graphics.reset causing crashes when called in between love.graphics.push/pop. + * Fixed tab characters ("\t") to display properly with love.graphics.print. + * Fixed love.graphics.isCreated to return false when love.window.setMode fails completely. + * Fixed love.window.setMode to not destroy OpenGL resources before checking whether a fullsceren size is supported. + * Fixed World:getBodyList and World:getJointList causing hard crashes. + * Fixed loading BC4 compressed textures. + * Fixed SoundData objects being initialized with garbage values. + * Fixed 8-bit SoundData samples when used in love.audio Sources. + + * Updated the error text for love.filesystem’s module searchers when require fails. + * Updated the love.filesystem module searchers to be tried after package.preload instead of before. + * Updated love.graphics.newParticleSystem, newSpriteBatch, and newMesh to accept Canvases. + * Updated Canvas drawing code, texture coordinates are no longer flipped vertically. + * Updated Canvas:renderTo to work properly if a Canvas is currently active. + * Updated ParticleSystem:setEmissionRate to accept non-integer numbers. + * Updated Source:play to return a boolean indicating success. + * Updated t.console in conf.lua to create the console before modules are loaded in Windows. + * Updated Mesh vertex maps (index buffers) to use less space in VRAM. + * Updated love.graphics.newMesh and Mesh:setVertices to default the UV parameters to 0,0. + * Updated Fixture:set/getUserData to work in Coroutines. + * Updated fullscreen-desktop and resizable window modes in OS X to use Mac OS 10.7's fullscreen Spaces. + * Updated love.filesystem's C library loader to look in paths added via love.filesystem.mount, in Fused mode. + * Updated the default love.run code to make initial love.math.random calls more random. + +LOVE 0.9.0 [Baby Inspector] +--------------------------- + + Released: 2013-12-13 + + * Added better multiplayer networking support via ENet. + * Added --fused command line argument, to simulate fusing. + * Added liblove. + * Added the ability to have exit values. + * Added exit value of 1 in case of error by default. + * Added basic support for the file:// uri scheme. + * Added love.filesystem.isFused. + * Added love.filesystem.getIdentity. + * Added love.filesystem.append. + * Added love.filesystem.getSize. + * Added love.filesystem.mount and love.filesystem.unmount. + * Added optional file search order parameter to love.filesystem.setIdentity. + * Added File:isOpen and File:getMode. + * Added Fie:setBuffer, File:getBuffer, and File:flush. + * Added textinput event for unicode text input. + * Added love.keyboard.setTextInput and love.keyboard.hasTextInput. + * Added previously internal Rasterizer and GlyphData object methods. + * Added support for UTF-8 ImageFonts. + * Added Font:getAscent/getDescent/getBaseline. + * Added Font:setFilter/getFilter. + * Added Font:hasGlyphs. + * Added angle, scale, and shear parameters to love.graphics.printf. + * Added HDR canvas support. + * Added mipmapping support (has isSupported test). + * Added vertex shader support. + * Added boolean support to Shader:send. + * Added Canvas:getPixel. + * Added blend mode "replace". + * Added line join modes. + * Added Mesh objects, allowing for arbitrary textured polygons. + * Added multiple render target support to love.graphics.setCanvas. + * Added love.graphics.setColorMask. + * Added love.graphics.origin. + * Added love.graphics.getRendererInfo. + * Added love.graphics.getMaxImageSize. + * Added SpriteBatch:getCount and SpriteBatch:getBufferSize. + * Added SpriteBatch:getColor. + * Added ParticleSystem:emit. + * Added ParticleSystem:setInsertMode and ParticleSystem:getInsertMode. + * Added many ParticleSystem getter methods. + * Added DXT compressed texture support via love.image.newCompressedData. + * Added love.image.isCompressed and Image:isCompressed. + * Added Image/Canvas/ImageData:getDimensions. + * Added anisotropic filtering support for Images, Canvases, and Fonts. + * Added Image:refresh. + * Added Image:getData. + * Added SoundData:getDuration and SoundData:getSampleCount. + * Added Source:isPlaying. + * Added Source:setRelative and Source:isRelative. + * Added Source:setCone and Source:getCone. + * Added Source:getChannels. + * Added new Channels API for love.thread. + * Added limited table support to Channel:push. + * Added Thread:getError. + * Added Thread:isRunning. + * Added threaderror event. + * Added love.math module. + * Added a platform-independent (good) random implementation to love.math. + * Added RandomGenerator objects. + * Added BezierCurve objects. + * Added love.math.triangulate and love.math.isConvex. + * Added love.math.noise. + * Added love.timer.getAverageDelta. + * Added Data:getString. + * Added Contact:getChildren. + * Added love.system module. + * Added love.system.getClipboardText and love.system.setClipboardText. + * Added love.system.getOS and love.system.getProcessorCount. + * Added love.window module. + * Added love.window.isVisible. + * Added flags to love.window.setMode. + * Added monitor choosing support to love.window.setMode. + * Added support for resizable, borderless, and non-centered windows. + * Added support for "fullscreen-desktop" mode. + * Added window resize and visible events. + * Added love.window.getDimensions. + * Added love.window.getIcon. + * Added t.window.icon to love.conf. + * Added love.mousefocus and love.window.hasMouseFocus. + * Added custom hardware cursors via love.mouse.newCursor. + * Added love.mouse.setX/setY. + * Added Joystick objects. + * Added love.joystick.getJoystick. + * Added joystick connect and disconnect events. + * Added joystickaxis and joystickhat events. + * Added unified Gamepad API for joysticks which have a similar layout to the Xbox controller. + * Added joystick vibration support, works with most common gamepads. + * OPTIONAL: Added support for Game Music Emu. + + * Fixed fused mode in OS X. + * Fixed printing to the console in Windows before love.load is called. + * Fixed the default love.run to not include the time taken by love.load in the first frame's dt. + * Fixed the error screen not always appearing until the next input event. + * Fixed love.event.clear. + * Fixed love.mouse.setPosition when called in love.load. + * Fixed scaling in several love.physics functions. + * Fixed Box2D exception in World:update. + * Fixed many uncaught Box2D / love.physics exceptions for Bodies and Joints. + * Fixed ChainShape:getPoints running out of Lua stack space and crashing. + * Fixed File:read reading past end of file. + * Fixed love.filesystem.setIdentity not removing read access from old directories. + * Fixed possible memory leak in utf-8 decoder. + * Fixed spacing for the last character in an ImageFont. + * Fixed line wrapping in love.graphics.printf. + * Fixed love.graphics.printf to error if the wrap limit is negative. + * Fixed love.graphics.print truncating strings with embedded zeros. + * Fixed crashes with font drawing on some ATI cards. + * Fixed artifacts when drawing lines at huge scale. + * Fixed Fonts and Canvases ignoring default image filter. + * Fixed scissor boxes when a canvas is set after love.graphics.setScissor is called. + * Fixed love.graphics.getLineWidth returning incorrect values. + * Fixed love.graphics.getColor on some Windows systems. + * Fixed alpha blend mode. + * Fixed multiplicative blend mode. + * Fixed love.graphics.getPointStyle. + * Fixed line numbers in shader errors. + * Fixed Shader:send with Images and Canvases failing sometimes. + * Fixed Shader:send to keep a reference to sent Images and Canvases. + * Fixed crash when binding SpriteBatches multiple times. + * Fixed SpriteBatches with more than 16,384 sprites. + * Fixed particle draw order for ParticleSystems. + * Fixed ParticleSystem:setSizes resetting the size variation. + * Fixed the graphics viewport not matching the window size when using an unsupported fullscreen mode. + * Fixed getMode and friends returning wrong values when using desktop size. + * Fixed keyrepeat settings being lost after (indirect) setMode. + * Fixed the icon being reset after setMode. + * Fixed memory leak in the mp3 decoder. + * Fixed sound issues with some versions of OpenAL soft, by enabling direct channels. + * Fixed 'random' hangs in audio. + * Fixed love.sound.newDecoder not accepting FileData. + * Fixed case (in)sensitivity of sound file extension parsing. + * Fixed looping support in tracker music formats. + * Fixed skipping/looping issues when playing streaming audio Sources. + * Fixed race condition in Source:play. + * Fixed WAVE sound playback. + + * Moved love's startup to modules/love. + * Moved window-related functions from love.graphics to love.window. + + * Renamed love's boot script to 'love.boot', which can be required. + * Renamed love.filesystem.mkdir to love.filesystem.createDirectory. + * Renamed love.filesystem.enumerate to love.filesystem.getDirectoryItems. + * Renamed World:setAllowSleeping to World:setSleepingAllowed. + * Renamed ChainShape:setPrevVertex to ChainShape:setPreviousVertex. + * Renamed Joint:enableMotor to Joint:setMotorEnabled. + * Renamed Joint:enableLimit and Joint:isLimitEnabled to Joint:setLimitsEnabled and Joint:hasLimitsEnabled. + * Renamed t.screen to t.window in love.conf. + * Renamed love.graphics.setCaption to love.window.setTitle. + * Renamed PixelEffect to Shader (but now with vertex shaders). + * Renamed love.graphics.setDefaultImageFilter to love.graphics.setDefaultFilter. + * Renamed ParticleSystem:setSprite to ParticleSystem:setImage. + * Renamed ParticleSystem:setGravity to ParticleSystem:setLinearAcceleration. + * Renamed ParticleSystem:setLifetime/setParticleLife to setEmitter/ParticleLifetime. + * Renamed ParticleSystem:count and all getNum* functions to get*Count. + * Renamed Source:setDistance to Source:setAttenuationDistances. + * Renamed SoundData:getBits and Decoder:getBits to SoundData:getBitDepth and Decoder:getBitDepth. + * Renamed love.mouse.setGrab to love.mouse.setGrabbed. + + * Removed release mode. + * Removed love.keyboard.getKeyRepeat (see love.keyboard.hasKeyRepeat). + * Removed the unicode argument from love.keypressed (see love.textinput). + * Removed love.graphics.drawTest. + * Removed love.graphics.quad/triangle. + * Removed love.graphics.setColorMode. + * Removed love.graphics.newStencil. + * Removed love.graphics.setLine/setPoint. + * Removed love.graphics.drawq (functionality is merged into love.graphics.draw). + * Removed SpriteBatch:addq/setq (functionality is merged into SpriteBatch:add/set). + * Removed Quad:flip. + * Removed ParticleSystem:isFull/isEmpty. + * Removed ParticleSystem:getX/getY. + * Removed love.graphics.checkMode. + * Removed love.joystick.open and friends. + * Removed love.joystick module functions which operated on individual joysticks (see Joystick objects). + * Removed joystick ball support. + * Removed thread names. + * Removed old thread messaging API (see Channels). + * Removed love.timer.getMicroTime. + + * Updated functions which return love objects to re-use the Lua-side object instead of always recreating it. + * Updated the windows console, it now tries to re-use an active one first. + * Updated error handling, error handlers now get resolved when the error occurs. + * Updated order of sleep/present in love.run (now draws, *then* sleeps). + * Updated love.filesystem to try to create the appdata directory if it doesn't exist yet. + * Updated the default filesystem identity to omit file extension. + * Updated love.filesystem.newFile to optionally open the file. + * Updated most love.filesystem functions to return nil, error on internal failure. + * Updated love.keyboard.setKeyRepeat to take a boolean argument instead of numbers. + * Updated love.keypressed's second argument to be a boolean indicating key repeat. + * Updated keyboard key constants for some more modern keyboard keys. + * Updated window code to use adaptive vsync when available, if vsync is enabled. + * updated love.graphics.print's x and y arguments to default to 0. + * Updated the setFilter and setWrap methods, the second argument is now optional. + * Updated Font and ParticleSystem rendering code, now more performant. + * Updated SpriteBatch code, now more performant when adding/setting and (un)binding. + * Updated Canvas code to support more systems. + * Updated Canvas:getImageData and love.graphics.newScreenshot to be more efficient. + * Updated love.graphics.newScreenshot to create a fully opaque image by default. + * Updated error messages when sending bad values to Shaders. + * Updated love.graphics.newParticleSystem to have a default buffer size of 1000. + * Updated ImageData:setPixel to accept a table and default to 255 alpha. + * Updated ImageData:mapPixel, is now more efficient and accepts optional x,y,w,h arguments. + * Updated love.image memory handling, improves errors and thread-safety. + * Updated all love object constructors to optionally accept FileData if they accept a filename. + * Updated allocation for SoundData, it's more efficient and less wasteful. + * Updated SoundData:set/getSample to error for invalid samples. + * Updated Source:set* functions to default z to 0. + * Updated Source:seek to error for negative offsets. + * Updated Thread:start to accept arguments which get passed to the thread. + * Updated love.timer.getFPS to be microsecond-accurate. + * Updated love.timer.getTime to be microsecond-accurate and monotonic. + * Updated Box2D to version 2.3.0. + +LOVE 0.8.0 [Rubber Piggy] +------------------------- + + Released: 2012-04-02 + + * Added release error screen. + * Added alpha to love.graphics.setBackgroundColor. + * Added Canvas:clear(r, g, b, a). + * Added Canvas support to love.graphics.drawq. + * Added Canvas:getWidth and Canvas:getHeight. + * Added love.graphics.arc. + * Added seek and tell to Source objects. + * Added color interpolation to ParticleSystem. + * Added automatic PO2 padding for systems not supporting the OpenGL extension. + * Added UTF-8 support for fonts. + * Added Box2D error handling for some commonly failing functions. + * Added ability for fused release games to have their write dir in appdata. + * Added shear transformation to drawing functions. + * Added origin to font printing. + * Added love.graphics.getMode. + * Added per-sprite colors on SpriteBatches. + * Added PixelEffects. + * Added love.graphics.isSupported. + * Added love.graphics.getCanvas. + * Added love.event.quit. + * Added stencil masks. + * Added alternative SpriteBatch provider, it should work everywhere now. + * Added a loader for binary modules. + * Added Thread:getKeys. + * Added option of fractions for Quads. + * Added PNG, JPEG and GIF support to ImageData:encode. + * Added 64-bit support for Mac OS X. + * Added premultiplied blending mode. + * Added functions to set/get default image filter modes. + * Added SpriteBatch:set. + * Added new events system, with support for custom events and long event names. + * Added sound attenuation by distance. + * Added SpriteBatch:getImage. + * Added combine color mode. + * Added automatic random seeding to love.run. + * Added support for the subtract BlendMode on older graphics cards. + * Added love._os field, which contains the OS the game is running on. + + * Fixed wrapping for single words. + * Fixed tracebacks not showing filenames. + * Fixed love.graphics.push/pop capable of causing overflows/underflows. + * Fixed setScissor on Canvases. + * Fixed several issues with audio, e.g. clicks and pops in mp3s. + * Fixed crashes when bodies were destroyed during collisions. + * Fixed bound SpriteBatches corrupting when drawing. + * Fixed thread-safety issues with ImageData. + * Fixed memory leaks in audio sources. + * Fixed thread's set (previously send) accidentally changing the type. + * Fixed SoundData allocating the wrong number of samples. + * Fixed SpriteBatch support on Intel cards. + * Fixed love.filesystem.lines() leaking. + * Fixed most leaking on unclosed File objects. + * Fixed crashes when operating on non-existent files. + * Fixed a bug where empty files on windows would never reach eof. + * Fixed crash when SoundData runs out of memory. + * Fixed ordering of loaders, love should have priority over lua. + * Fixed several miscellaneous memory leaks. + * Fixed a few cases where strings with \0 in them would not be stored correctly. + * Fixed love's startup time being in the first dt. + * Fixed internal string conversions, they are faster now. + * Fixed (bad) performance of ImageData:paste. + * Fixed love.graphics.toggleFullscreen not maintaining graphics state. + + * Renamed SpriteBatch's lock/unlock to bind/unbind. + * Renamed Framebuffer to Canvas. + * Renamed love.thread.send/receive to set/get. + * Renamed love.graphics.setRenderTarget to setCanvas. + + * Removed canvas auto-clearing. + * Removed EncodedImageData. + * Removed old syntax for require (with extension). + * Removed love.graphics.setFont([file], [size]). + * Removed Thread:kill. + + * Updated love.joystick to be 1-indexed. + * Updated Sources to update more cleanly and control more intuitively. + * Updated font engine. + * Updated line drawing to a custom system. + * Updated love.timer.sleep to use seconds, like the rest of love. + * Updated love.timer to be more accurate. + * Updated love.graphics.circle to have max(10, r) as default for segments. + * Updated ImageData:encode to write to files directly. + * Updated version compatibility system to actually do something. + * Updated love.run's order, events are checked just before update. + * Updated Box2D to version 2.2.1. + +LOVE 0.7.2 [Game Slave] +----------------------- + + Released: 2011-05-04 + + * Added Framebuffer:get/setWrap. + * Added love.event.clear. + * Added support for any number of arguments to love.keyboard.isDown, love.mouse.isDown and love.joystick.isDown. + * Added SpriteBatch:setImage(). + + * Fixed fused games not working. + * Fixed ParticleSystem:setSize ignoring the variation argument. + * Fixed some file-opening exceptions not being caught. + * Fixed files loaded by libmodplug being too loud. + * Fixed paths with periods in them not working. + * Fixed love.graphics.getBlendMode not detecting subtractive and multiplicative blend modes. + * Fixed crash when there was no memory available for newImageData(w, h). + + * Updated PhysicsFS version to 2.0.2 on Windows + * Updated OpenAL Soft version to 1.13 on Windows + * Updated libmodplug version to 0.8.8.1 on Windows + * Updated FreeType version to 2.4.4 on Windows + * Updated libmpg123 version to 1.13.2 on Windows + * Windows binary no longer depends on VC2005 runtime. + * Windows binary no longer depends on SSE2 support. + +LOVE 0.7.1 [Game Slave] +----------------------- + + Released: 2011-02-14 + + * Added source:isPaused() + * Added error when initial window can't be created. + * Added framebuffer filter modes. + * Added love.filesystem.getLastModified. + * Added filter modes for ImageFonts. + * Added dead key support by using "unknown" key with correct unicode value. + * Added 0 width and height in love.conf. (for current desktop resolution) + * Added alpha support when encoding TGA images. + + * Fixed a lot of bugs regarding zero characters in threads. + * Fixed handling of a directory named "love" in current directory. + * Fixed a few unhandled errors in setScissor. + * Fixed a bug where old physics callbacks were never dereferenced. + * Fixed loss of mouse visibility settings on setMode. + * Fixed creation of a framebuffer unbinding current framebuffer. + * Fixed several race conditions in love.thread. + * Fixed 'love .', so it won't use lovedir/. as save dir. + * Fixed setLineHeight. + * Fixed extended ascii and ImageFonts. + * Fixed printf's line wrapping. + * Fixed crash when playing sounds. + * Fixed playback of mp3s with arbitrary sample rates. + * Fixed handling of negative indices in love.joystick. + * Fixed toggleFullscreen. + * Fixed unexpected behaviour with hash tables to love.graphics.line. + * Fixed mouse coordinates being capped after setMode. + * Fixed setFont's error handling on a non-existant file. + * Fixed issue where Windows builds would hard crash on Lua errors + + * Removed custom sample rates for Decoders. + +LOVE 0.7.0 [Game Slave] +----------------------- + + Released: 2010-12-05 + + * Added love.thread. + * Added love.font. + * Added love.graphics.Framebuffer. + * Added Source:play, Source:pause, etc. + * Added Source:isStatic(). + * Added get/setPosition, get/setVelocity, and get/setDirection to Source. + * Added get/setGroupIndex to CircleShape and PolygonShape. + * Added Font:getWrap. + * Added identity field to love.conf. + * Added love.quit callback. + * Added love.focus callback. + * Added extra meter parameter to love.physics.newWorld. + * Added love.graphics.setIcon. + * Added way to make the window desktop resolution. + * Added subtractive and multiplicative blend modes. + * Added body:getAllowSleeping. + * Added shape:getBody. + * Added love.filesystem.FileData for public usage. + * Added base64 support for love.filesystem.FileData. + * Added table support for love.graphics.setColor and love.graphics.setBackgroundColor. + * Added love.graphics.hasFocus(). + * Added ?/init.lua to the loader. + + * Fixed the debug module not being an upvalue of the error handlers. (you can now override debug) + * Fixed some cases when love.audio.pause and friends, were acting on everything, not just the passed Source. + * Fixed setFixedRotation enabling other flags. + * Fixed a bug in the loader (for require). + * Fixed ParticleSystem::setSprite not retaining the new image. + * Fixed setMode removing images settings (wrapping, filters). + * Fixed shape:getBody, it's now exposed for LÖVE usage. + * Fixed DistanceJoint:getType() returning "circle" - it now returns "distance". + * Fixed SpriteBatches being unaffected by setColor + * Fixed the audio bug. + * Fixed invalid FSAA values crashing LÖVE. + * Fixed a bunch of compiler warnings. + * Fixed OS X not properly using UTIs for .love files. + * Fixed the modplug decoder not properly handeling files that fail to load. + * Fixed a memory leak in setFont. + * Fixed bug where errors in threads wouldn't get picked up by demand. + * Fixed part of the bug with newlines when scaling text (rotating still messes up the lines). + * Fixed the bug where newImageFont would try to created ImageData out of ImageData. + * Fixed error handler not resetting the blend mode. + + * Changed fonts, they're now po2 safe. + * Changed the traceback in the error screen. + * Changed font origin to top-left. + * Changed linux save dir location to obey to Freedesktop.org's XDG specs. (~/.local/share/love by default.) + + * Removed font functions from love.graphics. + * Removed love.physics.newWorld(w, h). Use love.physics.newWorld(x1, y1, x2, y2) instead. + +LOVE 0.6.2 [Jiggly Juice] +------------------------- + + Released: 2010-03-06 + + * Fixed a bug causing ImageFonts to cut off some pixels. + * Fixed a bug where filled rectangles were too small. + * Fixed a bug in Image:setFilter where it would switch the parameters. + * Fixed a bug in ImageRasterizer where it wasn't using the data. + * Image filter and wrap modes now use string constants as well. + * Fixed double-transform bug in SpriteBatch. + * Errors are reported on stdout again. + * Another fix for the icons on ubuntu. + +LOVE 0.6.1 [Jiggly Juice] +------------------------- + + Released: 2010-02-07 + + * Added Shape:setGroupIndex and getGroupIndex. + * Added Body:setFixedRotation and Body:getFixedRotation. + * Added Body:setInertia. + * Added CircleShape:getLocalCenter and CircleShape:getWorldCenter. + * Added icons and file associations for the debs. + * Added the demos folder to the Mac OS X DMG. + * It's now possible to run a .love from Resources in Mac OS X, thanks to Steve Johnson. + * Fixed a bug with multiple Sources on the same Music. + * Fixed a bug so the mouse doesn't get crippled when the keyboard is disabled. + * Fixed a bug where love.graphics.rectangle drew a too large rectangle. + * Fixed a bug where memory wouldn't be released correctly. + * Fixed epic physics typo (getRestituion->getRestitution). + * Fixed crash on opening non-existent image. + * The error screen redraws when an event occurs. + * The default love.run() now gracefully handles disabled modules. + * The debian packages should now successfully include icons, file associations, etc, and should give the correct architecture. + * Added support for drawing polylines to love.graphics.line - the syntax is the same as love.graphics.polygon. + * Removed Music and Sound. There are now only sources. + * Improved the stability of love.audio/love.sound. + +LOVE 0.6.0 [Jiggly Juice] +------------------------- + + Released: 2009-12-24 + + * Lost track of 0.6.0 changes a long while ago. Don't trust the list below. + + * Added love.graphics.print()/printf(). + * Added unicode-translated parameter to love.keypressed(). + * Added love.event. + * Added love.filesystem.setIdentity(). + * Added OpenAL dependency. + + * Fixed love.fileystem problems with internal \0 in strings. + * Fixed love.filesystem.mkdir/remove not working when write directory not set. + * Fixed position of Window. + + * Changed parameter order of draws(). + * Changed origin for images to top-left. + * Changed love.filesystem.open to accept mode (removed from love.filesystem.newFile). + * Changed love.filesystem.read() which now returns two parameters (data, length). + * Changed love.filesystem.write() which now takes up to four parameters (file, data, length, mode). + * Changed default color mode to "modulate". + * Changed name of love.color_normal to "replace". + * Changed name of love.blend_normal to "alpha". + * Changed the conf file format. + + * Removed Color object. + * Removed Animation. + * Removed several constants. + * Removed love.graphics.draw() for strings. + * Removed love.system. + * Removed SWIG. + * Removed boost. + * Removed SDL_mixer. + + +LOVE 0.5.0 [Salted Nuts] +------------------------ + + Released: 2009-01-02 + + * Added love.joystick. + * Added network support via LuaSocket. + * Added support for loading of appended .love-file. + + * Added love.filesystem.lines(). + * Added a loader function to enable use of normal require(). + * Added love.filesystem.load(). + * Added love.filesystem.getSaveDirectory() + * Added love.filesystem.getWorkingDirectory() + + * Added optional explicit destruction of Box2D objects. + * Added shape:testSegment(). + * Added love.graphics.screenshot() (.bmp only). + * Added default size (12) to font-related functions. + * Added love.graphics.setFont( filename, size ) + * Added love.graphics.setLineStippe and related functions. + * Added love.graphics.setPointSize and related functions. + + * Changed love.filesystem.read() to accept file name. + * Changed love.filesystem.write() to accept file name. + * Changed love.graphics.triangle() to accept CCW and CW ordering. + + * Fixed love.graphics.read adding bogus characters at the end of string. + * Fixed epic swigfusion bug. + * Fixed love.graphics.getFont so it returns nil if no font is present. + * Fixed bug where love.graphics.getBlendMode() always returns blend_normal. + * Fixed bug which caused error screen to be scissored (when enabled). + * Fixed Body:setAngle to accept degrees like everything else. + + * Cleaned up love::File and love_physfs. + * Cleaned up love::Reference so it stores its reference in _G. + +LOVE 0.4.0 [Taco Beam] +---------------------- + + Released: 2008-08-29 + + * Added love.physics. (YES!) + * Added love.audio.setMode(). + * Added love.audio.setChannels(). + * Added love.graphics.polygon(). + * Added love.graphics.setScissor() and love.graphics.getScissor() to handle scissoring the graphical area. + * Fixed missing constants related to image optimization. + * Fixed memory leak related to love::File (thanks amnesiasoft!). + + +LOVE 0.3.2 [Lemony Fresh] +------------------------- + + Released: 2008-07-04 + + * Added love.graphics.rectangle() + * Added love.graphics.setLineWidth() + * Added love.graphics.setLineStyle() + * Added love.graphics.getLineWidth() + * Added love.graphics.getLineStyle() + * Added love.mouse.getPosition() + * Added love.audio_loop + * Added love.timer.getTime() + * Changed love.graphics.quad() to accept CCW and CW ordering. + * Fixed default color mode bug. + * Fixed line width being applied unnecessarily. + * Fixed line width bug related to fullscreen toggle. + * Fixed music not looping. + +LOVE 0.3.1 [Space Meat] +----------------------- + + Released: 2008-06-21 + + * Fixed segfault related to graphics. + * Fixed wait-forever bug related to audio. + * Fixed error reporting not working across modules. + * Fixed bug where games with a trailing "/" would not start. + * Fixed bug which caused love.timer.sleep to delay for (way) too long. + +LOVE 0.3.0 [Mutant Vermin] +-------------------------- + + Released: 2008-06-17 + + * Added ParticleSystem. + * Added visual error reporting. + * Added love.system for game control needs. + * Added input grabbing. + * Added functions in love.graphics for display management. + * Added love.graphics.point(). + * Added functions in love.graphics for getting current color, font, etc. + * Added love.filesystem.enumerate() for getting folder contents. + * Added functions for setting the window caption. + * Added version checking. An error occurs if the game is incompatible. + * Fixed print() :) + * Removed all keyboard shortcuts. + * Save folders are now created only if required. + * On Windows, the new save location is %APPDATA%\LOVE\game + +LOVE 0.2.1 [Impending Doom] +--------------------------- + + Released: 2008-03-29 + + * Added many functions in love.filesystem. + * Added a dedicated save-folder for each game. + * Added timer.sleep. + * Added line heights to font objects. + * Added love.graphics.getWidth/getHeight. + * Added scaling and rotation for text. + * Added variable spacing to ImageFont. + * Added support for variable line quality when drawing primitives. + * Added several functions for drawing sections of images. (love.graphics.draws) + * Added image optimization function and padding function. + * Added love.graphics.getWidth/Height. + + * Split devices up into actual SWIG-modules. This means that: + - Functions are used like this: love.graphics.draw, not love.graphics:draw + - love.objects is no more. Objects are created by an appropriate device. + * How you draw primitives has been altered. + * draw(string, x, y, wrap, align) has become drawf(string, x, y, wrap, align) + + * Changed getFps to getFPS. + * Escape is no more ... enter: Alt+F4. + * love.filesystem.include has been renamed to love.filesystem.require. + * ImageFonts now consider the spacing as well as the glyph size. + * Fixed a massive ImageFont bug which resulted in float-positioning failure. + * Fixed a bug when loading fonts where the specified size doesn't represent the true size of the font. + + * Updated DevIL to version 1.6.8-rc2 (Windows) + * Updated FreeType to freetype-2.3.5-1 (Windows) + * Updated Lua to 5.1.3 (Windows) + * Updated SDL to 1.2.13 (Windows) + * Removed boost::filesystem. + +LOVE 0.2.0 [Mini-Moose] +----------------------- + + Released: 2008-02-06 + + * Added ImageFont + * Added Animation + * Added text formatting functions + * Added setCenter for Image and Animation. + * Added methods for rendering of scaled/rotated sprites. + * Added the drawing of basic shapes. + * Added default font and embedded resources. + * Added Ctrl+R for reload. + * Added blending and color modes. + * Fixed memory usage of Graphics. + * Fixed a bug where the set text color would change the color of any images rendered. + * Fixed CWD bug. + * Fixed titlebar. Game title is now displayed. + + +LOVE 0.1.1 [Santa-Power] +------------------------ + + Initial release! + Released: 2008-01-13 + + * Image loading and rendering. + * Sound loading and playing. + * Font loading and rendering. + * Lua-scriptable games. + * Config files. + * Stuff is loadable from archive files. + * Keyboard, mouse, display, timer, etc. (Basic devices). diff --git a/engine/game.ico b/engine/game.ico new file mode 100644 index 0000000..ecc5c0d Binary files /dev/null and b/engine/game.ico differ diff --git a/engine/license.txt b/engine/license.txt new file mode 100644 index 0000000..f0ef44d --- /dev/null +++ b/engine/license.txt @@ -0,0 +1,977 @@ +This software uses LOVE: + +LOVE is Copyright (c) 2006-2016 LOVE Development Team + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +--------- + +This software uses LuaJIT: + +LuaJIT is Copyright (c) 2005-2015 Mike Pall + +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. + +--------- + +This software uses ENet: + +Copyright (c) 2002-2014 Lee Salzman + +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. + +--------- + +This software uses lua-enet: + +Copyright (C) 2011 by Leaf Corcoran + +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. + +--------- + +This software uses UTF8-CPP: + +Copyright 2006 Nemanja Trifunovic + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +--------- + +This software uses the following LGPL libraries on Windows, Mac OS X, Linux, +and Android: + + - libmpg123 + Website: http://www.mpg123.de/ + Source download: http://sourceforge.net/projects/mpg123/files/latest/download + - OpenAL Soft + Website: http://kcat.strangesoft.net/openal.html + Source download: http://kcat.strangesoft.net/openal.html#download + +Following are the LGPL and GPL license texts: + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code +keep intact all notices of the absence of any warranty and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/engine/love.dll b/engine/love.dll new file mode 100644 index 0000000..849a1ce Binary files /dev/null and b/engine/love.dll differ diff --git a/engine/love.exe b/engine/love.exe new file mode 100644 index 0000000..1d2172a Binary files /dev/null and b/engine/love.exe differ diff --git a/engine/love.ico b/engine/love.ico new file mode 100644 index 0000000..b703c98 Binary files /dev/null and b/engine/love.ico differ diff --git a/engine/lua51.dll b/engine/lua51.dll new file mode 100644 index 0000000..78d2608 Binary files /dev/null and b/engine/lua51.dll differ diff --git a/engine/mpg123.dll b/engine/mpg123.dll new file mode 100644 index 0000000..84da7db Binary files /dev/null and b/engine/mpg123.dll differ diff --git a/engine/msvcp120.dll b/engine/msvcp120.dll new file mode 100644 index 0000000..a237d2d Binary files /dev/null and b/engine/msvcp120.dll differ diff --git a/engine/msvcr120.dll b/engine/msvcr120.dll new file mode 100644 index 0000000..8c36149 Binary files /dev/null and b/engine/msvcr120.dll differ diff --git a/engine/readme.txt b/engine/readme.txt new file mode 100644 index 0000000..c01c637 --- /dev/null +++ b/engine/readme.txt @@ -0,0 +1,96 @@ +LÖVE is an *awesome* framework you can use to make 2D games in Lua. It's free, open-source, and works on Windows, Mac OS X, Linux, Android, and iOS. + +[![Build Status: Windows](https://ci.appveyor.com/api/projects/status/u1a69u5o5ej1pus4?svg=true)](https://ci.appveyor.com/project/AlexSzpakowski/love) + +Documentation +------------- + +We use our [wiki][wiki] for documentation. +If you need further help, feel free to ask on our [forums][forums], and last but not least there's the irc channel [#love on OFTC][irc]. + +Compilation +----------- + +###Windows +Follow the instructions at the [megasource][megasource] repository page. + +###*nix +Run `platform/unix/automagic` from the repository root, then run ./configure and make. + + $ platform/unix/automagic + $ ./configure + $ make + +###Mac OS X +Download the required frameworks from [here][dependencies] and place them in `/Library/Frameworks/`. + +Then use the Xcode project found at `platform/xcode/love.xcodeproj` to build the `love-macosx` target. + +###iOS +Download the required libraries from [here][dependencies-ios] and place the `include` and `libraries` folders +into the `platform/xcode/ios` folder. + +Then use the Xcode project found at `platform/xcode/love.xcodeproj` to build the `love-ios` target. + +See `readme-iOS.rtf` for more information. + +###Android +Visit the [Android build repository][android-repository] for build instructions. + +Repository information +---------------------- + +We use the 'default' branch for development, and therefore it should not be considered stable. +Also used is the 'minor' branch, which is used for features in the next minor version and it is +not our development target (which would be the next revision - version numbers are formatted major.minor.revision.) + +We tag all our releases (since we started using mercurial), and have binary downloads available for them. + +Experimental changes are developed in the separate [love-experiments][love-experiments] repository. + +Contributing +------------ + +The best places to contribute are through the Bitbucket issue tracker and the official IRC channel. +For code contributions, pull requests and patches are welcome. Be sure to read the [source code style guide][codestyle]. + +Builds +------ + +Releases are found in the 'downloads' section on bitbucket, are linked on [the site][site], +and there's a ppa for ubuntu, [ppa:bartbes/love-stable][stableppa]. + +There are also unstable/nightly builds: + +- Most can be found [here][builds]. +- For ubuntu linux they are in [ppa:bartbes/love-unstable][unstableppa] +- For arch linux there's [love-hg][aur] in the AUR. + +Dependencies +------------ + +- SDL2 +- OpenGL 2.1+ / OpenGL ES 2+ +- OpenAL +- Lua / LuaJIT / LLVM-lua +- FreeType +- PhysicsFS +- ModPlug +- mpg123 +- Vorbisfile +- Theora + +[site]: http://love2d.org +[wiki]: http://love2d.org/wiki +[forums]: http://love2d.org/forums +[irc]: irc://irc.oftc.net/love +[dependencies]: http://love2d.org/sdk +[dependencies-ios]: https://bitbucket.org/rude/love/downloads/love-0.10.0-ios-libraries.zip +[megasource]: https://bitbucket.org/rude/megasource +[builds]: http://love2d.org/builds +[stableppa]: https://launchpad.net/~bartbes/+archive/love-stable +[unstableppa]: https://launchpad.net/~bartbes/+archive/love-unstable +[aur]: http://aur.archlinux.org/packages/love-hg +[love-experiments]: https://bitbucket.org/bartbes/love-experiments +[codestyle]: https://love2d.org/wiki/Code_Style +[android-repository]: https://bitbucket.org/MartinFelis/love-android-sdl2 diff --git a/lua5.1.dll b/lua5.1.dll new file mode 100644 index 0000000..19a03f1 Binary files /dev/null and b/lua5.1.dll differ diff --git a/net/chatting.lua b/net/chatting.lua new file mode 100644 index 0000000..09393df --- /dev/null +++ b/net/chatting.lua @@ -0,0 +1,51 @@ +require("net") +--General Stuff +--[[ What this module does! +Adds +net.chatting:init() +server:OnChatRecieved(function({user,msg}) end) +client:OnChatRecieved(function(user,msg) end) +client:sendChat(user,msg) +]] +net:registerModule("chatting",{1,0,0}) +function net.chatting:init() -- calling this initilizes the library and binds it to the servers and clients created + --Server Stuff + net.OnServerCreated:connect(function(s) + print("The Chatting Module has been loaded onto the server!") + s.OnDataRecieved:connect(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered + --First Lets make sure we are getting chatting data + local user,msg = data:match("!chatting! (%S-) '(.+)'") + if user and msg then + local struct={ -- pack the info up as a table so the server can do filtering and whatnot to the chat + user=user, + msg=msg + } + self.OnChatRecieved:Fire(struct) -- trigger the chat event + self:sendAll("!chatting! "..struct.user.." '"..struct.msg.."'") + end + end) + s.rooms={} + function s:regesterRoom(roomname) + self.rooms[roomname]={} + end + s.OnChatRecieved=multi:newConnection() -- create a chat event + end) + --Client Stuff + net.OnClientCreated:connect(function(c) + c.OnDataRecieved:connect(function(self,data) -- when the client recieves data this method is triggered + --First Lets make sure we are getting chatting data + local user,msg = data:match("!chatting! (%S-) '(.+)'") + if user and msg then + --This is the client so our job here is done + self.OnChatRecieved:Fire(user,msg) -- trigger the chat event + end + end) + function c:sendChat(user,msg) + self:send("!chatting! "..user.." '"..msg.."'") + end + c.OnChatRecieved=multi:newConnection() -- create a chat event + end) +end +if net.autoInit then + net.chatting:init() +end diff --git a/net/email.lua b/net/email.lua new file mode 100644 index 0000000..18de805 --- /dev/null +++ b/net/email.lua @@ -0,0 +1,53 @@ +require("net.identity") +net:registerModule("email",{1,0,0}) +smtp = require 'socket.smtp' +ssl = require 'ssl' + +function net.email.init(from,user,pass) + net.OnServerCreated:connect(function(s) + s.from=from + s.user=user + s.pass=pass + function s:sendMessage(subject, body, dTable) + local msg = { + headers = { + from = '<'..dTable.email..'>' + to = dTable.nick..' <'..dTable.email..'>', + subject = subject + }, + body = body + } + local ok, err = smtp.send { + from = '<'..self.from..'>', + rcpt = '<'..dTable.email..'>', + source = smtp.message(msg), + user = self.user, + password = self.pass, + server = 'smtp.gmail.com', + port = 465, + create = net.sslCreate + } + if not ok then + print("Mail send failed", err) -- better error handling required + end + end + end) +end +function net.sslCreate() + local sock = socket.tcp() + return setmetatable({ + connect = function(_, host, port) + local r, e = sock:connect(host, port) + if not r then return r, e end + sock = ssl.wrap(sock, {mode='client', protocol='tlsv1'}) + return sock:dohandshake() + end + }, { + __index = function(t,n) + return function(_, ...) + return sock[n](sock, ...) + end + end + }) +end + diff --git a/net/identity.lua b/net/identity.lua new file mode 100644 index 0000000..2cf9d0f --- /dev/null +++ b/net/identity.lua @@ -0,0 +1,192 @@ +require("net") +--General Stuff +--[[ What this module does! +Adds +net.identity:init() + +]] +net:registerModule("identity",{1,0,0}) +function net.hash(text,n) + n=n or 16 + return bin.new(text.."jgmhktyf"):getHash(n) +end +function net.identity:init() -- calling this initilizes the library and binds it to the servers and clients created + --Server Stuff + net.OnServerCreated:connect(function(s) + s.userFolder="./" + print("The identity Module has been loaded onto the server!") + function s:_isRegistered(user) + return io.fileExists(self.userFolder..net.hash(user)..".dat") + end + function s:getUserData(user) + local userdata=bin.load(self.userFolder..net.hash(user)..".dat") + local nick,dTable=userdata:match("%S-|%S-|(%S-)|(.+)") + return nick,loadstring("return "..(dTable or "{}"))() + end + function s:getUserCred(user) + local userdata=bin.load(self.userFolder..net.hash(user)..".dat") + return userdata:match("%S-|(%S-)|") + end + function s:userLoggedIn(cid) + for i,v in pairs(self.loggedIn) do + if v.cid==cid then + return i + end + end + return false + end + function s:setDataLocation(loc) + self.userFolder=loc + end + function s:loginUserOut(user) + self.loggedIn[user]=nil + end + function s:loginUserIn(user,cid) + local nick,dTable=self:getUserData(user) + self.loggedIn[user]={} + table.merge(self.loggedIn[user],dTable or {}) + self.loggedIn[user].cid=cid + self.loggedIn[user].nick=nick + return self:getUserDataHandle(user) + end + function s:getUserDataHandle(user) + return self.loggedIn[user] + end + function s:syncUserData(user,ip,port) + local handle=self:getUserDataHandle(user) + self:send(ip,"!identity! SYNC <-|"..bin.ToStr(handle).."|->",port) + end + s.loggedIn={} + s.OnUserRegistered=multi:newConnection() + s.OnUserLoggedIn=multi:newConnection() + s.OnUserLoggerOut=multi:newConnection() + s.OnAlreadyLoggedIn=multi:newConnection() + s.OnPasswordForgotten=multi:newConnection() + s.OnDataRecieved:connect(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered + local cmd,arg1,arg2,arg3,arg4 = data:match("!identity! (%S-) '(.-)' '(.-)' '(.-)' <%-|(.+)|%->") + if cmd=="register" then + local user,pass,nick,dTable = arg1,arg2,arg3,arg4 + if self:_isRegistered(user) then + self:send(ip,"!identity! REGISTERED <-|"..user.."|->",port) + else + if not(self.userFolder:sub(-1,-1)=="/" or self.userFolder:sub(-1,-1)=="\\") then + self.userFolder=self.userFolder.."/" + end + local rets=self.OnUserRegistered:Fire(user,pass,nick,loadstring("return "..(dTable or "{}"))()) + for i=1,#rets do + if rets[i][1]==false then + print("Server refused to accept registration request!") + self:send(ip,"!identity! REGISTERREFUSED <-|NIL|->",port) + return + end + end + bin.new(string.format("%s|%s|%s|%s\n",user,pass,nick,dTable)):tofile(self.userFolder..net.hash(user)..".dat") + self:send(ip,"!identity! REGISTEREDGOOD <-|"..user.."|->",port) + end + return + elseif cmd=="login" then + local user,pass = arg1,arg2 + local _pass=s:getUserCred(user) + if not(self:_isRegistered(user)) then + self:send(ip,"!identity! LOGINBAD <-|nil|->",port) + return + end + print(pass,_pass) + if pass==_pass then + if self:userLoggedIn(cid) then + self.OnAlreadyLoggedIn:Fire(self,user,cid,ip,port) + self:send(ip,"!identity! ALREADYLOGGEDIN <-|nil|->",port) + return + end + local handle=self:loginUserIn(user,cid) -- binds the cid to username + self:send(ip,"!identity! LOGINGOOD <-|"..bin.ToStr(handle).."|->",port) + self.OnUserLoggedIn:Fire(user,cid,ip,port) + return + else + self:send(ip,"!identity! LOGINBAD <-|nil|->",port) + return + end + elseif cmd=="logout" then + self:loginUserOut(user) + self.OnClientClosed:Fire(self,"User logged out!",cid,ip,port) + elseif cmd=="sync" then + local dTable = loadstring("return "..(arg4 or "{}"))() + local handle = self:getUserDataHandle(self:userLoggedIn(cid)) + table.merge(handle,dTable) + elseif cmd=="pass" then + local user=arg1 + if self:_isRegistered(user) then + self.OnPasswordForgotten:Fire(arg1,cid) + self:send(ip,"!identity! PASSREQUESTHANDLED <-|NONE|->",port) + else + self:send(ip,"!identity! NOUSER <-|"..user.."|->",port) + end + end + end) + s.OnClientClosed:connect(function(self,reason,cid,ip,port) + self.OnUserLoggerOut:Fire(self,self:userLoggedIn(cid),cid,reason) + end) + end) + --Client Stuff + net.OnClientCreated:connect(function(c) + c.userdata={} + c.OnUserLoggedIn=multi:newConnection() + c.OnBadLogin=multi:newConnection() + c.OnUserAlreadyRegistered=multi:newConnection() + c.OnUserAlreadyLoggedIn=multi:newConnection() + c.OnUserRegistered=multi:newConnection() + c.OnNoUserWithName=multi:newConnection() + c.OnPasswordRequest=multi:newConnection() + c.OnUserRegisterRefused=multi:newConnection() + function c:logout() + self:send("!identity! logout 'NONE' 'NONE' 'NONE' <-|nil|->") + end + c.OnDataRecieved:connect(function(self,data) -- when the client recieves data this method is triggered + local cmd,arg1 = data:match("!identity! (%S-) <%-|(.+)|%->") + if cmd=="REGISTERED" then + self.OnUserAlreadyRegistered:Fire(self,arg1) + elseif cmd=="REGISTEREDGOOD" then + self.OnUserRegistered:Fire(self,arg1) + elseif cmd=="REGISTERREFUSED" then + self.OnUserRegisterRefused:Fire(self,arg1) + elseif cmd=="ALREADYLOGGEDIN" then + self.OnUserAlreadyLoggedIn:Fire(self,arg1) + elseif cmd=="LOGINBAD" then + self.OnBadLogin:Fire(self) + elseif cmd=="LOGINGOOD" then + local dTable=loadstring("return "..(arg1 or "{}"))() + table.merge(self.userdata,dTable) + self.OnUserLoggedIn:Fire(self,self.userdata) + elseif cmd=="SYNC" then + local dTable=loadstring("return "..(arg1 or "{}"))() + table.merge(self.userdata,dTable) + elseif cmd=="NOUSER" then + self.OnNoUserWithName:Fire(self,arg1) + elseif cmd=="PASSREQUESTHANDLED" then + self.OnPasswordRequest:Fire(self) + end + end) + function c:syncUserData() + self:send(string.format("!identity! sync 'NONE' 'NONE' 'NONE' <-|%s|->",bin.ToStr(dTable))) + end + function c:forgotPass(user) + self:send(string.format("!identity! pass '%s' 'NONE' 'NONE' <-|nil|->",user)) + end + function c:getUserDataHandle() + return self.userdata + end + function c:logIn(user,pass) + self:send(string.format("!identity! login '%s' '%s' 'NONE' <-|nil|->",user,net.hash(pass))) + end + function c:register(user,pass,nick,dTable) + if dTable then + self:send(string.format("!identity! register '%s' '%s' '%s' <-|%s|->",user,net.hash(pass),nick,bin.ToStr(dTable))) + else + self:send(string.format("!identity! register '%s' '%s' '%s' <-|nil|->",user,net.hash(pass),nick)) + end + end + end) +end +if net.autoInit then + net.identity:init() +end diff --git a/net/init.lua b/net/init.lua new file mode 100644 index 0000000..0b67ca1 --- /dev/null +++ b/net/init.lua @@ -0,0 +1,483 @@ +function string.trim(s) + local from = s:match"^%s*()" + return from > #s and "" or s:match(".*%S", from) +end +socket=require("socket") +net={} +net.Version={1,0,0} +net.OnServerCreated=multi:newConnection() +net.OnClientCreated=multi:newConnection() +net.loadedModules={} +net.autoInit=true +function net:registerModule(mod,version) + table.insert(self.loadedModules,mod) + net[mod]={} + if version then + net[mod].Version=version + else + net[mod].Version={1,0,0} + end +end +function net.getModuleVersion(ext) + if not ext then + return string.format("%d.%d.%d",net.Version[1],net.Version[2],net.Version[3]) + end + return string.format("%d.%d.%d",net[ext].Version[1],net[ext].Version[2],net[ext].Version[3]) +end +function net.resolveID(obj) + local num=math.random(10000000,99999999) + if obj[tostring(num)] then + return net.resolveID(obj) + end + obj.ids[tostring(num)]=true + return tostring(num) +end +function net.inList(list,dat) + for i,v in pairs(list) do + if v==dat then + return true + end + end + return false +end +function net.setTrigger(funcW,funcE) + multi:newTrigger(func) +end +-- UDP Stuff +function net:newServer(port,servercode) + local c={} + c.udp=assert(socket.udp()) + c.udp:settimeout(0) + c.udp:setsockname("*", port) + c.ips={} + c.Type="udp" + c.port=port + c.ids={} + c.servercode=servercode + c.bannedIPs={} + c.bannedCIDs={} + function c:setUpdateRate(n) + print("Not needed in a udp server!") + end + function c:banCID(cid) + table.insert(self.bannedCIDs,cid) + end + function c:banIP(ip) + table.insert(self.bannedIPs,cid) + end + function c:send(ip,data,port,cid) + if self.servercode then + cid=cid or self:CIDFrom(ip,port) + if not self.ips[cid] then + print("Can't determine cid from client... sending the client a new one!") + local cid=net.resolveID(self) + print("Sending unique cid to client: "..cid) + self.ips[cid]={ip,port,0,self.servercode==nil} + print(ip) + self.udp:sendto("I!"..cid,ip,port) + if self.servercode then + self.udp:sendto("S!",ip,port) + end + return + end + if net.inList(self.bannedIPs,ip) or net.inList(self.bannedCIDs,cid) then + self.udp:sendto("BANNED CLIENT", ip, port or self.port) + elseif self.ips[cid][4] then + self.udp:sendto(data, ip, port or self.port) + elseif self.ips[cid][4]==false then + self.udp:sendto("Make sure your server code is correct!", ip, port) + end + else + self.udp:sendto(data, ip, port or self.port) + end + end + function c:pollClientModules(ip,port) + self:send(ip,"L!",port) + end + function c:CIDFrom(ip,port) + for i,v in pairs(self.ips) do + if(ip==v[1] and v[2]==port) then + return i + end + end + end + function c:sendAll(data) + for i,v in pairs(self.ips) do + self:send(v[1],data,v[2],i) + end + end + function c:sendAllBut(data,cid) + for i,v in pairs(self.ips) do + if i~=cid then + self:send(v[1],data,v[2],i) + end + end + end + function c:clientRegistered(cid) + return self.ips[cid] + end + function c:clientLoggedIn(cid) + if not self.clientRegistered(cid) then + return nil + end + return self.ips[cid][4] + end + function c:update() + local data,ip,port=self.udp:receivefrom() + if net.inList(self.bannedIPs,ip) or net.inList(self.bannedCIDs,cid) then + print("We will ingore data from a banned client!") + return + end + if data then + if data:sub(1,4)=="pong" then + print("Recieved pong from: "..data:sub(5,-1)) + self.ips[data:sub(5,-1)][3]=os.clock() + elseif data:sub(1,2)=="S!" then + local cid=self:CIDFrom(ip,port) + if data:sub(3,-1)==self.servercode then + print("Servercode Accepted: "..self.servercode) + if self.ips[cid] then + self.ips[cid][4]=true + else + print("Server can't keep up! CID: "..cid.." has been skipped! Sending new CID to the client!") + local cid=net.resolveID(self) + print("Sending unique cid to client: "..cid) + self.ips[cid]={ip,port,0,self.servercode==nil} + print(ip) + self.udp:sendto("I!"..cid,ip,port) + if self.servercode then + self.udp:sendto("S!",ip,port) + end + end + else + self.udp:sendto("Make sure your server code is correct!", ip, port) + end + elseif data:sub(1,2)=="C!" then + self.OnDataRecieved:Fire(self,data:sub(11,-1),data:sub(3,10),ip,port) + elseif data:sub(1,2)=="E!" then + self.ips[data:sub(3,10)]=nil + obj.ids[data:sub(3,10)]=false + self.OnClientClosed:Fire(self,"Client Closed Connection!",data:sub(3,10),ip,port) + elseif data=="I!" then + local cid=net.resolveID(self) + print("Sending unique cid to client: "..cid) + self.ips[cid]={ip,port,os.clock(),self.servercode==nil} + print(ip) + self.udp:sendto("I!"..cid,ip,port) + if self.servercode then + self.udp:sendto("S!",ip,port) + end + elseif data:sub(1,2)=="L!" then + cid,cList=data:sub(3,10),data:sub(11,-1) + local list={} + for m,v in cList:gmatch("(%S-):(%S-)|") do + list[m]=v + end + self.OnClientsModulesList:Fire(list,cid,ip,port) + end + end + for cid,dat in pairs(self.ips) do + if not((os.clock()-dat[3])<65) then + self.ips[cid]=nil + self.OnClientClosed:Fire(self,"Client lost Connection: ping timeout",cid,ip,port) + end + end + end + c.OnClientsModulesList=multi:newConnection() + c.OnDataRecieved=multi:newConnection() + c.OnPongRecieved=multi:newConnection() + c.OnClientClosed=multi:newConnection() + c.connectiontest=multi:newAlarm(30) + c.connectiontest.link=c + c.connectiontest:OnRing(function(alarm) + print("pinging clients!") + alarm.link:sendAll("ping") + alarm:Reset() + end) + multi:newLoop(function() + c:update() + end) + net.OnServerCreated:Fire(c) + return c +end + +function net:newClient(host,port,servercode,nonluaServer) + local c={} + c.ip=assert(socket.dns.toip(host)) + c.udp=assert(socket.udp()) + c.udp:setpeername(c.ip, port) + c.udp:settimeout(0) + c.cid="NIL" + c.lastPing=0 + c.Type="udp" + c.servercode=servercode + c.autoReconnect=true + function c:pollPing(n) + return not((os.clock()-self.lastPing)<(n or 60)) + end + function c:send(data) + self.udp:send("C!"..self.cid..data) + end + function c:sendRaw(data) + self.udp:send(data) + end + function c:getCID() + if self:IDAssigned() then + return self.cid + end + end + function c:close() + self:send("E!") + end + function c:IDAssigned() + return self.cid~="NIL" + end + function c:update() + local data=self.udp:receive() + if data then + if data:sub(1,2)=="I!" then + self.cid=data:sub(3,-1) + self.OnClientReady:Fire(self) + elseif data=="S!" then + self.udp:send("S!"..(self.servercode or "")) + elseif data=="L!" then + local mods="" + local m="" + for i=1,#net.loadedModules do + m=net.loadedModules[i] + mods=mods..m..":"..net.getModuleVersion(m).."|" + end + self.udp:send("L!"..self.cid..mods) + elseif data=="ping" then + self.lastPing=os.clock() + self.OnPingRecieved:Fire(self) + self.udp:send("pong"..self.cid) + else + self.OnDataRecieved:Fire(self,data) + end + end + end + function c:reconnect() + if not nonluaServer then + self.cid="NIL" + c.udp:send("I!") + end + end + c.pingEvent=multi:newEvent(function(self) return self.link:pollPing() end) + c.pingEvent:OnEvent(function(self) + if self.link.autoReconnect then + self.link.OnServerNotAvailable:Fire("Connection to server lost: ping timeout! Attempting to reconnect...") + self.link:reconnect() + else + self.link.OnServerNotAvailable:Fire("Connection to server lost: ping timeout!") + end + end) + c.pingEvent.link=c + c.OnPingRecieved=multi:newConnection() + c.OnDataRecieved=multi:newConnection() + c.OnServerNotAvailable=multi:newConnection() + c.OnClientReady=multi:newConnection() + c.notConnected=multi:newFunction(function(self) + self:hold(3) + if self.link:IDAssigned()==false then + self.link.OnServerNotAvailable:Fire("Can't connect to the server: no response from server") + end + end) + c.notConnected.link=c + if not nonluaServer then + c.udp:send("I!") + end + multi:newLoop(function() + c:update() + end) + multi:newJob(function() c.notConnected() end) + net.OnClientCreated:Fire(c) + return c +end +--TCP Stuff +function net:newTCPServer(port) + local c={} + c.tcp=assert(socket.bind("*", port)) + c.tcp:settimeout(0) + c.ip,c.port=c.tcp:getsockname() + c.ips={} + c.port=port + c.ids={} + c.bannedIPs={} + c.Type="tcp" + c.rMode="*l" + c.sMode="*l" + c.updaterRate=1 + function c:setUpdateRate(n) + self.updaterRate=n + end + function c:setReceiveMode(mode) + self.rMode=mode + end + function c:setSendMode(mode) + self.rMode=mode + end + function c:banCID(cid) + print("Function not supported on a tcp server!") + end + function c:banIP(ip) + table.insert(self.bannedIPs,cid) + end + function c:send(handle,data) + if self.sMode=="*l" then + handle:send(data.."\n") + else + handle:send(data) + end + end + function c:pollClientModules(ip,port) + self:send(ip,"L!",port) + end + function c:CIDFrom(ip,port) + print("Method not supported when using a TCP Server!") + return "CIDs in TCP work differently!" + end + function c:sendAll(data) + for i,v in pairs(self.ips) do + self:send(v,data) + end + end + function c:sendAllBut(data,cid) + for i,v in pairs(self.ips) do + if not(cid==i) then + self:send(v,data) + end + end + end + function c:clientRegistered(cid) + return self.ips[cid] + end + function c:clientLoggedIn(cid) + return self.ips[cid] + end + function c:update() + local client = self.tcp:accept(self.rMode) + if not client then return end + table.insert(self.ips,client) + client:settimeout(0) + --client:setoption('tcp-nodelay', true) + client:setoption('keepalive', true) + ip,port=client:getpeername() + if ip and port then + print("Got connection from: ",ip,port) + local updater=multi:newUpdater(skip) + updater:OnUpdate(function(self) + local data, err = self.client:receive(self.Link.rMode) + if err=="closed" then + for i=1,#self.Link.ips do + if self.Link.ips[i]==self.client then + table.remove(self.Link.ips,i) + end + end + self.Link.OnClientClosed:Fire(self.Link,"Client Closed Connection!",self.client,self.client,ip) + self:Destroy() + end + if data then + if net.inList(self.Link.bannedIPs,ip) then + print("We will ingore data from a banned client!") + return + end + self.Link.OnDataRecieved:Fire(self.Link,data,self.client,self.client,ip) + if data:sub(1,2)=="L!" then + cList=data + local list={} + for m,v in cList:gmatch("(%S-):(%S-)|") do + list[m]=v + end + self.Link.OnClientsModulesList:Fire(list,self.client,self.client,ip) + end + end + end) + updater:setSkip(self.updaterRate) + updater.client=client + updater.Link=self + end + end + c.OnClientsModulesList=multi:newConnection() + c.OnDataRecieved=multi:newConnection() + c.OnClientClosed=multi:newConnection() + multi:newLoop(function() + c:update() + end) + net.OnServerCreated:Fire(c) + return c +end + +function net:newTCPClient(host,port) + local c={} + c.ip=assert(socket.dns.toip(host)) + c.port=post + c.tcp=socket.connect(c.ip,port) + if not c.tcp then + print("Can't connect to the server: no response from server") + return false + end + c.tcp:settimeout(0) + c.tcp:setoption('tcp-nodelay', true) + c.tcp:setoption('keepalive', true) + c.Type="tcp" + c.autoReconnect=true + c.rMode="*l" + c.sMode="*l" + function c:setReceiveMode(mode) + self.rMode=mode + end + function c:setSendMode(mode) + self.sMode=mode + end + function c:send(data) + if self.sMode=="*l" then + self.tcp:send(data.."\n") + else + self.tcp:send(data) + end + end + function c:sendRaw(data) + self.tcp:send(data) + end + function c:getCID() + return "No Cid on a tcp client!" + end + function c:close() + self.tcp:close() + end + function c:IDAssigned() + return true + end + function c:update() + local data=self.tcp:receive() + if data then + self.OnDataRecieved:Fire(self,data) + end + end + function c:reconnect() + self.ip=assert(socket.dns.toip(host)) + self.tcp=socket.connect(self.ip,self.port) + if not self.tcp then + print("Can't connect to the server: no response from server") + return + end + self.tcp:settimeout(0) + self.tcp:setoption('tcp-nodelay', true) + self.tcp:setoption('keepalive', true) + end + c.event=multi:newEvent(function(event) + return event.link:IDAssigned() + end) + c.event:OnEvent(function(event) + event.link.OnClientReady:Fire(event.link) + end) + c.event.link=c + c.OnClientReady=multi:newConnection() + c.OnDataRecieved=multi:newConnection() + multi:newLoop(function() + c:update() + end) + net.OnClientCreated:Fire(c) + return c +end diff --git a/net/sft.lua b/net/sft.lua new file mode 100644 index 0000000..6679aa6 --- /dev/null +++ b/net/sft.lua @@ -0,0 +1,179 @@ +require("net") +--General Stuff +--[[ What this module does! +Adds + +]] +function io.fileExists(path) + g=io.open(path or '','r') + if path =='' then + p='empty path' + return nil + end + if g~=nil and true or false then + p=(g~=nil and true or false) + end + if g~=nil then + io.close(g) + else + return false + end + return p +end +net:registerModule("sft",{1,0,0}) +function net.sft:init() -- calling this initilizes the library and binds it to the servers and clients created + --Server Stuff + net.OnServerCreated:connect(function(s) + print("The sft(Simple File Transfer) Module has been loaded onto the server!") + if s.Type~="tcp" then + print("It is recomended that you use tcp to transfer files!") + end + s.transfers={} + s.OnUploadRequest=multi:newConnection() -- create a sft event + s.OnFileUploaded=multi:newConnection() -- create a sft event + s.OnDownloadRequest=multi:newConnection() + s.OnDataRecieved:connect(function(self,data,cid,ip,port) -- when the server recieves data this method is triggered + --First Lets make sure we are getting sft data + --filename,dat=data:match("!sft! (%S-) (%S+)") + local cmd,arg1,arg2=data:match("!sft! (%S-) (%S-) (.+)") + if cmd=="tstart" then + local rets=self.OnUploadRequest:Fire(self,cid,ip,port) + for i=1,#rets do + if rets[i][1]==false then + print("Server refused to accept upload request!") + self:send(ip,"!sft! CANTUPLOAD NIL NIL",port) + return + end + end + local ID,streamable=arg1:match("(.+)|(.+)") + local file,hash=arg2:match("(.+)|(.+)") + if streamable~="NIL" then + self.transfers[ID]={bin.stream(streamable,false),hash,file} + else + self.transfers[ID]={bin.new(""),hash,file} + end + return + elseif cmd=="transfer" then + if self.transfers[arg1]~=nil then + self.transfers[arg1][1]:tackE(bin.fromhex(arg2)) + --print(self.transfers[arg1][1]:getSize()) + end + return + elseif cmd=="tend" then + if self.transfers[arg1]~=nil then + if self.transfers[arg1][1]:getHash(32)==self.transfers[arg1][2] then + self.OnFileUploaded:Fire(self,self.transfers[arg1][1],self.transfers[arg1][3],"Hash Good!") + else + print("Hash Error!") + self.OnFileUploaded:Fire(self,self.transfers[arg1][1],self.transfers[arg1][3],"Hash Bad!") + end + self.transfers[arg1]=nil + end + return + end + local filename=cmd + local dat=arg1 + if filename==nil then return end + local rets=self.OnDownloadRequest:Fire(self,cid,ip,port) + for i=1,#rets do + if rets[i][1]==false then + print("Server refused to accept download request!") + self:send(ip,"!sft! CANTREQUEST NIL NIL",port) + return + end + end + if io.fileExists(filename) then + --Lets first load the file + local file=bin.stream(filename,false) + local size=file:getSize() + local pieceSize=512 + local pieces=math.ceil(size/pieceSize) + local step=multi:newStep(1,pieces) + step.TransferID=tostring(math.random(1000,9999)) + step.sender=self + step.ip=ip + step.port=port + step.pieceSize=pieceSize + step:OnStart(function(self) + self.sender:send(self.ip,"!sft! TSTART "..self.TransferID.."|"..dat.." "..filename.."|"..file:getHash(32),self.port) + end) + step:OnStep(function(pos,self) + self:hold(.01) + self.sender:send(self.ip,"!sft! TRANSFER "..self.TransferID.." "..bin.tohex(file:sub(((self.pieceSize*pos)+1)-self.pieceSize,self.pieceSize*pos)),self.port) + end) + step:OnEnd(function(self) + self.sender:send(self.ip,"!sft! TEND "..self.TransferID.." NIL",self.port) + end) + else + self:send(ip,"!sft! CANTREQUEST NIL NIL",port) + end + end) + end) + --Client Stuff + net.OnClientCreated:connect(function(c) + c.transfers={} + c.OnTransferStarted=multi:newConnection() -- create a sft event + c.OnTransferFinished=multi:newConnection() -- create a sft event + c.OnFileRequestFailed=multi:newConnection() -- create a sft event + c.OnFileUploadFailed=multi:newConnection() -- create a sft event + c.OnDataRecieved:connect(function(self,data) -- when the client recieves data this method is triggered + --First Lets make sure we are getting sft data + local cmd,arg1,arg2=data:match("!sft! (%S-) (%S-) (.+)") + if cmd=="TSTART" then + local ID,streamable=arg1:match("(.+)|(.+)") + local file,hash=arg2:match("(.+)|(.+)") + if streamable~="NIL" then + self.transfers[ID]={bin.stream(streamable,false),hash,file} + else + self.transfers[ID]={bin.new(""),hash,file} + end + self.OnTransferStarted:Fire(self) + elseif cmd=="TRANSFER" then + self.transfers[arg1][1]:tackE(bin.fromhex(arg2)) + elseif cmd=="TEND" then + if self.transfers[arg1][1]:getHash(32)==self.transfers[arg1][2] then + self.OnTransferFinished:Fire(self,self.transfers[arg1][1],self.transfers[arg1][3],"Hash Good!") + else + print("Hash Error!") + self.OnTransferFinished:Fire(self,self.transfers[arg1][1],self.transfers[arg1][3],"Hash Bad!") + end + self.transfers[arg1]=nil + elseif cmd=="CANTREQUEST" then + self.OnFileRequestFailed:Fire(self,"Could not request the file for some reason!") + elseif cmd=="CANTUPLOAD" then + self.OnFileUploadFailed:Fire(self,"Could not upload the file for some reason!") + end + end) + function c:uploadFile(filename) + if io.fileExists(filename) then + local file=bin.stream(filename,false) + local size=file:getSize() + local pieceSize=512 + local pieces=math.ceil(size/pieceSize) + local step=multi:newStep(1,pieces) + step.TransferID=tostring(math.random(1000,9999)) + step.sender=self + step.pieceSize=pieceSize + step:OnStart(function(self) + self.sender:send("!sft! tstart "..self.TransferID.."|NIL "..filename.."|"..file:getHash(32)) + end) + step:OnStep(function(pos,self) + self:hold(.01) + self.sender:send("!sft! transfer "..self.TransferID.." "..bin.tohex(file:sub(((self.pieceSize*pos)+1)-self.pieceSize,self.pieceSize*pos))) + end) + step:OnEnd(function(self) + print("Request done!") + self.sender:send("!sft! tend "..self.TransferID.." NIL") + end) + else + self.OnFileUploadFailed:Fire(self,filename,"File does not exist!") + end + end + function c:requestFile(filename) + self:send("!sft! "..filename.." NIL NIL NIL") + end + end) +end +if net.autoInit then + net.sft.init() +end diff --git a/runclient.bat b/runclient.bat new file mode 100644 index 0000000..00220a7 --- /dev/null +++ b/runclient.bat @@ -0,0 +1 @@ +start engine\love.exe .\client \ No newline at end of file diff --git a/runserver.bat b/runserver.bat new file mode 100644 index 0000000..de5d753 --- /dev/null +++ b/runserver.bat @@ -0,0 +1 @@ +start server.exe \ No newline at end of file diff --git a/server.exe b/server.exe new file mode 100644 index 0000000..17af12a Binary files /dev/null and b/server.exe differ diff --git a/server.lua b/server.lua new file mode 100644 index 0000000..d5ff376 --- /dev/null +++ b/server.lua @@ -0,0 +1,6 @@ +package.path="?/init.lua;"..package.path +require("Libs/MultiManager") -- allows for multitasking +require("net") -- Loads the networking library +require("net.chatting") -- loads the networking chatting module +server=net:newTCPServer(12345) -- starts a server with the port 12345 on local host +multi:mainloop() -- starts the mainloop to keep the server going diff --git a/socket.lua b/socket.lua new file mode 100644 index 0000000..74dd401 --- /dev/null +++ b/socket.lua @@ -0,0 +1,133 @@ +----------------------------------------------------------------------------- +-- LuaSocket helper module +-- Author: Diego Nehab +-- RCS ID: $Id: socket.lua,v 1.22 2005/11/22 08:33:29 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local string = require("string") +local math = require("math") +local socket = require("socket.core") +module("socket") + +----------------------------------------------------------------------------- +-- Exported auxiliar functions +----------------------------------------------------------------------------- +function connect(address, port, laddress, lport) + local sock, err = socket.tcp() + if not sock then return nil, err end + if laddress then + local res, err = sock:bind(laddress, lport, -1) + if not res then return nil, err end + end + local res, err = sock:connect(address, port) + if not res then return nil, err end + return sock +end + +function bind(host, port, backlog) + local sock, err = socket.tcp() + if not sock then return nil, err end + sock:setoption("reuseaddr", true) + local res, err = sock:bind(host, port) + if not res then return nil, err end + res, err = sock:listen(backlog) + if not res then return nil, err end + return sock +end + +try = newtry() + +function choose(table) + return function(name, opt1, opt2) + if base.type(name) ~= "string" then + name, opt1, opt2 = "default", name, opt1 + end + local f = table[name or "nil"] + if not f then base.error("unknown key (".. base.tostring(name) ..")", 3) + else return f(opt1, opt2) end + end +end + +----------------------------------------------------------------------------- +-- Socket sources and sinks, conforming to LTN12 +----------------------------------------------------------------------------- +-- create namespaces inside LuaSocket namespace +sourcet = {} +sinkt = {} + +BLOCKSIZE = 2048 + +sinkt["close-when-done"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if not chunk then + sock:close() + return 1 + else return sock:send(chunk) end + end + }) +end + +sinkt["keep-open"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if chunk then return sock:send(chunk) + else return 1 end + end + }) +end + +sinkt["default"] = sinkt["keep-open"] + +sink = choose(sinkt) + +sourcet["by-length"] = function(sock, length) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + if length <= 0 then return nil end + local size = math.min(socket.BLOCKSIZE, length) + local chunk, err = sock:receive(size) + if err then return nil, err end + length = length - string.len(chunk) + return chunk + end + }) +end + +sourcet["until-closed"] = function(sock) + local done + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + if done then return nil end + local chunk, err, partial = sock:receive(socket.BLOCKSIZE) + if not err then return chunk + elseif err == "closed" then + sock:close() + done = 1 + return partial + else return nil, err end + end + }) +end + + +sourcet["default"] = sourcet["until-closed"] + +source = choose(sourcet) + diff --git a/socket/core.dll b/socket/core.dll new file mode 100644 index 0000000..3e95695 Binary files /dev/null and b/socket/core.dll differ diff --git a/socket/ftp.lua b/socket/ftp.lua new file mode 100644 index 0000000..624e582 --- /dev/null +++ b/socket/ftp.lua @@ -0,0 +1,281 @@ +----------------------------------------------------------------------------- +-- FTP support for the Lua language +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: ftp.lua,v 1.45 2007/07/11 19:25:47 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local table = require("table") +local string = require("string") +local math = require("math") +local socket = require("socket") +local url = require("socket.url") +local tp = require("socket.tp") +local ltn12 = require("ltn12") +module("socket.ftp") + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +-- timeout in seconds before the program gives up on a connection +TIMEOUT = 60 +-- default port for ftp service +PORT = 21 +-- this is the default anonymous password. used when no password is +-- provided in url. should be changed to your e-mail. +USER = "ftp" +PASSWORD = "anonymous@anonymous.org" + +----------------------------------------------------------------------------- +-- Low level FTP API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function open(server, port, create) + local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT, create)) + local f = base.setmetatable({ tp = tp }, metat) + -- make sure everything gets closed in an exception + f.try = socket.newtry(function() f:close() end) + return f +end + +function metat.__index:portconnect() + self.try(self.server:settimeout(TIMEOUT)) + self.data = self.try(self.server:accept()) + self.try(self.data:settimeout(TIMEOUT)) +end + +function metat.__index:pasvconnect() + self.data = self.try(socket.tcp()) + self.try(self.data:settimeout(TIMEOUT)) + self.try(self.data:connect(self.pasvt.ip, self.pasvt.port)) +end + +function metat.__index:login(user, password) + self.try(self.tp:command("user", user or USER)) + local code, reply = self.try(self.tp:check{"2..", 331}) + if code == 331 then + self.try(self.tp:command("pass", password or PASSWORD)) + self.try(self.tp:check("2..")) + end + return 1 +end + +function metat.__index:pasv() + self.try(self.tp:command("pasv")) + local code, reply = self.try(self.tp:check("2..")) + local pattern = "(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)" + local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern)) + self.try(a and b and c and d and p1 and p2, reply) + self.pasvt = { + ip = string.format("%d.%d.%d.%d", a, b, c, d), + port = p1*256 + p2 + } + if self.server then + self.server:close() + self.server = nil + end + return self.pasvt.ip, self.pasvt.port +end + +function metat.__index:port(ip, port) + self.pasvt = nil + if not ip then + ip, port = self.try(self.tp:getcontrol():getsockname()) + self.server = self.try(socket.bind(ip, 0)) + ip, port = self.try(self.server:getsockname()) + self.try(self.server:settimeout(TIMEOUT)) + end + local pl = math.mod(port, 256) + local ph = (port - pl)/256 + local arg = string.gsub(string.format("%s,%d,%d", ip, ph, pl), "%.", ",") + self.try(self.tp:command("port", arg)) + self.try(self.tp:check("2..")) + return 1 +end + +function metat.__index:send(sendt) + self.try(self.pasvt or self.server, "need port or pasv first") + -- if there is a pasvt table, we already sent a PASV command + -- we just get the data connection into self.data + if self.pasvt then self:pasvconnect() end + -- get the transfer argument and command + local argument = sendt.argument or + url.unescape(string.gsub(sendt.path or "", "^[/\\]", "")) + if argument == "" then argument = nil end + local command = sendt.command or "stor" + -- send the transfer command and check the reply + self.try(self.tp:command(command, argument)) + local code, reply = self.try(self.tp:check{"2..", "1.."}) + -- if there is not a a pasvt table, then there is a server + -- and we already sent a PORT command + if not self.pasvt then self:portconnect() end + -- get the sink, source and step for the transfer + local step = sendt.step or ltn12.pump.step + local readt = {self.tp.c} + local checkstep = function(src, snk) + -- check status in control connection while downloading + local readyt = socket.select(readt, nil, 0) + if readyt[tp] then code = self.try(self.tp:check("2..")) end + return step(src, snk) + end + local sink = socket.sink("close-when-done", self.data) + -- transfer all data and check error + self.try(ltn12.pump.all(sendt.source, sink, checkstep)) + if string.find(code, "1..") then self.try(self.tp:check("2..")) end + -- done with data connection + self.data:close() + -- find out how many bytes were sent + local sent = socket.skip(1, self.data:getstats()) + self.data = nil + return sent +end + +function metat.__index:receive(recvt) + self.try(self.pasvt or self.server, "need port or pasv first") + if self.pasvt then self:pasvconnect() end + local argument = recvt.argument or + url.unescape(string.gsub(recvt.path or "", "^[/\\]", "")) + if argument == "" then argument = nil end + local command = recvt.command or "retr" + self.try(self.tp:command(command, argument)) + local code = self.try(self.tp:check{"1..", "2.."}) + if not self.pasvt then self:portconnect() end + local source = socket.source("until-closed", self.data) + local step = recvt.step or ltn12.pump.step + self.try(ltn12.pump.all(source, recvt.sink, step)) + if string.find(code, "1..") then self.try(self.tp:check("2..")) end + self.data:close() + self.data = nil + return 1 +end + +function metat.__index:cwd(dir) + self.try(self.tp:command("cwd", dir)) + self.try(self.tp:check(250)) + return 1 +end + +function metat.__index:type(type) + self.try(self.tp:command("type", type)) + self.try(self.tp:check(200)) + return 1 +end + +function metat.__index:greet() + local code = self.try(self.tp:check{"1..", "2.."}) + if string.find(code, "1..") then self.try(self.tp:check("2..")) end + return 1 +end + +function metat.__index:quit() + self.try(self.tp:command("quit")) + self.try(self.tp:check("2..")) + return 1 +end + +function metat.__index:close() + if self.data then self.data:close() end + if self.server then self.server:close() end + return self.tp:close() +end + +----------------------------------------------------------------------------- +-- High level FTP API +----------------------------------------------------------------------------- +local function override(t) + if t.url then + local u = url.parse(t.url) + for i,v in base.pairs(t) do + u[i] = v + end + return u + else return t end +end + +local function tput(putt) + putt = override(putt) + socket.try(putt.host, "missing hostname") + local f = open(putt.host, putt.port, putt.create) + f:greet() + f:login(putt.user, putt.password) + if putt.type then f:type(putt.type) end + f:pasv() + local sent = f:send(putt) + f:quit() + f:close() + return sent +end + +local default = { + path = "/", + scheme = "ftp" +} + +local function parse(u) + local t = socket.try(url.parse(u, default)) + socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'") + socket.try(t.host, "missing hostname") + local pat = "^type=(.)$" + if t.params then + t.type = socket.skip(2, string.find(t.params, pat)) + socket.try(t.type == "a" or t.type == "i", + "invalid type '" .. t.type .. "'") + end + return t +end + +local function sput(u, body) + local putt = parse(u) + putt.source = ltn12.source.string(body) + return tput(putt) +end + +put = socket.protect(function(putt, body) + if base.type(putt) == "string" then return sput(putt, body) + else return tput(putt) end +end) + +local function tget(gett) + gett = override(gett) + socket.try(gett.host, "missing hostname") + local f = open(gett.host, gett.port, gett.create) + f:greet() + f:login(gett.user, gett.password) + if gett.type then f:type(gett.type) end + f:pasv() + f:receive(gett) + f:quit() + return f:close() +end + +local function sget(u) + local gett = parse(u) + local t = {} + gett.sink = ltn12.sink.table(t) + tget(gett) + return table.concat(t) +end + +command = socket.protect(function(cmdt) + cmdt = override(cmdt) + socket.try(cmdt.host, "missing hostname") + socket.try(cmdt.command, "missing command") + local f = open(cmdt.host, cmdt.port, cmdt.create) + f:greet() + f:login(cmdt.user, cmdt.password) + f.try(f.tp:command(cmdt.command, cmdt.argument)) + if cmdt.check then f.try(f.tp:check(cmdt.check)) end + f:quit() + return f:close() +end) + +get = socket.protect(function(gett) + if base.type(gett) == "string" then return sget(gett) + else return tget(gett) end +end) + diff --git a/socket/headers.lua b/socket/headers.lua new file mode 100644 index 0000000..38cd50e --- /dev/null +++ b/socket/headers.lua @@ -0,0 +1,104 @@ +----------------------------------------------------------------------------- +-- Canonic header field capitalization +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- +local socket = require("socket") +socket.headers = {} +local _M = socket.headers + +_M.canonic = { + ["accept"] = "Accept", + ["accept-charset"] = "Accept-Charset", + ["accept-encoding"] = "Accept-Encoding", + ["accept-language"] = "Accept-Language", + ["accept-ranges"] = "Accept-Ranges", + ["action"] = "Action", + ["alternate-recipient"] = "Alternate-Recipient", + ["age"] = "Age", + ["allow"] = "Allow", + ["arrival-date"] = "Arrival-Date", + ["authorization"] = "Authorization", + ["bcc"] = "Bcc", + ["cache-control"] = "Cache-Control", + ["cc"] = "Cc", + ["comments"] = "Comments", + ["connection"] = "Connection", + ["content-description"] = "Content-Description", + ["content-disposition"] = "Content-Disposition", + ["content-encoding"] = "Content-Encoding", + ["content-id"] = "Content-ID", + ["content-language"] = "Content-Language", + ["content-length"] = "Content-Length", + ["content-location"] = "Content-Location", + ["content-md5"] = "Content-MD5", + ["content-range"] = "Content-Range", + ["content-transfer-encoding"] = "Content-Transfer-Encoding", + ["content-type"] = "Content-Type", + ["cookie"] = "Cookie", + ["date"] = "Date", + ["diagnostic-code"] = "Diagnostic-Code", + ["dsn-gateway"] = "DSN-Gateway", + ["etag"] = "ETag", + ["expect"] = "Expect", + ["expires"] = "Expires", + ["final-log-id"] = "Final-Log-ID", + ["final-recipient"] = "Final-Recipient", + ["from"] = "From", + ["host"] = "Host", + ["if-match"] = "If-Match", + ["if-modified-since"] = "If-Modified-Since", + ["if-none-match"] = "If-None-Match", + ["if-range"] = "If-Range", + ["if-unmodified-since"] = "If-Unmodified-Since", + ["in-reply-to"] = "In-Reply-To", + ["keywords"] = "Keywords", + ["last-attempt-date"] = "Last-Attempt-Date", + ["last-modified"] = "Last-Modified", + ["location"] = "Location", + ["max-forwards"] = "Max-Forwards", + ["message-id"] = "Message-ID", + ["mime-version"] = "MIME-Version", + ["original-envelope-id"] = "Original-Envelope-ID", + ["original-recipient"] = "Original-Recipient", + ["pragma"] = "Pragma", + ["proxy-authenticate"] = "Proxy-Authenticate", + ["proxy-authorization"] = "Proxy-Authorization", + ["range"] = "Range", + ["received"] = "Received", + ["received-from-mta"] = "Received-From-MTA", + ["references"] = "References", + ["referer"] = "Referer", + ["remote-mta"] = "Remote-MTA", + ["reply-to"] = "Reply-To", + ["reporting-mta"] = "Reporting-MTA", + ["resent-bcc"] = "Resent-Bcc", + ["resent-cc"] = "Resent-Cc", + ["resent-date"] = "Resent-Date", + ["resent-from"] = "Resent-From", + ["resent-message-id"] = "Resent-Message-ID", + ["resent-reply-to"] = "Resent-Reply-To", + ["resent-sender"] = "Resent-Sender", + ["resent-to"] = "Resent-To", + ["retry-after"] = "Retry-After", + ["return-path"] = "Return-Path", + ["sender"] = "Sender", + ["server"] = "Server", + ["smtp-remote-recipient"] = "SMTP-Remote-Recipient", + ["status"] = "Status", + ["subject"] = "Subject", + ["te"] = "TE", + ["to"] = "To", + ["trailer"] = "Trailer", + ["transfer-encoding"] = "Transfer-Encoding", + ["upgrade"] = "Upgrade", + ["user-agent"] = "User-Agent", + ["vary"] = "Vary", + ["via"] = "Via", + ["warning"] = "Warning", + ["will-retry-until"] = "Will-Retry-Until", + ["www-authenticate"] = "WWW-Authenticate", + ["x-mailer"] = "X-Mailer", +} + +return _M diff --git a/socket/http.lua b/socket/http.lua new file mode 100644 index 0000000..976af89 --- /dev/null +++ b/socket/http.lua @@ -0,0 +1,350 @@ +----------------------------------------------------------------------------- +-- HTTP/1.1 client support for the Lua language. +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: http.lua,v 1.70 2007/03/12 04:08:40 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +------------------------------------------------------------------------------- +local socket = require("socket") +local url = require("socket.url") +local ltn12 = require("ltn12") +local mime = require("mime") +local string = require("string") +local base = _G +local table = require("table") +module("socket.http") + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +-- connection timeout in seconds +TIMEOUT = 60 +-- default port for document retrieval +PORT = 80 +-- user agent field sent in request +USERAGENT = socket._VERSION + +----------------------------------------------------------------------------- +-- Reads MIME headers from a connection, unfolding where needed +----------------------------------------------------------------------------- +local function receiveheaders(sock, headers) + local line, name, value, err + headers = headers or {} + -- get first line + line, err = sock:receive() + if err then return nil, err end + -- headers go until a blank line is found + while line ~= "" do + -- get field-name and value + name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)")) + if not (name and value) then return nil, "malformed reponse headers" end + name = string.lower(name) + -- get next line (value might be folded) + line, err = sock:receive() + if err then return nil, err end + -- unfold any folded values + while string.find(line, "^%s") do + value = value .. line + line = sock:receive() + if err then return nil, err end + end + -- save pair in table + if headers[name] then headers[name] = headers[name] .. ", " .. value + else headers[name] = value end + end + return headers +end + +----------------------------------------------------------------------------- +-- Extra sources and sinks +----------------------------------------------------------------------------- +socket.sourcet["http-chunked"] = function(sock, headers) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + -- get chunk size, skip extention + local line, err = sock:receive() + if err then return nil, err end + local size = base.tonumber(string.gsub(line, ";.*", ""), 16) + if not size then return nil, "invalid chunk size" end + -- was it the last chunk? + if size > 0 then + -- if not, get chunk and skip terminating CRLF + local chunk, err, part = sock:receive(size) + if chunk then sock:receive() end + return chunk, err + else + -- if it was, read trailers into headers table + headers, err = receiveheaders(sock, headers) + if not headers then return nil, err end + end + end + }) +end + +socket.sinkt["http-chunked"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if not chunk then return sock:send("0\r\n\r\n") end + local size = string.format("%X\r\n", string.len(chunk)) + return sock:send(size .. chunk .. "\r\n") + end + }) +end + +----------------------------------------------------------------------------- +-- Low level HTTP API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function open(host, port, create) + -- create socket with user connect function, or with default + local c = socket.try((create or socket.tcp)()) + local h = base.setmetatable({ c = c }, metat) + -- create finalized try + h.try = socket.newtry(function() h:close() end) + -- set timeout before connecting + h.try(c:settimeout(TIMEOUT)) + h.try(c:connect(host, port or PORT)) + -- here everything worked + return h +end + +function metat.__index:sendrequestline(method, uri) + local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri) + return self.try(self.c:send(reqline)) +end + +function metat.__index:sendheaders(headers) + local h = "\r\n" + for i, v in base.pairs(headers) do + h = i .. ": " .. v .. "\r\n" .. h + end + self.try(self.c:send(h)) + return 1 +end + +function metat.__index:sendbody(headers, source, step) + source = source or ltn12.source.empty() + step = step or ltn12.pump.step + -- if we don't know the size in advance, send chunked and hope for the best + local mode = "http-chunked" + if headers["content-length"] then mode = "keep-open" end + return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step)) +end + +function metat.__index:receivestatusline() + local status = self.try(self.c:receive(5)) + -- identify HTTP/0.9 responses, which do not contain a status line + -- this is just a heuristic, but is what the RFC recommends + if status ~= "HTTP/" then return nil, status end + -- otherwise proceed reading a status line + status = self.try(self.c:receive("*l", status)) + local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)")) + return self.try(base.tonumber(code), status) +end + +function metat.__index:receiveheaders() + return self.try(receiveheaders(self.c)) +end + +function metat.__index:receivebody(headers, sink, step) + sink = sink or ltn12.sink.null() + step = step or ltn12.pump.step + local length = base.tonumber(headers["content-length"]) + local t = headers["transfer-encoding"] -- shortcut + local mode = "default" -- connection close + if t and t ~= "identity" then mode = "http-chunked" + elseif base.tonumber(headers["content-length"]) then mode = "by-length" end + return self.try(ltn12.pump.all(socket.source(mode, self.c, length), + sink, step)) +end + +function metat.__index:receive09body(status, sink, step) + local source = ltn12.source.rewind(socket.source("until-closed", self.c)) + source(status) + return self.try(ltn12.pump.all(source, sink, step)) +end + +function metat.__index:close() + return self.c:close() +end + +----------------------------------------------------------------------------- +-- High level HTTP API +----------------------------------------------------------------------------- +local function adjusturi(reqt) + local u = reqt + -- if there is a proxy, we need the full url. otherwise, just a part. + if not reqt.proxy and not PROXY then + u = { + path = socket.try(reqt.path, "invalid path 'nil'"), + params = reqt.params, + query = reqt.query, + fragment = reqt.fragment + } + end + return url.build(u) +end + +local function adjustproxy(reqt) + local proxy = reqt.proxy or PROXY + if proxy then + proxy = url.parse(proxy) + return proxy.host, proxy.port or 3128 + else + return reqt.host, reqt.port + end +end + +local function adjustheaders(reqt) + -- default headers + local lower = { + ["user-agent"] = USERAGENT, + ["host"] = reqt.host, + ["connection"] = "close, TE", + ["te"] = "trailers" + } + -- if we have authentication information, pass it along + if reqt.user and reqt.password then + lower["authorization"] = + "Basic " .. (mime.b64(reqt.user .. ":" .. reqt.password)) + end + -- override with user headers + for i,v in base.pairs(reqt.headers or lower) do + lower[string.lower(i)] = v + end + return lower +end + +-- default url parts +local default = { + host = "", + port = PORT, + path ="/", + scheme = "http" +} + +local function adjustrequest(reqt) + -- parse url if provided + local nreqt = reqt.url and url.parse(reqt.url, default) or {} + -- explicit components override url + for i,v in base.pairs(reqt) do nreqt[i] = v end + if nreqt.port == "" then nreqt.port = 80 end + socket.try(nreqt.host and nreqt.host ~= "", + "invalid host '" .. base.tostring(nreqt.host) .. "'") + -- compute uri if user hasn't overriden + nreqt.uri = reqt.uri or adjusturi(nreqt) + -- ajust host and port if there is a proxy + nreqt.host, nreqt.port = adjustproxy(nreqt) + -- adjust headers in request + nreqt.headers = adjustheaders(nreqt) + return nreqt +end + +local function shouldredirect(reqt, code, headers) + return headers.location and + string.gsub(headers.location, "%s", "") ~= "" and + (reqt.redirect ~= false) and + (code == 301 or code == 302) and + (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") + and (not reqt.nredirects or reqt.nredirects < 5) +end + +local function shouldreceivebody(reqt, code) + if reqt.method == "HEAD" then return nil end + if code == 204 or code == 304 then return nil end + if code >= 100 and code < 200 then return nil end + return 1 +end + +-- forward declarations +local trequest, tredirect + +function tredirect(reqt, location) + local result, code, headers, status = trequest { + -- the RFC says the redirect URL has to be absolute, but some + -- servers do not respect that + url = url.absolute(reqt.url, location), + source = reqt.source, + sink = reqt.sink, + headers = reqt.headers, + proxy = reqt.proxy, + nredirects = (reqt.nredirects or 0) + 1, + create = reqt.create + } + -- pass location header back as a hint we redirected + headers = headers or {} + headers.location = headers.location or location + return result, code, headers, status +end + +function trequest(reqt) + -- we loop until we get what we want, or + -- until we are sure there is no way to get it + local nreqt = adjustrequest(reqt) + local h = open(nreqt.host, nreqt.port, nreqt.create) + -- send request line and headers + h:sendrequestline(nreqt.method, nreqt.uri) + h:sendheaders(nreqt.headers) + -- if there is a body, send it + if nreqt.source then + h:sendbody(nreqt.headers, nreqt.source, nreqt.step) + end + local code, status = h:receivestatusline() + -- if it is an HTTP/0.9 server, simply get the body and we are done + if not code then + h:receive09body(status, nreqt.sink, nreqt.step) + return 1, 200 + end + local headers + -- ignore any 100-continue messages + while code == 100 do + headers = h:receiveheaders() + code, status = h:receivestatusline() + end + headers = h:receiveheaders() + -- at this point we should have a honest reply from the server + -- we can't redirect if we already used the source, so we report the error + if shouldredirect(nreqt, code, headers) and not nreqt.source then + h:close() + return tredirect(reqt, headers.location) + end + -- here we are finally done + if shouldreceivebody(nreqt, code) then + h:receivebody(headers, nreqt.sink, nreqt.step) + end + h:close() + return 1, code, headers, status +end + +local function srequest(u, b) + local t = {} + local reqt = { + url = u, + sink = ltn12.sink.table(t) + } + if b then + reqt.source = ltn12.source.string(b) + reqt.headers = { + ["content-length"] = string.len(b), + ["content-type"] = "application/x-www-form-urlencoded" + } + reqt.method = "POST" + end + local code, headers, status = socket.skip(1, trequest(reqt)) + return table.concat(t), code, headers, status +end + +request = socket.protect(function(reqt, body) + if base.type(reqt) == "string" then return srequest(reqt, body) + else return trequest(reqt) end +end) diff --git a/socket/smtp.lua b/socket/smtp.lua new file mode 100644 index 0000000..4335e61 --- /dev/null +++ b/socket/smtp.lua @@ -0,0 +1,251 @@ +----------------------------------------------------------------------------- +-- SMTP client support for the Lua language. +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: smtp.lua,v 1.46 2007/03/12 04:08:40 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local coroutine = require("coroutine") +local string = require("string") +local math = require("math") +local os = require("os") +local socket = require("socket") +local tp = require("socket.tp") +local ltn12 = require("ltn12") +local mime = require("mime") +module("socket.smtp") + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +-- timeout for connection +TIMEOUT = 60 +-- default server used to send e-mails +SERVER = "localhost" +-- default port +PORT = 25 +-- domain used in HELO command and default sendmail +-- If we are under a CGI, try to get from environment +DOMAIN = os.getenv("SERVER_NAME") or "localhost" +-- default time zone (means we don't know) +ZONE = "-0000" + +--------------------------------------------------------------------------- +-- Low level SMTP API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function metat.__index:greet(domain) + self.try(self.tp:check("2..")) + self.try(self.tp:command("EHLO", domain or DOMAIN)) + return socket.skip(1, self.try(self.tp:check("2.."))) +end + +function metat.__index:mail(from) + self.try(self.tp:command("MAIL", "FROM:" .. from)) + return self.try(self.tp:check("2..")) +end + +function metat.__index:rcpt(to) + self.try(self.tp:command("RCPT", "TO:" .. to)) + return self.try(self.tp:check("2..")) +end + +function metat.__index:data(src, step) + self.try(self.tp:command("DATA")) + self.try(self.tp:check("3..")) + self.try(self.tp:source(src, step)) + self.try(self.tp:send("\r\n.\r\n")) + return self.try(self.tp:check("2..")) +end + +function metat.__index:quit() + self.try(self.tp:command("QUIT")) + return self.try(self.tp:check("2..")) +end + +function metat.__index:close() + return self.tp:close() +end + +function metat.__index:login(user, password) + self.try(self.tp:command("AUTH", "LOGIN")) + self.try(self.tp:check("3..")) + self.try(self.tp:command(mime.b64(user))) + self.try(self.tp:check("3..")) + self.try(self.tp:command(mime.b64(password))) + return self.try(self.tp:check("2..")) +end + +function metat.__index:plain(user, password) + local auth = "PLAIN " .. mime.b64("\0" .. user .. "\0" .. password) + self.try(self.tp:command("AUTH", auth)) + return self.try(self.tp:check("2..")) +end + +function metat.__index:auth(user, password, ext) + if not user or not password then return 1 end + if string.find(ext, "AUTH[^\n]+LOGIN") then + return self:login(user, password) + elseif string.find(ext, "AUTH[^\n]+PLAIN") then + return self:plain(user, password) + else + self.try(nil, "authentication not supported") + end +end + +-- send message or throw an exception +function metat.__index:send(mailt) + self:mail(mailt.from) + if base.type(mailt.rcpt) == "table" then + for i,v in base.ipairs(mailt.rcpt) do + self:rcpt(v) + end + else + self:rcpt(mailt.rcpt) + end + self:data(ltn12.source.chain(mailt.source, mime.stuff()), mailt.step) +end + +function open(server, port, create) + local tp = socket.try(tp.connect(server or SERVER, port or PORT, + TIMEOUT, create)) + local s = base.setmetatable({tp = tp}, metat) + -- make sure tp is closed if we get an exception + s.try = socket.newtry(function() + s:close() + end) + return s +end + +-- convert headers to lowercase +local function lower_headers(headers) + local lower = {} + for i,v in base.pairs(headers or lower) do + lower[string.lower(i)] = v + end + return lower +end + +--------------------------------------------------------------------------- +-- Multipart message source +----------------------------------------------------------------------------- +-- returns a hopefully unique mime boundary +local seqno = 0 +local function newboundary() + seqno = seqno + 1 + return string.format('%s%05d==%05u', os.date('%d%m%Y%H%M%S'), + math.random(0, 99999), seqno) +end + +-- send_message forward declaration +local send_message + +-- yield the headers all at once, it's faster +local function send_headers(headers) + local h = "\r\n" + for i,v in base.pairs(headers) do + h = i .. ': ' .. v .. "\r\n" .. h + end + coroutine.yield(h) +end + +-- yield multipart message body from a multipart message table +local function send_multipart(mesgt) + -- make sure we have our boundary and send headers + local bd = newboundary() + local headers = lower_headers(mesgt.headers or {}) + headers['content-type'] = headers['content-type'] or 'multipart/mixed' + headers['content-type'] = headers['content-type'] .. + '; boundary="' .. bd .. '"' + send_headers(headers) + -- send preamble + if mesgt.body.preamble then + coroutine.yield(mesgt.body.preamble) + coroutine.yield("\r\n") + end + -- send each part separated by a boundary + for i, m in base.ipairs(mesgt.body) do + coroutine.yield("\r\n--" .. bd .. "\r\n") + send_message(m) + end + -- send last boundary + coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n") + -- send epilogue + if mesgt.body.epilogue then + coroutine.yield(mesgt.body.epilogue) + coroutine.yield("\r\n") + end +end + +-- yield message body from a source +local function send_source(mesgt) + -- make sure we have a content-type + local headers = lower_headers(mesgt.headers or {}) + headers['content-type'] = headers['content-type'] or + 'text/plain; charset="iso-8859-1"' + send_headers(headers) + -- send body from source + while true do + local chunk, err = mesgt.body() + if err then coroutine.yield(nil, err) + elseif chunk then coroutine.yield(chunk) + else break end + end +end + +-- yield message body from a string +local function send_string(mesgt) + -- make sure we have a content-type + local headers = lower_headers(mesgt.headers or {}) + headers['content-type'] = headers['content-type'] or + 'text/plain; charset="iso-8859-1"' + send_headers(headers) + -- send body from string + coroutine.yield(mesgt.body) +end + +-- message source +function send_message(mesgt) + if base.type(mesgt.body) == "table" then send_multipart(mesgt) + elseif base.type(mesgt.body) == "function" then send_source(mesgt) + else send_string(mesgt) end +end + +-- set defaul headers +local function adjust_headers(mesgt) + local lower = lower_headers(mesgt.headers) + lower["date"] = lower["date"] or + os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or ZONE) + lower["x-mailer"] = lower["x-mailer"] or socket._VERSION + -- this can't be overriden + lower["mime-version"] = "1.0" + return lower +end + +function message(mesgt) + mesgt.headers = adjust_headers(mesgt) + -- create and return message source + local co = coroutine.create(function() send_message(mesgt) end) + return function() + local ret, a, b = coroutine.resume(co) + if ret then return a, b + else return nil, a end + end +end + +--------------------------------------------------------------------------- +-- High level SMTP API +----------------------------------------------------------------------------- +send = socket.protect(function(mailt) + local s = open(mailt.server, mailt.port, mailt.create) + local ext = s:greet(mailt.domain) + s:auth(mailt.user, mailt.password, ext) + s:send(mailt) + s:quit() + return s:close() +end) diff --git a/socket/tp.lua b/socket/tp.lua new file mode 100644 index 0000000..5c968cd --- /dev/null +++ b/socket/tp.lua @@ -0,0 +1,123 @@ +----------------------------------------------------------------------------- +-- Unified SMTP/FTP subsystem +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: tp.lua,v 1.22 2006/03/14 09:04:15 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local string = require("string") +local socket = require("socket") +local ltn12 = require("ltn12") +module("socket.tp") + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +TIMEOUT = 60 + +----------------------------------------------------------------------------- +-- Implementation +----------------------------------------------------------------------------- +-- gets server reply (works for SMTP and FTP) +local function get_reply(c) + local code, current, sep + local line, err = c:receive() + local reply = line + if err then return nil, err end + code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) + if not code then return nil, "invalid server reply" end + if sep == "-" then -- reply is multiline + repeat + line, err = c:receive() + if err then return nil, err end + current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) + reply = reply .. "\n" .. line + -- reply ends with same code + until code == current and sep == " " + end + return code, reply +end + +-- metatable for sock object +local metat = { __index = {} } + +function metat.__index:check(ok) + local code, reply = get_reply(self.c) + if not code then return nil, reply end + if base.type(ok) ~= "function" then + if base.type(ok) == "table" then + for i, v in base.ipairs(ok) do + if string.find(code, v) then + return base.tonumber(code), reply + end + end + return nil, reply + else + if string.find(code, ok) then return base.tonumber(code), reply + else return nil, reply end + end + else return ok(base.tonumber(code), reply) end +end + +function metat.__index:command(cmd, arg) + if arg then + return self.c:send(cmd .. " " .. arg.. "\r\n") + else + return self.c:send(cmd .. "\r\n") + end +end + +function metat.__index:sink(snk, pat) + local chunk, err = c:receive(pat) + return snk(chunk, err) +end + +function metat.__index:send(data) + return self.c:send(data) +end + +function metat.__index:receive(pat) + return self.c:receive(pat) +end + +function metat.__index:getfd() + return self.c:getfd() +end + +function metat.__index:dirty() + return self.c:dirty() +end + +function metat.__index:getcontrol() + return self.c +end + +function metat.__index:source(source, step) + local sink = socket.sink("keep-open", self.c) + local ret, err = ltn12.pump.all(source, sink, step or ltn12.pump.step) + return ret, err +end + +-- closes the underlying c +function metat.__index:close() + self.c:close() + return 1 +end + +-- connect with server and return c object +function connect(host, port, timeout, create) + local c, e = (create or socket.tcp)() + if not c then return nil, e end + c:settimeout(timeout or TIMEOUT) + local r, e = c:connect(host, port) + if not r then + c:close() + return nil, e + end + return base.setmetatable({c = c}, metat) +end + diff --git a/socket/url.lua b/socket/url.lua new file mode 100644 index 0000000..f4a213c --- /dev/null +++ b/socket/url.lua @@ -0,0 +1,297 @@ +----------------------------------------------------------------------------- +-- URI parsing, composition and relative URL resolution +-- LuaSocket toolkit. +-- Author: Diego Nehab +-- RCS ID: $Id: url.lua,v 1.38 2006/04/03 04:45:42 diego Exp $ +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module +----------------------------------------------------------------------------- +local string = require("string") +local base = _G +local table = require("table") +module("socket.url") + +----------------------------------------------------------------------------- +-- Module version +----------------------------------------------------------------------------- +_VERSION = "URL 1.0.1" + +----------------------------------------------------------------------------- +-- Encodes a string into its escaped hexadecimal representation +-- Input +-- s: binary string to be encoded +-- Returns +-- escaped representation of string binary +----------------------------------------------------------------------------- +function escape(s) + return string.gsub(s, "([^A-Za-z0-9_])", function(c) + return string.format("%%%02x", string.byte(c)) + end) +end + +----------------------------------------------------------------------------- +-- Protects a path segment, to prevent it from interfering with the +-- url parsing. +-- Input +-- s: binary string to be encoded +-- Returns +-- escaped representation of string binary +----------------------------------------------------------------------------- +local function make_set(t) + local s = {} + for i,v in base.ipairs(t) do + s[t[i]] = 1 + end + return s +end + +-- these are allowed withing a path segment, along with alphanum +-- other characters must be escaped +local segment_set = make_set { + "-", "_", ".", "!", "~", "*", "'", "(", + ")", ":", "@", "&", "=", "+", "$", ",", +} + +local function protect_segment(s) + return string.gsub(s, "([^A-Za-z0-9_])", function (c) + if segment_set[c] then return c + else return string.format("%%%02x", string.byte(c)) end + end) +end + +----------------------------------------------------------------------------- +-- Encodes a string into its escaped hexadecimal representation +-- Input +-- s: binary string to be encoded +-- Returns +-- escaped representation of string binary +----------------------------------------------------------------------------- +function unescape(s) + return string.gsub(s, "%%(%x%x)", function(hex) + return string.char(base.tonumber(hex, 16)) + end) +end + +----------------------------------------------------------------------------- +-- Builds a path from a base path and a relative path +-- Input +-- base_path +-- relative_path +-- Returns +-- corresponding absolute path +----------------------------------------------------------------------------- +local function absolute_path(base_path, relative_path) + if string.sub(relative_path, 1, 1) == "/" then return relative_path end + local path = string.gsub(base_path, "[^/]*$", "") + path = path .. relative_path + path = string.gsub(path, "([^/]*%./)", function (s) + if s ~= "./" then return s else return "" end + end) + path = string.gsub(path, "/%.$", "/") + local reduced + while reduced ~= path do + reduced = path + path = string.gsub(reduced, "([^/]*/%.%./)", function (s) + if s ~= "../../" then return "" else return s end + end) + end + path = string.gsub(reduced, "([^/]*/%.%.)$", function (s) + if s ~= "../.." then return "" else return s end + end) + return path +end + +----------------------------------------------------------------------------- +-- Parses a url and returns a table with all its parts according to RFC 2396 +-- The following grammar describes the names given to the URL parts +-- ::= :///;?# +-- ::= @: +-- ::= [:] +-- :: = {/} +-- Input +-- url: uniform resource locator of request +-- default: table with default values for each field +-- Returns +-- table with the following fields, where RFC naming conventions have +-- been preserved: +-- scheme, authority, userinfo, user, password, host, port, +-- path, params, query, fragment +-- Obs: +-- the leading '/' in {/} is considered part of +----------------------------------------------------------------------------- +function parse(url, default) + -- initialize default parameters + local parsed = {} + for i,v in base.pairs(default or parsed) do parsed[i] = v end + -- empty url is parsed to nil + if not url or url == "" then return nil, "invalid url" end + -- remove whitespace + -- url = string.gsub(url, "%s", "") + -- get fragment + url = string.gsub(url, "#(.*)$", function(f) + parsed.fragment = f + return "" + end) + -- get scheme + url = string.gsub(url, "^([%w][%w%+%-%.]*)%:", + function(s) parsed.scheme = s; return "" end) + -- get authority + url = string.gsub(url, "^//([^/]*)", function(n) + parsed.authority = n + return "" + end) + -- get query stringing + url = string.gsub(url, "%?(.*)", function(q) + parsed.query = q + return "" + end) + -- get params + url = string.gsub(url, "%;(.*)", function(p) + parsed.params = p + return "" + end) + -- path is whatever was left + if url ~= "" then parsed.path = url end + local authority = parsed.authority + if not authority then return parsed end + authority = string.gsub(authority,"^([^@]*)@", + function(u) parsed.userinfo = u; return "" end) + authority = string.gsub(authority, ":([^:]*)$", + function(p) parsed.port = p; return "" end) + if authority ~= "" then parsed.host = authority end + local userinfo = parsed.userinfo + if not userinfo then return parsed end + userinfo = string.gsub(userinfo, ":([^:]*)$", + function(p) parsed.password = p; return "" end) + parsed.user = userinfo + return parsed +end + +----------------------------------------------------------------------------- +-- Rebuilds a parsed URL from its components. +-- Components are protected if any reserved or unallowed characters are found +-- Input +-- parsed: parsed URL, as returned by parse +-- Returns +-- a stringing with the corresponding URL +----------------------------------------------------------------------------- +function build(parsed) + local ppath = parse_path(parsed.path or "") + local url = build_path(ppath) + if parsed.params then url = url .. ";" .. parsed.params end + if parsed.query then url = url .. "?" .. parsed.query end + local authority = parsed.authority + if parsed.host then + authority = parsed.host + if parsed.port then authority = authority .. ":" .. parsed.port end + local userinfo = parsed.userinfo + if parsed.user then + userinfo = parsed.user + if parsed.password then + userinfo = userinfo .. ":" .. parsed.password + end + end + if userinfo then authority = userinfo .. "@" .. authority end + end + if authority then url = "//" .. authority .. url end + if parsed.scheme then url = parsed.scheme .. ":" .. url end + if parsed.fragment then url = url .. "#" .. parsed.fragment end + -- url = string.gsub(url, "%s", "") + return url +end + +----------------------------------------------------------------------------- +-- Builds a absolute URL from a base and a relative URL according to RFC 2396 +-- Input +-- base_url +-- relative_url +-- Returns +-- corresponding absolute url +----------------------------------------------------------------------------- +function absolute(base_url, relative_url) + if base.type(base_url) == "table" then + base_parsed = base_url + base_url = build(base_parsed) + else + base_parsed = parse(base_url) + end + local relative_parsed = parse(relative_url) + if not base_parsed then return relative_url + elseif not relative_parsed then return base_url + elseif relative_parsed.scheme then return relative_url + else + relative_parsed.scheme = base_parsed.scheme + if not relative_parsed.authority then + relative_parsed.authority = base_parsed.authority + if not relative_parsed.path then + relative_parsed.path = base_parsed.path + if not relative_parsed.params then + relative_parsed.params = base_parsed.params + if not relative_parsed.query then + relative_parsed.query = base_parsed.query + end + end + else + relative_parsed.path = absolute_path(base_parsed.path or "", + relative_parsed.path) + end + end + return build(relative_parsed) + end +end + +----------------------------------------------------------------------------- +-- Breaks a path into its segments, unescaping the segments +-- Input +-- path +-- Returns +-- segment: a table with one entry per segment +----------------------------------------------------------------------------- +function parse_path(path) + local parsed = {} + path = path or "" + --path = string.gsub(path, "%s", "") + string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end) + for i = 1, table.getn(parsed) do + parsed[i] = unescape(parsed[i]) + end + if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end + if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end + return parsed +end + +----------------------------------------------------------------------------- +-- Builds a path component from its segments, escaping protected characters. +-- Input +-- parsed: path segments +-- unsafe: if true, segments are not protected before path is built +-- Returns +-- path: corresponding path stringing +----------------------------------------------------------------------------- +function build_path(parsed, unsafe) + local path = "" + local n = table.getn(parsed) + if unsafe then + for i = 1, n-1 do + path = path .. parsed[i] + path = path .. "/" + end + if n > 0 then + path = path .. parsed[n] + if parsed.is_directory then path = path .. "/" end + end + else + for i = 1, n-1 do + path = path .. protect_segment(parsed[i]) + path = path .. "/" + end + if n > 0 then + path = path .. protect_segment(parsed[n]) + if parsed.is_directory then path = path .. "/" end + end + end + if parsed.is_absolute then path = "/" .. path end + return path +end