376 lines
9 KiB
Text
376 lines
9 KiB
Text
--[[
|
|
*
|
|
* Singe 2
|
|
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
|
|
*
|
|
* 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 cleanTitle(a)
|
|
local output = string.lower(a)
|
|
output = string.gsub(output, '%p', ' ')
|
|
output = string.gsub(output, '^ +', '')
|
|
output = string.gsub(output, '^the ', '')
|
|
output = string.gsub(output, '^a ', '')
|
|
output = string.gsub(output, '^an ', '')
|
|
return output
|
|
end
|
|
|
|
|
|
function compareTitles(a, b)
|
|
return cleanTitle(a.TITLE) < cleanTitle(b.TITLE)
|
|
end
|
|
|
|
|
|
function loadGameAssets()
|
|
|
|
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
|
|
unloadGameAssets()
|
|
loadGameAssets()
|
|
end
|
|
|
|
if what == SWITCH_RIGHT then
|
|
GAME_SELECTED = GAME_SELECTED + 1
|
|
if GAME_SELECTED > GAME_COUNT then
|
|
GAME_SELECTED = 1
|
|
end
|
|
unloadGameAssets()
|
|
loadGameAssets()
|
|
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()
|
|
unloadGameAssets()
|
|
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 unloadGameAssets()
|
|
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
|
|
|
|
|
|
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()
|
|
end
|