boot.lua 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801
  1. --[[
  2. Copyright (c) 2006-2019 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 p:gsub("\\", "/")
  25. end
  26. -- Makes sure there is a slash at the end
  27. -- of a path.
  28. function love.path.endslash(p)
  29. if p:sub(-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 tmp:find("/") == 1 then
  40. return true
  41. end
  42. -- Path is absolute if it starts with a
  43. -- letter followed by a colon.
  44. if tmp:find("%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 = p:find("/", a+1)
  70. if a then
  71. last = p:sub(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], m
  86. end
  87. love.arg.options = {
  88. console = { a = 0 },
  89. fused = {a = 0 },
  90. game = { a = 1 }
  91. }
  92. love.arg.optionIndices = {}
  93. function love.arg.parseOption(m, i)
  94. m.set = true
  95. if m.a > 0 then
  96. m.arg = {}
  97. for j=i,i+m.a-1 do
  98. love.arg.optionIndices[j] = true
  99. table.insert(m.arg, arg[j])
  100. end
  101. end
  102. return m.a
  103. end
  104. function love.arg.parseOptions()
  105. local game
  106. local argc = #arg
  107. local i = 1
  108. while i <= argc do
  109. -- Look for options.
  110. local m = arg[i]:match("^%-%-(.*)")
  111. if m and m ~= "" and love.arg.options[m] and not love.arg.options[m].set then
  112. love.arg.optionIndices[i] = true
  113. i = i + love.arg.parseOption(love.arg.options[m], i+1)
  114. elseif m == "" then -- handle '--' as an option
  115. love.arg.optionIndices[i] = true
  116. if not game then -- handle '--' followed by game name
  117. game = i + 1
  118. end
  119. break
  120. elseif not game then
  121. game = i
  122. end
  123. i = i + 1
  124. end
  125. if not love.arg.options.game.set then
  126. love.arg.parseOption(love.arg.options.game, game or 0)
  127. end
  128. end
  129. -- Returns the arguments that are passed to your game via love.load()
  130. -- arguments that were parsed as options are skipped.
  131. function love.arg.parseGameArguments(a)
  132. local out = {}
  133. local _, lowindex = love.arg.getLow(a)
  134. local o = lowindex
  135. for i=lowindex, #a do
  136. if not love.arg.optionIndices[i] then
  137. out[o] = a[i]
  138. o = o + 1
  139. end
  140. end
  141. return out
  142. end
  143. function love.createhandlers()
  144. -- Standard callback handlers.
  145. love.handlers = setmetatable({
  146. keypressed = function (b,s,r)
  147. if love.keypressed then return love.keypressed(b,s,r) end
  148. end,
  149. keyreleased = function (b,s)
  150. if love.keyreleased then return love.keyreleased(b,s) end
  151. end,
  152. textinput = function (t)
  153. if love.textinput then return love.textinput(t) end
  154. end,
  155. textedited = function (t,s,l)
  156. if love.textedited then return love.textedited(t,s,l) end
  157. end,
  158. mousemoved = function (x,y,dx,dy,t)
  159. if love.mousemoved then return love.mousemoved(x,y,dx,dy,t) end
  160. end,
  161. mousepressed = function (x,y,b,t,c)
  162. if love.mousepressed then return love.mousepressed(x,y,b,t,c) end
  163. end,
  164. mousereleased = function (x,y,b,t,c)
  165. if love.mousereleased then return love.mousereleased(x,y,b,t,c) end
  166. end,
  167. wheelmoved = function (x,y)
  168. if love.wheelmoved then return love.wheelmoved(x,y) end
  169. end,
  170. touchpressed = function (id,x,y,dx,dy,p)
  171. if love.touchpressed then return love.touchpressed(id,x,y,dx,dy,p) end
  172. end,
  173. touchreleased = function (id,x,y,dx,dy,p)
  174. if love.touchreleased then return love.touchreleased(id,x,y,dx,dy,p) end
  175. end,
  176. touchmoved = function (id,x,y,dx,dy,p)
  177. if love.touchmoved then return love.touchmoved(id,x,y,dx,dy,p) end
  178. end,
  179. joystickpressed = function (j,b)
  180. if love.joystickpressed then return love.joystickpressed(j,b) end
  181. end,
  182. joystickreleased = function (j,b)
  183. if love.joystickreleased then return love.joystickreleased(j,b) end
  184. end,
  185. joystickaxis = function (j,a,v)
  186. if love.joystickaxis then return love.joystickaxis(j,a,v) end
  187. end,
  188. joystickhat = function (j,h,v)
  189. if love.joystickhat then return love.joystickhat(j,h,v) end
  190. end,
  191. gamepadpressed = function (j,b)
  192. if love.gamepadpressed then return love.gamepadpressed(j,b) end
  193. end,
  194. gamepadreleased = function (j,b)
  195. if love.gamepadreleased then return love.gamepadreleased(j,b) end
  196. end,
  197. gamepadaxis = function (j,a,v)
  198. if love.gamepadaxis then return love.gamepadaxis(j,a,v) end
  199. end,
  200. joystickadded = function (j)
  201. if love.joystickadded then return love.joystickadded(j) end
  202. end,
  203. joystickremoved = function (j)
  204. if love.joystickremoved then return love.joystickremoved(j) end
  205. end,
  206. focus = function (f)
  207. if love.focus then return love.focus(f) end
  208. end,
  209. mousefocus = function (f)
  210. if love.mousefocus then return love.mousefocus(f) end
  211. end,
  212. visible = function (v)
  213. if love.visible then return love.visible(v) end
  214. end,
  215. quit = function ()
  216. return
  217. end,
  218. threaderror = function (t, err)
  219. if love.threaderror then return love.threaderror(t, err) end
  220. end,
  221. resize = function (w, h)
  222. if love.resize then return love.resize(w, h) end
  223. end,
  224. filedropped = function (f)
  225. if love.filedropped then return love.filedropped(f) end
  226. end,
  227. directorydropped = function (dir)
  228. if love.directorydropped then return love.directorydropped(dir) end
  229. end,
  230. lowmemory = function ()
  231. if love.lowmemory then love.lowmemory() end
  232. collectgarbage()
  233. collectgarbage()
  234. end,
  235. displayrotated = function (display, orient)
  236. if love.displayrotated then return love.displayrotated(display, orient) end
  237. end,
  238. }, {
  239. __index = function(self, name)
  240. error("Unknown event: " .. name)
  241. end,
  242. })
  243. end
  244. local function uridecode(s)
  245. return s:gsub("%%%x%x", function(str)
  246. return string.char(tonumber(str:sub(2), 16))
  247. end)
  248. end
  249. local no_game_code = false
  250. local invalid_game_path = nil
  251. -- This can't be overridden.
  252. function love.boot()
  253. -- This is absolutely needed.
  254. require("love.filesystem")
  255. local arg0 = love.arg.getLow(arg)
  256. love.filesystem.init(arg0)
  257. local exepath = love.filesystem.getExecutablePath()
  258. if #exepath == 0 then
  259. -- This shouldn't happen, but just in case we'll fall back to arg0.
  260. exepath = arg0
  261. end
  262. no_game_code = false
  263. invalid_game_path = nil
  264. -- Is this one of those fancy "fused" games?
  265. local can_has_game = pcall(love.filesystem.setSource, exepath)
  266. -- It's a fused game, don't parse --game argument
  267. if can_has_game then
  268. love.arg.options.game.set = true
  269. end
  270. -- Parse options now that we know which options we're looking for.
  271. love.arg.parseOptions()
  272. local o = love.arg.options
  273. local is_fused_game = can_has_game or love.arg.options.fused.set
  274. love.filesystem.setFused(is_fused_game)
  275. love.setDeprecationOutput(not love.filesystem.isFused())
  276. local identity = ""
  277. if not can_has_game and o.game.set and o.game.arg[1] then
  278. local nouri = o.game.arg[1]
  279. if nouri:sub(1, 7) == "file://" then
  280. nouri = uridecode(nouri:sub(8))
  281. end
  282. local full_source = love.path.getFull(nouri)
  283. can_has_game = pcall(love.filesystem.setSource, full_source)
  284. if not can_has_game then
  285. invalid_game_path = full_source
  286. end
  287. -- Use the name of the source .love as the identity for now.
  288. identity = love.path.leaf(full_source)
  289. else
  290. -- Use the name of the exe as the identity for now.
  291. identity = love.path.leaf(exepath)
  292. end
  293. -- Try to use the archive containing main.lua as the identity name. It
  294. -- might not be available, in which case the fallbacks above are used.
  295. local realdir = love.filesystem.getRealDirectory("main.lua")
  296. if realdir then
  297. identity = love.path.leaf(realdir)
  298. end
  299. identity = identity:gsub("^([%.]+)", "") -- strip leading "."'s
  300. identity = identity:gsub("%.([^%.]+)$", "") -- strip extension
  301. identity = identity:gsub("%.", "_") -- replace remaining "."'s with "_"
  302. identity = #identity > 0 and identity or "lovegame"
  303. -- When conf.lua is initially loaded, the main source should be checked
  304. -- before the save directory (the identity should be appended.)
  305. pcall(love.filesystem.setIdentity, identity, true)
  306. if can_has_game and not (love.filesystem.getInfo("main.lua") or love.filesystem.getInfo("conf.lua")) then
  307. no_game_code = true
  308. end
  309. if not can_has_game then
  310. local nogame = require("love.nogame")
  311. nogame()
  312. end
  313. end
  314. function love.init()
  315. -- Create default configuration settings.
  316. -- NOTE: Adding a new module to the modules list
  317. -- will NOT make it load, see below.
  318. local c = {
  319. title = "Untitled",
  320. version = love._version,
  321. window = {
  322. width = 800,
  323. height = 600,
  324. x = nil,
  325. y = nil,
  326. minwidth = 1,
  327. minheight = 1,
  328. fullscreen = false,
  329. fullscreentype = "desktop",
  330. display = 1,
  331. vsync = 1,
  332. msaa = 0,
  333. borderless = false,
  334. resizable = false,
  335. centered = true,
  336. highdpi = false,
  337. usedpiscale = true,
  338. },
  339. modules = {
  340. data = true,
  341. event = true,
  342. keyboard = true,
  343. mouse = true,
  344. timer = true,
  345. joystick = true,
  346. touch = true,
  347. image = true,
  348. graphics = true,
  349. audio = true,
  350. math = true,
  351. physics = true,
  352. sound = true,
  353. system = true,
  354. font = true,
  355. thread = true,
  356. window = true,
  357. video = true,
  358. },
  359. audio = {
  360. mixwithsystem = true, -- Only relevant for Android / iOS.
  361. },
  362. console = false, -- Only relevant for windows.
  363. identity = false,
  364. appendidentity = false,
  365. externalstorage = false, -- Only relevant for Android.
  366. accelerometerjoystick = true, -- Only relevant for Android / iOS.
  367. gammacorrect = false,
  368. }
  369. -- Console hack, part 1.
  370. local openedconsole = false
  371. if love.arg.options.console.set and love._openConsole then
  372. love._openConsole()
  373. openedconsole = true
  374. end
  375. -- If config file exists, load it and allow it to update config table.
  376. local confok, conferr
  377. if (not love.conf) and love.filesystem and love.filesystem.getInfo("conf.lua") then
  378. confok, conferr = pcall(require, "conf")
  379. end
  380. -- Yes, conf.lua might not exist, but there are other ways of making
  381. -- love.conf appear, so we should check for it anyway.
  382. if love.conf then
  383. confok, conferr = pcall(love.conf, c)
  384. -- If love.conf errors, we'll trigger the error after loading modules so
  385. -- the error message can be displayed in the window.
  386. end
  387. -- Console hack, part 2.
  388. if c.console and love._openConsole and not openedconsole then
  389. love._openConsole()
  390. end
  391. -- Hack for disabling accelerometer-as-joystick on Android / iOS.
  392. if love._setAccelerometerAsJoystick then
  393. love._setAccelerometerAsJoystick(c.accelerometerjoystick)
  394. end
  395. if love._setGammaCorrect then
  396. love._setGammaCorrect(c.gammacorrect)
  397. end
  398. if love._setAudioMixWithSystem and c.audio then
  399. love._setAudioMixWithSystem(c.audio.mixwithsystem)
  400. end
  401. -- Gets desired modules.
  402. for k,v in ipairs{
  403. "data",
  404. "thread",
  405. "timer",
  406. "event",
  407. "keyboard",
  408. "joystick",
  409. "mouse",
  410. "touch",
  411. "sound",
  412. "system",
  413. "audio",
  414. "image",
  415. "video",
  416. "font",
  417. "window",
  418. "graphics",
  419. "math",
  420. "physics",
  421. } do
  422. if c.modules[v] then
  423. require("love." .. v)
  424. end
  425. end
  426. if love.event then
  427. love.createhandlers()
  428. end
  429. -- Check the version
  430. c.version = tostring(c.version)
  431. if not love.isVersionCompatible(c.version) then
  432. local major, minor, revision = c.version:match("^(%d+)%.(%d+)%.(%d+)$")
  433. if (not major or not minor or not revision) or (major ~= love._version_major and minor ~= love._version_minor) then
  434. local msg = ("This game indicates it was made for version '%s' of LOVE.\n"..
  435. "It may not be compatible with the running version (%s)."):format(c.version, love._version)
  436. print(msg)
  437. if love.window then
  438. love.window.showMessageBox("Compatibility Warning", msg, "warning")
  439. end
  440. end
  441. end
  442. if not confok and conferr then
  443. error(conferr)
  444. end
  445. -- Setup window here.
  446. if c.window and c.modules.window then
  447. assert(love.window.setMode(c.window.width, c.window.height,
  448. {
  449. fullscreen = c.window.fullscreen,
  450. fullscreentype = c.window.fullscreentype,
  451. vsync = c.window.vsync,
  452. msaa = c.window.msaa,
  453. stencil = c.window.stencil,
  454. depth = c.window.depth,
  455. resizable = c.window.resizable,
  456. minwidth = c.window.minwidth,
  457. minheight = c.window.minheight,
  458. borderless = c.window.borderless,
  459. centered = c.window.centered,
  460. display = c.window.display,
  461. highdpi = c.window.highdpi,
  462. usedpiscale = c.window.usedpiscale,
  463. x = c.window.x,
  464. y = c.window.y,
  465. }), "Could not set window mode")
  466. love.window.setTitle(c.window.title or c.title)
  467. if c.window.icon then
  468. assert(love.image, "If an icon is set in love.conf, love.image must be loaded!")
  469. love.window.setIcon(love.image.newImageData(c.window.icon))
  470. end
  471. end
  472. -- Our first timestep, because window creation can take some time
  473. if love.timer then
  474. love.timer.step()
  475. end
  476. if love.filesystem then
  477. love.filesystem._setAndroidSaveExternal(c.externalstorage)
  478. love.filesystem.setIdentity(c.identity or love.filesystem.getIdentity(), c.appendidentity)
  479. if love.filesystem.getInfo("main.lua") then
  480. require("main")
  481. end
  482. end
  483. if no_game_code then
  484. error("No code to run\nYour game might be packaged incorrectly.\nMake sure main.lua is at the top level of the zip.")
  485. elseif invalid_game_path then
  486. error("Cannot load game at path '" .. invalid_game_path .. "'.\nMake sure a folder exists at the specified path.")
  487. end
  488. end
  489. -----------------------------------------------------------
  490. -- Default callbacks.
  491. -----------------------------------------------------------
  492. function love.run()
  493. if love.load then love.load(love.arg.parseGameArguments(arg), arg) end
  494. -- We don't want the first frame's dt to include time taken by love.load.
  495. if love.timer then love.timer.step() end
  496. local dt = 0
  497. -- Main loop time.
  498. return function()
  499. -- Process events.
  500. if love.event then
  501. love.event.pump()
  502. for name, a,b,c,d,e,f in love.event.poll() do
  503. if name == "quit" then
  504. if not love.quit or not love.quit() then
  505. return a or 0
  506. end
  507. end
  508. love.handlers[name](a,b,c,d,e,f)
  509. end
  510. end
  511. -- Update dt, as we'll be passing it to update
  512. if love.timer then dt = love.timer.step() end
  513. -- Call update and draw
  514. if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled
  515. if love.graphics and love.graphics.isActive() then
  516. love.graphics.origin()
  517. love.graphics.clear(love.graphics.getBackgroundColor())
  518. if love.draw then love.draw() end
  519. love.graphics.present()
  520. end
  521. if love.timer then love.timer.sleep(0.001) end
  522. end
  523. end
  524. local debug, print, error = debug, print, error
  525. function love.threaderror(t, err)
  526. error("Thread error ("..tostring(t)..")\n\n"..err, 0)
  527. end
  528. local utf8 = require("utf8")
  529. local function error_printer(msg, layer)
  530. print((debug.traceback("Error: " .. tostring(msg), 1+(layer or 1)):gsub("\n[^\n]+$", "")))
  531. end
  532. function love.errhand(msg)
  533. msg = tostring(msg)
  534. error_printer(msg, 2)
  535. if not love.window or not love.graphics or not love.event then
  536. return
  537. end
  538. if not love.graphics.isCreated() or not love.window.isOpen() then
  539. local success, status = pcall(love.window.setMode, 800, 600)
  540. if not success or not status then
  541. return
  542. end
  543. end
  544. -- Reset state.
  545. if love.mouse then
  546. love.mouse.setVisible(true)
  547. love.mouse.setGrabbed(false)
  548. love.mouse.setRelativeMode(false)
  549. if love.mouse.isCursorSupported() then
  550. love.mouse.setCursor()
  551. end
  552. end
  553. if love.joystick then
  554. -- Stop all joystick vibrations.
  555. for i,v in ipairs(love.joystick.getJoysticks()) do
  556. v:setVibration()
  557. end
  558. end
  559. if love.audio then love.audio.stop() end
  560. love.graphics.reset()
  561. local font = love.graphics.setNewFont(14)
  562. love.graphics.setColor(1, 1, 1, 1)
  563. local trace = debug.traceback()
  564. love.graphics.origin()
  565. local sanitizedmsg = {}
  566. for char in msg:gmatch(utf8.charpattern) do
  567. table.insert(sanitizedmsg, char)
  568. end
  569. sanitizedmsg = table.concat(sanitizedmsg)
  570. local err = {}
  571. table.insert(err, "Error\n")
  572. table.insert(err, sanitizedmsg)
  573. if #sanitizedmsg ~= #msg then
  574. table.insert(err, "Invalid UTF-8 string in error message.")
  575. end
  576. table.insert(err, "\n")
  577. for l in trace:gmatch("(.-)\n") do
  578. if not l:match("boot.lua") then
  579. l = l:gsub("stack traceback:", "Traceback\n")
  580. table.insert(err, l)
  581. end
  582. end
  583. local p = table.concat(err, "\n")
  584. p = p:gsub("\t", "")
  585. p = p:gsub("%[string \"(.-)\"%]", "%1")
  586. local function draw()
  587. local pos = 70
  588. love.graphics.clear(89/255, 157/255, 220/255)
  589. love.graphics.printf(p, pos, pos, love.graphics.getWidth() - pos)
  590. love.graphics.present()
  591. end
  592. local fullErrorText = p
  593. local function copyToClipboard()
  594. if not love.system then return end
  595. love.system.setClipboardText(fullErrorText)
  596. p = p .. "\nCopied to clipboard!"
  597. draw()
  598. end
  599. if love.system then
  600. p = p .. "\n\nPress Ctrl+C or tap to copy this error"
  601. end
  602. return function()
  603. love.event.pump()
  604. for e, a, b, c in love.event.poll() do
  605. if e == "quit" then
  606. return 1
  607. elseif e == "keypressed" and a == "escape" then
  608. return 1
  609. elseif e == "keypressed" and a == "c" and love.keyboard.isDown("lctrl", "rctrl") then
  610. copyToClipboard()
  611. elseif e == "touchpressed" then
  612. local name = love.window.getTitle()
  613. if #name == 0 or name == "Untitled" then name = "Game" end
  614. local buttons = {"OK", "Cancel"}
  615. if love.system then
  616. buttons[3] = "Copy to clipboard"
  617. end
  618. local pressed = love.window.showMessageBox("Quit "..name.."?", "", buttons)
  619. if pressed == 1 then
  620. return 1
  621. elseif pressed == 3 then
  622. copyToClipboard()
  623. end
  624. end
  625. end
  626. draw()
  627. if love.timer then
  628. love.timer.sleep(0.1)
  629. end
  630. end
  631. end
  632. -----------------------------------------------------------
  633. -- The root of all calls.
  634. -----------------------------------------------------------
  635. return function()
  636. local func
  637. local inerror = false
  638. local function deferErrhand(...)
  639. local errhand = love.errorhandler or love.errhand
  640. local handler = (not inerror and errhand) or error_printer
  641. inerror = true
  642. func = handler(...)
  643. end
  644. local function earlyinit()
  645. -- If love.boot fails, return 1 and finish immediately
  646. local result = xpcall(love.boot, error_printer)
  647. if not result then return 1 end
  648. -- If love.init or love.run fails, don't return a value,
  649. -- as we want the error handler to take over
  650. result = xpcall(love.init, deferErrhand)
  651. if not result then return end
  652. -- NOTE: We can't assign to func directly, as we'd
  653. -- overwrite the result of deferErrhand with nil on error
  654. local main
  655. result, main = xpcall(love.run, deferErrhand)
  656. if result then
  657. func = main
  658. end
  659. end
  660. func = earlyinit
  661. while func do
  662. local _, retval = xpcall(func, deferErrhand)
  663. if retval then return retval end
  664. coroutine.yield()
  665. end
  666. return 1
  667. end