boot.lua 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. --[[
  2. Copyright (c) 2006-2015 LOVE Development Team
  3. This software is provided 'as-is', without any express or implied
  4. warranty. In no event will the authors be held liable for any damages
  5. arising from the use of this software.
  6. Permission is granted to anyone to use this software for any purpose,
  7. including commercial applications, and to alter it and redistribute it
  8. freely, subject to the following restrictions:
  9. 1. The origin of this software must not be misrepresented; you must not
  10. claim that you wrote the original software. If you use this software
  11. in a product, an acknowledgment in the product documentation would be
  12. appreciated but is not required.
  13. 2. Altered source versions must be plainly marked as such, and must not be
  14. misrepresented as being the original software.
  15. 3. This notice may not be removed or altered from any source distribution.
  16. --]]
  17. -- Make sure love exists.
  18. local love = require("love")
  19. -- Used for setup:
  20. love.path = {}
  21. love.arg = {}
  22. -- Replace any \ with /.
  23. function love.path.normalslashes(p)
  24. return string.gsub(p, "\\", "/")
  25. end
  26. -- Makes sure there is a slash at the end
  27. -- of a path.
  28. function love.path.endslash(p)
  29. if string.sub(p, string.len(p)-1) ~= "/" then
  30. return p .. "/"
  31. else
  32. return p
  33. end
  34. end
  35. -- Checks whether a path is absolute or not.
  36. function love.path.abs(p)
  37. local tmp = love.path.normalslashes(p)
  38. -- Path is absolute if it starts with a "/".
  39. if string.find(tmp, "/") == 1 then
  40. return true
  41. end
  42. -- Path is absolute if it starts with a
  43. -- letter followed by a colon.
  44. if string.find(tmp, "%a:") == 1 then
  45. return true
  46. end
  47. -- Relative.
  48. return false
  49. end
  50. -- Converts any path into a full path.
  51. function love.path.getfull(p)
  52. if love.path.abs(p) then
  53. return love.path.normalslashes(p)
  54. end
  55. local cwd = love.filesystem.getWorkingDirectory()
  56. cwd = love.path.normalslashes(cwd)
  57. cwd = love.path.endslash(cwd)
  58. -- Construct a full path.
  59. local full = cwd .. love.path.normalslashes(p)
  60. -- Remove trailing /., if applicable
  61. return full:match("(.-)/%.$") or full
  62. end
  63. -- Returns the leaf of a full path.
  64. function love.path.leaf(p)
  65. p = love.path.normalslashes(p)
  66. local a = 1
  67. local last = p
  68. while a do
  69. a = string.find(p, "/", a+1)
  70. if a then
  71. last = string.sub(p, a+1)
  72. end
  73. end
  74. return last
  75. end
  76. -- Finds the key in the table with the lowest integral index. The lowest
  77. -- will typically the executable, for instance "lua5.1.exe".
  78. function love.arg.getLow(a)
  79. local m = math.huge
  80. for k,v in pairs(a) do
  81. if k < m then
  82. m = k
  83. end
  84. end
  85. return a[m]
  86. end
  87. love.arg.options = {
  88. console = { a = 0 },
  89. fused = {a = 0 },
  90. game = { a = 1 }
  91. }
  92. function love.arg.parse_option(m, i)
  93. m.set = true
  94. if m.a > 0 then
  95. m.arg = {}
  96. for j=i,i+m.a-1 do
  97. table.insert(m.arg, arg[j])
  98. i = j
  99. end
  100. end
  101. return i
  102. end
  103. function love.arg.parse_options()
  104. local game
  105. local argc = #arg
  106. for i=1,argc do
  107. -- Look for options.
  108. local s, e, m = string.find(arg[i], "%-%-(.+)")
  109. if m and love.arg.options[m] then
  110. i = love.arg.parse_option(love.arg.options[m], i+1)
  111. elseif not game then
  112. game = i
  113. end
  114. end
  115. if not love.arg.options.game.set then
  116. love.arg.parse_option(love.arg.options.game, game or 0)
  117. end
  118. end
  119. function love.createhandlers()
  120. -- Standard callback handlers.
  121. love.handlers = setmetatable({
  122. keypressed = function (b,s,r)
  123. if love.keypressed then return love.keypressed(b,s,r) end
  124. end,
  125. keyreleased = function (b,s)
  126. if love.keyreleased then return love.keyreleased(b,s) end
  127. end,
  128. textinput = function (t)
  129. if love.textinput then return love.textinput(t) end
  130. end,
  131. textedited = function (t,s,l)
  132. if love.textedited then return love.textedited(t,s,l) end
  133. end,
  134. mousemoved = function (x,y,dx,dy,t)
  135. if love.mousemoved then return love.mousemoved(x,y,dx,dy,t) end
  136. end,
  137. mousepressed = function (x,y,b,t)
  138. if love.mousepressed then return love.mousepressed(x,y,b,t) end
  139. end,
  140. mousereleased = function (x,y,b,t)
  141. if love.mousereleased then return love.mousereleased(x,y,b,t) end
  142. end,
  143. wheelmoved = function (x,y)
  144. if love.wheelmoved then return love.wheelmoved(x,y) end
  145. end,
  146. touchpressed = function (id,x,y,dx,dy,p)
  147. if love.touchpressed then return love.touchpressed(id,x,y,dx,dy,p) end
  148. end,
  149. touchreleased = function (id,x,y,dx,dy,p)
  150. if love.touchreleased then return love.touchreleased(id,x,y,dx,dy,p) end
  151. end,
  152. touchmoved = function (id,x,y,dx,dy,p)
  153. if love.touchmoved then return love.touchmoved(id,x,y,dx,dy,p) end
  154. end,
  155. joystickpressed = function (j,b)
  156. if love.joystickpressed then return love.joystickpressed(j,b) end
  157. end,
  158. joystickreleased = function (j,b)
  159. if love.joystickreleased then return love.joystickreleased(j,b) end
  160. end,
  161. joystickaxis = function (j,a,v)
  162. if love.joystickaxis then return love.joystickaxis(j,a,v) end
  163. end,
  164. joystickhat = function (j,h,v)
  165. if love.joystickhat then return love.joystickhat(j,h,v) end
  166. end,
  167. gamepadpressed = function (j,b)
  168. if love.gamepadpressed then return love.gamepadpressed(j,b) end
  169. end,
  170. gamepadreleased = function (j,b)
  171. if love.gamepadreleased then return love.gamepadreleased(j,b) end
  172. end,
  173. gamepadaxis = function (j,a,v)
  174. if love.gamepadaxis then return love.gamepadaxis(j,a,v) end
  175. end,
  176. joystickadded = function (j)
  177. if love.joystickadded then return love.joystickadded(j) end
  178. end,
  179. joystickremoved = function (j)
  180. if love.joystickremoved then return love.joystickremoved(j) end
  181. end,
  182. focus = function (f)
  183. if love.focus then return love.focus(f) end
  184. end,
  185. mousefocus = function (f)
  186. if love.mousefocus then return love.mousefocus(f) end
  187. end,
  188. visible = function (v)
  189. if love.visible then return love.visible(v) end
  190. end,
  191. quit = function ()
  192. return
  193. end,
  194. threaderror = function (t, err)
  195. if love.threaderror then return love.threaderror(t, err) end
  196. end,
  197. resize = function (w, h)
  198. if love.resize then return love.resize(w, h) end
  199. end,
  200. filedropped = function (f)
  201. if love.filedropped then return love.filedropped(f) end
  202. end,
  203. directorydropped = function (dir)
  204. if love.directorydropped then return love.directorydropped(dir) end
  205. end,
  206. lowmemory = function ()
  207. collectgarbage()
  208. if love.lowmemory then return love.lowmemory() end
  209. end,
  210. }, {
  211. __index = function(self, name)
  212. error("Unknown event: " .. name)
  213. end,
  214. })
  215. end
  216. local function uridecode(s)
  217. return s:gsub("%%%x%x", function(str)
  218. return string.char(tonumber(str:sub(2), 16))
  219. end)
  220. end
  221. local no_game_code = false
  222. -- This can't be overriden.
  223. function love.boot()
  224. -- This is absolutely needed.
  225. require("love.filesystem")
  226. love.arg.parse_options()
  227. local o = love.arg.options
  228. local arg0 = love.arg.getLow(arg)
  229. love.filesystem.init(arg0)
  230. local exepath = love.filesystem.getExecutablePath()
  231. if #exepath == 0 then
  232. -- This shouldn't happen, but just in case we'll fall back to arg0.
  233. exepath = arg0
  234. end
  235. -- Is this one of those fancy "fused" games?
  236. local can_has_game = pcall(love.filesystem.setSource, exepath)
  237. local is_fused_game = can_has_game
  238. if love.arg.options.fused.set then
  239. is_fused_game = true
  240. end
  241. love.filesystem.setFused(is_fused_game)
  242. local identity = ""
  243. if not can_has_game and o.game.set and o.game.arg[1] then
  244. local nouri = o.game.arg[1]
  245. -- Use the realdir to determine the identity, if we can
  246. if love.filesystem.isFile("main.lua") then
  247. nouri = love.filesystem.getRealDirectory("main.lua")
  248. end
  249. if nouri:sub(1, 7) == "file://" then
  250. nouri = uridecode(nouri:sub(8))
  251. end
  252. local full_source = love.path.getfull(nouri)
  253. can_has_game = pcall(love.filesystem.setSource, full_source)
  254. -- Use the name of the source .love as the identity for now.
  255. identity = love.path.leaf(full_source)
  256. else
  257. -- Use the name of the exe as the identity for now.
  258. identity = love.path.leaf(exepath)
  259. end
  260. identity = identity:gsub("^([%.]+)", "") -- strip leading "."'s
  261. identity = identity:gsub("%.([^%.]+)$", "") -- strip extension
  262. identity = identity:gsub("%.", "_") -- replace remaining "."'s with "_"
  263. identity = #identity > 0 and identity or "lovegame"
  264. -- When conf.lua is initially loaded, the main source should be checked
  265. -- before the save directory (the identity should be appended.)
  266. pcall(love.filesystem.setIdentity, identity, true)
  267. if can_has_game and not (love.filesystem.isFile("main.lua") or love.filesystem.isFile("conf.lua")) then
  268. no_game_code = true
  269. end
  270. if not can_has_game then
  271. local nogame = require("love.nogame")
  272. nogame()
  273. end
  274. end
  275. function love.init()
  276. -- Create default configuration settings.
  277. -- NOTE: Adding a new module to the modules list
  278. -- will NOT make it load, see below.
  279. local c = {
  280. title = "Untitled",
  281. version = love._version,
  282. window = {
  283. width = 800,
  284. height = 600,
  285. x = nil,
  286. y = nil,
  287. minwidth = 1,
  288. minheight = 1,
  289. fullscreen = false,
  290. fullscreentype = "desktop",
  291. display = 1,
  292. vsync = true,
  293. msaa = 0,
  294. borderless = false,
  295. resizable = false,
  296. centered = true,
  297. highdpi = false,
  298. srgb = false,
  299. },
  300. modules = {
  301. event = true,
  302. keyboard = true,
  303. mouse = true,
  304. timer = true,
  305. joystick = true,
  306. touch = true,
  307. image = true,
  308. graphics = true,
  309. audio = true,
  310. math = true,
  311. physics = true,
  312. sound = true,
  313. system = true,
  314. font = true,
  315. thread = true,
  316. window = true,
  317. },
  318. console = false, -- Only relevant for windows.
  319. identity = false,
  320. appendidentity = false,
  321. accelerometerjoystick = true, -- Only relevant for Android / iOS.
  322. }
  323. -- Console hack, part 1.
  324. local openedconsole = false
  325. if love.arg.options.console.set and love._openConsole then
  326. love._openConsole()
  327. openedconsole = true
  328. end
  329. -- If config file exists, load it and allow it to update config table.
  330. if not love.conf and love.filesystem and love.filesystem.isFile("conf.lua") then
  331. require("conf")
  332. end
  333. -- Yes, conf.lua might not exist, but there are other ways of making
  334. -- love.conf appear, so we should check for it anyway.
  335. local confok, conferr
  336. if love.conf then
  337. confok, conferr = pcall(love.conf, c)
  338. -- If love.conf errors, we'll trigger the error after loading modules so
  339. -- the error message can be displayed in the window.
  340. end
  341. -- Console hack, part 2.
  342. if c.console and love._openConsole and not openedconsole then
  343. love._openConsole()
  344. end
  345. -- Hack for disabling accelerometer-as-joystick on Android / iOS.
  346. if love._setAccelerometerAsJoystick then
  347. love._setAccelerometerAsJoystick(c.accelerometerjoystick)
  348. end
  349. -- Gets desired modules.
  350. for k,v in ipairs{
  351. "thread",
  352. "timer",
  353. "event",
  354. "keyboard",
  355. "joystick",
  356. "mouse",
  357. "touch",
  358. "sound",
  359. "system",
  360. "audio",
  361. "image",
  362. "font",
  363. "window",
  364. "graphics",
  365. "math",
  366. "physics",
  367. } do
  368. if c.modules[v] then
  369. require("love." .. v)
  370. end
  371. end
  372. if love.event then
  373. love.createhandlers()
  374. end
  375. -- Check the version
  376. c.version = tostring(c.version)
  377. if not love.isVersionCompatible(c.version) then
  378. local major, minor, revision = c.version:match("^(%d+)%.(%d+)%.(%d+)$")
  379. if (not major or not minor or not revision) or (major ~= love._version_major and minor ~= love._version_minor) then
  380. local msg = "This game was made for a different version of LOVE.\n"..
  381. "It may not be not be compatible with the running version ("..love._version..")."
  382. print(msg)
  383. if love.window then
  384. love.window.showMessageBox("Compatibility Warning", msg, "warning")
  385. end
  386. end
  387. end
  388. if not confok and conferr then
  389. error(conferr)
  390. end
  391. -- Setup window here.
  392. if c.window and c.modules.window then
  393. assert(love.window.setMode(c.window.width, c.window.height,
  394. {
  395. fullscreen = c.window.fullscreen,
  396. fullscreentype = c.window.fullscreentype,
  397. vsync = c.window.vsync,
  398. msaa = c.window.msaa,
  399. resizable = c.window.resizable,
  400. minwidth = c.window.minwidth,
  401. minheight = c.window.minheight,
  402. borderless = c.window.borderless,
  403. centered = c.window.centered,
  404. display = c.window.display,
  405. highdpi = c.window.highdpi,
  406. srgb = c.window.srgb,
  407. x = c.window.x,
  408. y = c.window.y,
  409. }), "Could not set window mode")
  410. love.window.setTitle(c.window.title or c.title)
  411. if c.window.icon then
  412. assert(love.image, "If an icon is set in love.conf, love.image has to be loaded!")
  413. love.window.setIcon(love.image.newImageData(c.window.icon))
  414. end
  415. end
  416. -- Our first timestep, because window creation can take some time
  417. if love.timer then
  418. love.timer.step()
  419. end
  420. if love.filesystem then
  421. love.filesystem.setIdentity(c.identity or love.filesystem.getIdentity(), c.appendidentity)
  422. if love.filesystem.isFile("main.lua") then
  423. require("main")
  424. end
  425. end
  426. if no_game_code then
  427. error("No code to run\nYour game might be packaged incorrectly\nMake sure main.lua is at the top level of the zip")
  428. end
  429. end
  430. function love.run()
  431. if love.math then
  432. love.math.setRandomSeed(os.time())
  433. end
  434. if love.load then love.load(arg) end
  435. -- We don't want the first frame's dt to include time taken by love.load.
  436. if love.timer then love.timer.step() end
  437. local dt = 0
  438. -- Main loop time.
  439. while true do
  440. -- Process events.
  441. if love.event then
  442. love.event.pump()
  443. for name, a,b,c,d,e,f in love.event.poll() do
  444. if name == "quit" then
  445. if not love.quit or not love.quit() then
  446. return
  447. end
  448. end
  449. love.handlers[name](a,b,c,d,e,f)
  450. end
  451. end
  452. -- Update dt, as we'll be passing it to update
  453. if love.timer then
  454. love.timer.step()
  455. dt = love.timer.getDelta()
  456. end
  457. -- Call update and draw
  458. if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled
  459. if love.graphics and love.graphics.isActive() then
  460. love.graphics.clear(love.graphics.getBackgroundColor())
  461. love.graphics.origin()
  462. if love.draw then love.draw() end
  463. love.graphics.present()
  464. end
  465. if love.timer then love.timer.sleep(0.001) end
  466. end
  467. end
  468. -----------------------------------------------------------
  469. -- Error screen.
  470. -----------------------------------------------------------
  471. local debug, print = debug, print
  472. local function error_printer(msg, layer)
  473. print((debug.traceback("Error: " .. tostring(msg), 1+(layer or 1)):gsub("\n[^\n]+$", "")))
  474. end
  475. function love.errhand(msg)
  476. msg = tostring(msg)
  477. error_printer(msg, 2)
  478. if not love.window or not love.graphics or not love.event then
  479. return
  480. end
  481. if not love.graphics.isCreated() or not love.window.isCreated() then
  482. local success, status = pcall(love.window.setMode, 800, 600)
  483. if not success or not status then
  484. return
  485. end
  486. end
  487. -- Reset state.
  488. if love.mouse then
  489. love.mouse.setVisible(true)
  490. love.mouse.setGrabbed(false)
  491. end
  492. if love.joystick then
  493. -- Stop all joystick vibrations.
  494. for i,v in ipairs(love.joystick.getJoysticks()) do
  495. v:setVibration()
  496. end
  497. end
  498. if love.audio then love.audio.stop() end
  499. love.graphics.reset()
  500. local font = love.graphics.setNewFont(math.floor(love.window.toPixels(14)))
  501. local sRGB = select(3, love.window.getMode()).srgb
  502. if sRGB and love.math then
  503. love.graphics.setBackgroundColor(love.math.gammaToLinear(89, 157, 220))
  504. else
  505. love.graphics.setBackgroundColor(89, 157, 220)
  506. end
  507. love.graphics.setColor(255, 255, 255, 255)
  508. local trace = debug.traceback()
  509. love.graphics.clear(love.graphics.getBackgroundColor())
  510. love.graphics.origin()
  511. local err = {}
  512. table.insert(err, "Error\n")
  513. table.insert(err, msg.."\n\n")
  514. for l in string.gmatch(trace, "(.-)\n") do
  515. if not string.match(l, "boot.lua") then
  516. l = string.gsub(l, "stack traceback:", "Traceback\n")
  517. table.insert(err, l)
  518. end
  519. end
  520. local p = table.concat(err, "\n")
  521. p = string.gsub(p, "\t", "")
  522. p = string.gsub(p, "%[string \"(.-)\"%]", "%1")
  523. local function draw()
  524. local pos = love.window.toPixels(70)
  525. love.graphics.clear(love.graphics.getBackgroundColor())
  526. love.graphics.printf(p, pos, pos, love.graphics.getWidth() - pos)
  527. love.graphics.present()
  528. end
  529. while true do
  530. love.event.pump()
  531. for e, a, b, c in love.event.poll() do
  532. if e == "quit" then
  533. return
  534. end
  535. if e == "keypressed" and a == "escape" then
  536. return
  537. end
  538. end
  539. draw()
  540. if love.timer then
  541. love.timer.sleep(0.1)
  542. end
  543. end
  544. end
  545. local function deferErrhand(...)
  546. local handler = love.errhand or error_printer
  547. return handler(...)
  548. end
  549. -----------------------------------------------------------
  550. -- The root of all calls.
  551. -----------------------------------------------------------
  552. return function()
  553. local result = xpcall(love.boot, error_printer)
  554. if not result then return 1 end
  555. local result = xpcall(love.init, deferErrhand)
  556. if not result then return 1 end
  557. local result, retval = xpcall(love.run, deferErrhand)
  558. if not result then return 1 end
  559. return tonumber(retval) or 0
  560. end