--[[ * * Singe 2 * Copyright (C) 2006-2024 Scott Duensing * * 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * --]] dofile("Singe/Framework.singe") lfs = require("lfs") function compareTitles(a, b) return a.TITLE < b.TITLE end function loadGameAssets(firstGame) if not firstGame then spriteUnload(SPRITE_CABINET) spriteUnload(SPRITE_MARQUEE) videoUnload(VIDEO_ATTRACT) for _, handle in ipairs(TEXT_SPRITE_LIST) do if handle >= 0 then spriteUnload(handle) end end TEXT_SPRITE_LIST = {} end SPRITE_CABINET = spriteLoad(GAME_LIST[GAME_SELECTED].CABINET) SPRITE_MARQUEE = spriteLoad(GAME_LIST[GAME_SELECTED].MARQUEE) VIDEO_ATTRACT = videoLoad(GAME_LIST[GAME_SELECTED].ATTRACT) videoPlay(VIDEO_ATTRACT) videoSeek(VIDEO_ATTRACT, GAME_LIST[GAME_SELECTED].ATTRACT_START) videoSetVolume(VIDEO_ATTRACT, 0, 0) -- Build text sprites local textBox = GAME_LIST[GAME_SELECTED].DESCRIPTION .. WRAP_BREAK .. WRAP_BREAK .. "Year: " .. GAME_LIST[GAME_SELECTED].YEAR .. WRAP_BREAK .. "Genre: " .. GAME_LIST[GAME_SELECTED].GENRE .. WRAP_BREAK .. "Platform: " .. GAME_LIST[GAME_SELECTED].PLATFORM .. WRAP_BREAK .. "Developer: " .. GAME_LIST[GAME_SELECTED].DEVELOPER .. WRAP_BREAK .. "Publisher: " .. GAME_LIST[GAME_SELECTED].PUBLISHER .. WRAP_BREAK .. WRAP_BREAK .. "Singe Port: " .. GAME_LIST[GAME_SELECTED].CREATOR .. WRAP_BREAK .. "Source: " .. GAME_LIST[GAME_SELECTED].SOURCE wrapText(textBox, TEXT_W) TEXT_LINE_COUNT = utilGetTableSize(TEXT_SPRITE_LIST) TEXT_LINE_TOP = 1 end function onInputPressed(what) -- Are we displaying the grid background? if (discGetFrame() >= DISC_GRID_START) then if what == SWITCH_UP then if TEXT_LINE_TOP > 1 then TEXT_LINE_TOP = TEXT_LINE_TOP - 1 end end if what == SWITCH_DOWN then if TEXT_LINE_TOP < TEXT_LINE_COUNT - TEXT_LINE_LIMIT + 1 then TEXT_LINE_TOP = TEXT_LINE_TOP + 1 end end if what == SWITCH_LEFT then GAME_SELECTED = GAME_SELECTED - 1 if GAME_SELECTED < 1 then GAME_SELECTED = GAME_COUNT end loadGameAssets(false) end if what == SWITCH_RIGHT then GAME_SELECTED = GAME_SELECTED + 1 if GAME_SELECTED > GAME_COUNT then GAME_SELECTED = 1 end loadGameAssets(false) end if what == SWITCH_START1 or what == SWITCH_START2 or what == SWITCH_BUTTON1 or what == SWITCH_BUTTON2 or what == SWITCH_BUTTON3 or what == SWITCH_BUTTON4 then -- Start next game SHUTDOWN_FROM_PUSH = true scriptPush(GAME_LIST[GAME_SELECTED]) end end end function onOverlayUpdate() local x = 0 local y = 0 local c = 0 local t = 0 overlayClear() -- Are we displaying the grid background? if (discGetFrame() >= DISC_GRID_START) then -- Cabinet image x = CABINET_X + (CABINET_W - spriteGetWidth(SPRITE_CABINET)) * 0.5 y = CABINET_Y + (CABINET_H - spriteGetHeight(SPRITE_CABINET)) * 0.5 spriteDraw(x, y, SPRITE_CABINET) -- Marquee Image x = MARQUEE_X + (MARQUEE_W - spriteGetWidth(SPRITE_MARQUEE)) * 0.5 y = MARQUEE_Y + (MARQUEE_H - spriteGetHeight(SPRITE_MARQUEE)) * 0.5 spriteDraw(x, y, SPRITE_MARQUEE) -- Attract Mode Video videoDraw(VIDEO_ATTRACT, VIDEO_X, VIDEO_Y, VIDEO_X + VIDEO_W, VIDEO_Y + VIDEO_H) if videoGetFrame(VIDEO_ATTRACT) > GAME_LIST[GAME_SELECTED].ATTRACT_END then videoSeek(VIDEO_ATTRACT, GAME_LIST[GAME_SELECTED].ATTRACT_START) end -- Game Description colorForeground(255, 255, 255, 255) y = TEXT_Y c = 0 t = 1 for _, handle in ipairs(TEXT_SPRITE_LIST) do -- Find height of font and number of lines that fit if TEXT_LINE_HEIGHT == 0 then if (handle >= 0) then TEXT_LINE_HEIGHT = spriteGetHeight(handle) TEXT_LINE_LIMIT = math.floor(TEXT_H / TEXT_LINE_HEIGHT) end end -- Only display what is visible in the window if (t >= TEXT_LINE_TOP) then if (c < TEXT_LINE_LIMIT) then if (handle >= 0) then spriteDraw(TEXT_X, y, handle) end y = y + TEXT_LINE_HEIGHT + 1 c = c + 1 end end t = t + 1 end end -- Loop video? if (discGetFrame() >= DISC_LAST_FRAME) then discSkipToFrame(DISC_GRID_START) end return(OVERLAY_UPDATED) end function onShutdown() saveConfig(not SHUTDOWN_FROM_PUSH) end function saveConfig(showIntro) if GAME_COUNT > 0 then -- Save what game we're currently viewing local cfg = io.open(CONFIG_FILE, "w") cfg:write("GAME_SELECTED = " .. GAME_SELECTED .. "\n") cfg:write("SHOW_INTRO = " .. tostring(showIntro) .. "\n") cfg:close() end end function wrapText(text, maxWidth) local words = {} local toWrap = "" local line = "" local lastLine = "" local fragment = "" local doBreak = false local spriteTemp = -1 local trimBreak = utilTrim(WRAP_BREAK) -- Break input into words toWrap = text .. WRAP_BREAK for w in toWrap:gmatch("%S+") do words[#words+1] = w end -- Iterate over words and try to fit them in the space given for _, word in ipairs(words) do -- Is this an intentional break? if word == trimBreak then doBreak = true else -- Add this word to the line line = utilTrim(line .. " " .. word) -- Do we even have text? if string.len(line) > 0 then -- Is the line too long to fit now? spriteTemp = fontToSprite(line) if spriteGetWidth(spriteTemp) > maxWidth then doBreak = true fragment = word line = lastLine else fragment = "" end spriteUnload(spriteTemp) end end lastLine = line -- Did we find a break? if doBreak then -- Did we get anything to print? if string.len(line) > 0 then table.insert(TEXT_SPRITE_LIST, fontToSprite(line)) else -- Blank line table.insert(TEXT_SPRITE_LIST, -1) end -- Reset for next line line = fragment fragment = "" doBreak = false end end end -- Search for games.dat files in subdirectories GAME_LIST = {} GAME_COUNT = 0 for dir in lfs.dir(".") do if dir ~= "." and dir ~= ".." then local dirattr = lfs.attributes(dir) if dirattr.mode == "directory" then for file in lfs.dir(dir .. "/.") do if file == "games.dat" then -- Load games.dat dofile(dir .. "/games.dat") for key,value in pairs(GAMES) do table.insert(GAME_LIST, value) GAME_COUNT = GAME_COUNT + 1 end GAMES = {} end end end end end table.sort(GAME_LIST, compareTitles) if GAME_COUNT == 0 then debugPrint("No games found! Exiting.") singeQuit() else overlaySetResolution(vldpGetWidth(), vldpGetHeight()) freeSans18 = fontLoad("Singe/FreeSansBold.ttf", 18) fontQuality(FONT_QUALITY_BLENDED) fontSelect(freeSans18) DISC_GRID_START = 180 DISC_LAST_FRAME = 359 MARGIN_X = 25 MARGIN_Y = 25 VIDEO_W = 320 VIDEO_H = 200 MARQUEE_H = 75 MARQUEE_W = VIDEO_W MARQUEE_X = overlayGetWidth() - MARGIN_X - MARQUEE_W MARQUEE_Y = MARGIN_Y VIDEO_X = overlayGetWidth() - MARGIN_X - VIDEO_W VIDEO_Y = MARGIN_Y + MARQUEE_H + MARGIN_Y TEXT_W = VIDEO_W TEXT_X = overlayGetWidth() - MARGIN_X - TEXT_W TEXT_Y = VIDEO_Y + VIDEO_H + MARGIN_Y TEXT_H = overlayGetHeight() - MARGIN_Y - TEXT_Y CABINET_X = MARGIN_X CABINET_Y = MARGIN_Y CABINET_W = VIDEO_X - MARGIN_X - CABINET_X CABINET_H = overlayGetHeight() - MARGIN_Y - CABINET_Y --[[ Cabinet is 325x430 Marquee is 320x75 Video is 320x200 Text is 320x105 --]] --[[ debugPrint("Cabinet is " .. CABINET_W .. "x" .. CABINET_H) debugPrint("Marquee is " .. MARQUEE_W .. "x" .. MARQUEE_H) debugPrint(" Video is " .. VIDEO_W .. "x" .. VIDEO_H) debugPrint(" Text is " .. TEXT_W .. "x" .. TEXT_H) --]] WRAP_BREAK = " [!wb!] " TEXT_LINE_TOP = 1 TEXT_LINE_LIMIT = 0 TEXT_LINE_COUNT = 0 TEXT_LINE_HEIGHT = 0 TEXT_SPRITE_LIST = {} SHUTDOWN_FROM_PUSH = false -- Load configuration SHOW_INTRO = true CONFIG_FILE = singeGetDataPath() .. "menu.dat" local confattr = lfs.attributes(CONFIG_FILE) if confattr then dofile(CONFIG_FILE) if GAME_SELECTED > GAME_COUNT then GAME_SELECTED = GAME_COUNT end else GAME_SELECTED = 1 end discPlay() if (not SHOW_INTRO) then discSkipToFrame(DISC_GRID_START) end -- Prime the pump loadGameAssets(true) end