First working menu script.

This commit is contained in:
Scott Duensing 2020-03-30 20:20:40 -05:00
parent 98ff928553
commit 9535e202b9
12 changed files with 175 additions and 39 deletions

2
.gitignore vendored
View file

@ -49,3 +49,5 @@ Makefile.in
singe/menuBackground.mkv singe/menuBackground.mkv
singe/menuBackground_mkv.h singe/menuBackground_mkv.h
singe/Menu_singe.h singe/Menu_singe.h
singe/FreeSansBold_ttf.h
singe/Manual_pdf.h

View file

@ -56,6 +56,20 @@ function utilDump(o)
end end
function utilGetTableSize(t)
local count = 0
for _, __ in pairs(t) do
count = count + 1
end
return count
end
function utilTrim(s)
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
SCANCODE = { SCANCODE = {
A = { name = "A", value = 4 }, A = { name = "A", value = 4 },
B = { name = "B", value = 5 }, B = { name = "B", value = 5 },

BIN
singe/Manual.odt (Stored with Git LFS)

Binary file not shown.

View file

@ -29,28 +29,13 @@ function compareTitles(a, b)
end end
function box(x1, y1, x2, y2)
overlayLine(x1, y1, x2, y1)
overlayLine(x2, y1, x2, y2)
overlayLine(x2, y2, x1, y2)
overlayLine(x1, y2, x1, y1)
end
-- Remove whitespace from string
function trim(s)
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
function wrapText(text, maxWidth) function wrapText(text, maxWidth)
local words = {} local words = {}
local line = "" local line = ""
local lastLine = "" local lastLine = ""
local lastWord = "" local lastWord = ""
local newLine = false local newLine = false
local trimBreak = utilTrim(WRAP_BREAK)
-- Break input into words -- Break input into words
for w in text:gmatch("%S+") do for w in text:gmatch("%S+") do
@ -66,14 +51,26 @@ function wrapText(text, maxWidth)
line = lastWord line = lastWord
newLine = false newLine = false
end end
line = trim(line .. " " .. word) line = utilTrim(line .. " " .. word)
-- Create a temporary sprite to see how wide this is -- Create a temporary sprite to see how wide this is
if string.len(line) > 0 then if string.len(line) > 0 then
spriteTemp = fontToSprite(line) spriteTemp = fontToSprite(line)
if spriteGetWidth(spriteTemp) > maxWidth then if spriteGetWidth(spriteTemp) > maxWidth or word == trimBreak then
-- Was the wrap forced?
if word == trimBreak then
word = ""
if spriteGetWidth(spriteTemp) <= maxWidth then
lastLine = lastLine .. lastWord
end
end
-- We wrapped - Create sprite from this line before the last word was added -- We wrapped - Create sprite from this line before the last word was added
table.insert(TEXT_SPRITE_LIST, fontToSprite(lastLine)) if string.len(lastLine) > 0 then
table.insert(TEXT_SPRITE_LIST, fontToSprite(lastLine))
else
-- Blank line?
table.insert(TEXT_SPRITE_LIST, -1)
end
-- Get ready for the next line -- Get ready for the next line
line = "" line = ""
lastWord = word lastWord = word
@ -102,7 +99,9 @@ function loadGameAssets(firstGame)
spriteUnload(SPRITE_MARQUEE) spriteUnload(SPRITE_MARQUEE)
videoUnload(VIDEO_ATTRACT) videoUnload(VIDEO_ATTRACT)
for _, handle in ipairs(TEXT_SPRITE_LIST) do for _, handle in ipairs(TEXT_SPRITE_LIST) do
spriteUnload(handle) if handle >= 0 then
spriteUnload(handle)
end
end end
TEXT_SPRITE_LIST = {} TEXT_SPRITE_LIST = {}
end end
@ -113,13 +112,38 @@ function loadGameAssets(firstGame)
videoPlay(VIDEO_ATTRACT) videoPlay(VIDEO_ATTRACT)
videoSeek(VIDEO_ATTRACT, GAME_LIST[GAME_SELECTED].ATTRACT_START) videoSeek(VIDEO_ATTRACT, GAME_LIST[GAME_SELECTED].ATTRACT_START)
videoSetVolume(VIDEO_ATTRACT, 0, 0) videoSetVolume(VIDEO_ATTRACT, 0, 0)
wrapText(GAME_LIST[GAME_SELECTED].DESCRIPTION, TEXT_W)
-- Build text sprites
local textBox = GAME_LIST[GAME_SELECTED].DESCRIPTION .. WRAP_BREAK .. WRAP_BREAK ..
"Year: " .. GAME_LIST[GAME_SELECTED].YEAR .. WRAP_BREAK ..
"Genere: " .. GAME_LIST[GAME_SELECTED].GENERE .. 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 end
function onInputPressed(what) function onInputPressed(what)
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 if what == SWITCH_LEFT then
GAME_SELECTED = GAME_SELECTED - 1 GAME_SELECTED = GAME_SELECTED - 1
if GAME_SELECTED < 1 then if GAME_SELECTED < 1 then
@ -137,6 +161,11 @@ function onInputPressed(what)
end 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 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
-- Save what game we're currently viewing
local cfg = io.open(CONFIG_FILE, "w")
cfg:write("GAME_SELECTED = " .. GAME_SELECTED .. "\n")
cfg:close()
-- Start next game
scriptPush(GAME_LIST[GAME_SELECTED]) scriptPush(GAME_LIST[GAME_SELECTED])
end end
@ -147,6 +176,8 @@ function onOverlayUpdate()
local x = 0 local x = 0
local y = 0 local y = 0
local c = 0
local t = 0
overlayClear() overlayClear()
@ -169,9 +200,27 @@ function onOverlayUpdate()
-- Game Description -- Game Description
colorForeground(255, 255, 255, 255) colorForeground(255, 255, 255, 255)
y = TEXT_Y y = TEXT_Y
c = 0
t = 1
for _, handle in ipairs(TEXT_SPRITE_LIST) do for _, handle in ipairs(TEXT_SPRITE_LIST) do
spriteDraw(TEXT_X, y, handle) -- Find height of font and number of lines that fit
y = y + spriteGetHeight(handle) + 1 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
return(OVERLAY_UPDATED) return(OVERLAY_UPDATED)
@ -252,8 +301,24 @@ debugPrint(" Video is " .. VIDEO_W .. "x" .. VIDEO_H)
debugPrint(" Text is " .. TEXT_W .. "x" .. TEXT_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 = {} TEXT_SPRITE_LIST = {}
-- Load configuration
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
-- Prime the pump -- Prime the pump
GAME_SELECTED = 1
loadGameAssets(true) loadGameAssets(true)

View file

@ -65,7 +65,7 @@ function doBuild() {
echo "Compressing ${TARGET}..." echo "Compressing ${TARGET}..."
${CROSS}-strip "${TARGET}" ${CROSS}-strip "${TARGET}"
upx -q -9 "${TARGET}" upx -9 "${TARGET}"
popd popd
} }

View file

@ -34,6 +34,7 @@
#include "Framework_singe.h" #include "Framework_singe.h"
#include "controls_cfg.h" #include "controls_cfg.h"
#include "Menu_singe.h" #include "Menu_singe.h"
#include "FreeSansBold_ttf.h"
#include "menuBackground_mkv.h" #include "menuBackground_mkv.h"
#include "Manual_pdf.h" #include "Manual_pdf.h"

View file

@ -440,7 +440,7 @@ bool extractFile(char *filename, unsigned char *data, int32_t length) {
if (!out) utilDie("Unable to create %s", filename); if (!out) utilDie("Unable to create %s", filename);
fwrite(data, length, 1, out); fwrite(data, length, 1, out);
fclose(out); fclose(out);
utilSay("Created File: %s", filename); utilSay(">>> Created File: %s", filename);
return true; return true;
} }
@ -652,6 +652,7 @@ void queueScript(ConfigT *conf) {
__attribute__((noreturn)) __attribute__((noreturn))
void showUsage(char *name, char *message) { void showUsage(char *name, char *message) {
char *temp = NULL; char *temp = NULL;
char *data = NULL;
bool created = false; bool created = false;
int32_t result = 0; int32_t result = 0;
@ -700,17 +701,36 @@ void showUsage(char *name, char *message) {
free(temp); free(temp);
// Singe/Manual.pdf // Singe/Manual.pdf
temp = utilCreateString("Singe%Manual.pdf", utilGetPathSeparator()); temp = utilCreateString("Singe%Manual.pdf", utilGetPathSeparator());
created |= extractFile(temp, Manual_pdf, Manual_pdf_len); //created |= extractFile(temp, Manual_pdf, Manual_pdf_len);
free(temp); free(temp);
// Singe/Menu.singe // Singe/Menu.singe
temp = utilCreateString("Singe%cMenu.singe", utilGetPathSeparator()); temp = utilCreateString("Singe%cMenu.singe", utilGetPathSeparator());
created |= extractFile(temp, Menu_singe, Menu_singe_len); created |= extractFile(temp, Menu_singe, Menu_singe_len);
free(temp); free(temp);
// Singe/FreeSansBold.ttf
temp = utilCreateString("Singe%cFreeSansBold.ttf", utilGetPathSeparator());
created |= extractFile(temp, FreeSansBold_ttf, FreeSansBold_ttf_len);
free(temp);
// Singe/menuBackground.mkv // Singe/menuBackground.mkv
temp = utilCreateString("Singe%cmenuBackground.mkv", utilGetPathSeparator()); temp = utilCreateString("Singe%cmenuBackground.mkv", utilGetPathSeparator());
created |= extractFile(temp, menuBackground_mkv, menuBackground_mkv_len); created |= extractFile(temp, menuBackground_mkv, menuBackground_mkv_len);
free(temp); free(temp);
// Script to start menu system
if (utilGetPathSeparator() == '/') {
// Unix-ish
temp = utilCreateString("Menu.sh");
data = utilCreateString("#!/bin/bash\n\n./%s -k -x 720 -y 480 -d data -v Singe/menuBackground.mkv Singe/Menu.singe\n", utilGetLastPathComponent(name));
} else {
// Winders
temp = utilCreateString("Menu.bat");
data = utilCreateString("@echo off\n\r\n\r%s -k -x 720 -y 480 -d data -v Singe\\menuBackground.mkv Singe\\Menu.singe\n\r", utilGetLastPathComponent(name));
}
created |= extractFile(temp, (unsigned char *)data, strlen(data));
utilChMod(temp, 0777);
free(data);
data = NULL;
temp = NULL; temp = NULL;
if (created) utilSay(""); if (created) utilSay("");
@ -741,9 +761,9 @@ int main(int argc, char *argv[]) {
while (_scriptQueue) { while (_scriptQueue) {
q = _scriptQueue; q = _scriptQueue;
conf = (ConfigT *)&q->conf; conf = (ConfigT *)&q->conf;
LL_DELETE(_scriptQueue, q);
launcher(exeName, conf); launcher(exeName, conf);
destroyConf(&conf); destroyConf(&conf);
LL_DELETE(_scriptQueue, q);
} }
return 0; return 0;

View file

@ -589,6 +589,9 @@ createEmbeddedBinary controls.cfg controls_cfg.h CONTROLS_CFG_H
# === Singe Menu App === # === Singe Menu App ===
createEmbeddedBinary Menu.singe Menu_singe.h MENU_SINGE_H createEmbeddedBinary Menu.singe Menu_singe.h MENU_SINGE_H
# === Singe Menu Font ===
createEmbeddedBinary FreeSansBold.ttf FreeSansBold_ttf.h FREESANSBOLD_TTF_H
# === Singe Menu Background Video === # === Singe Menu Background Video ===
if [[ ! -f menuBackground_mkv.h ]]; then if [[ ! -f menuBackground_mkv.h ]]; then
ffmpeg -i 180503_01_PurpleGrid.mp4 -filter:v 'crop=ih/3*4:ih' -vf scale=720:480 -c:v libx264 -c:a copy menuBackground.mkv ffmpeg -i 180503_01_PurpleGrid.mp4 -filter:v 'crop=ih/3*4:ih' -vf scale=720:480 -c:v libx264 -c:a copy menuBackground.mkv

View file

@ -280,6 +280,7 @@ int32_t apiScriptPush(lua_State *L);
int32_t apiSingeDisablePauseKey(lua_State *L); int32_t apiSingeDisablePauseKey(lua_State *L);
int32_t apiSingeEnablePauseKey(lua_State *L); int32_t apiSingeEnablePauseKey(lua_State *L);
int32_t apiSingeGetDataPath(lua_State *L);
int32_t apiSingeGetPauseFlag(lua_State *L); int32_t apiSingeGetPauseFlag(lua_State *L);
int32_t apiSingeGetScriptPath(lua_State *L); int32_t apiSingeGetScriptPath(lua_State *L);
int32_t apiSingeSetGameName(lua_State *L); int32_t apiSingeSetGameName(lua_State *L);
@ -2131,9 +2132,14 @@ int32_t apiVideoLoad(lua_State *L) {
name = lua_tostring(L, 1); name = lua_tostring(L, 1);
// Create data directory based on video path. // Create data directory based on video path.
data = utilCreateString("%s%s", _global.conf.dataDirBase, utilGetUpToLastPathComponent((char *)name)); data = utilCreateString("%s%s", _global.conf.dataDirBase, utilGetUpToLastPathComponent((char *)name));
// Be sure it exists.
utilMkDirP(data, 0777);
if (!utilPathExists(data)) {
luaDie(L, "videoLoad", "Unable to create data directory: %s", data);
}
// Load this video.
video = (VideoT *)calloc(1, sizeof(VideoT)); video = (VideoT *)calloc(1, sizeof(VideoT));
if (!video) luaDie(L, "videoLoad", "Unable to allocate new video."); if (!video) luaDie(L, "videoLoad", "Unable to allocate new video.");
// Load this video.
video->handle = videoLoad((char *)name, data, false, _global.renderer); video->handle = videoLoad((char *)name, data, false, _global.renderer);
if (video->handle < 0) luaDie(L, "videoLoad", "Failed to load video: %s", name); if (video->handle < 0) luaDie(L, "videoLoad", "Failed to load video: %s", name);
video->id = _global.nextVideoId; video->id = _global.nextVideoId;
@ -2674,6 +2680,13 @@ int32_t apiSingeVersion(lua_State *L) {
} }
int32_t apiSingeGetDataPath(lua_State *L) {
luaTrace(L, "singeGetDataPath", "%s", _global.conf.dataDir);
lua_pushstring(L, _global.conf.dataDir);
return 1;
}
int32_t apiSingeSetGameName(lua_State *L) { int32_t apiSingeSetGameName(lua_State *L) {
// Adds the name of the singe game to the window's title bar. // Adds the name of the singe game to the window's title bar.
// Valid value is a string no longer than 25 characters. // Valid value is a string no longer than 25 characters.
@ -2709,13 +2722,7 @@ int32_t apiSingeSetGameName(lua_State *L) {
int32_t apiSingeGetScriptPath(lua_State *L) { int32_t apiSingeGetScriptPath(lua_State *L) {
// Returns the path to the singe script. luaTrace(L, "singeGetScriptPath", "%s", _global.conf.scriptFile);
// e.g. lua code,
//
// sGameDirectory = singeGetScriptPath()
//
luaTrace(L, "singeSetScriptPath", "%s", _global.conf.scriptFile);
lua_pushstring(L, _global.conf.scriptFile); lua_pushstring(L, _global.conf.scriptFile);
return 1; return 1;
} }
@ -3313,7 +3320,6 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, ConfigT *conf) {
// Set up globals // Set up globals
memset(&_global, 0, sizeof(GlobalT)); memset(&_global, 0, sizeof(GlobalT));
memcpy(&_global.conf, conf, sizeof(ConfigT));
_global.colorForeground.r = 255; _global.colorForeground.r = 255;
_global.colorForeground.g = 255; _global.colorForeground.g = 255;
_global.colorForeground.b = 255; _global.colorForeground.b = 255;
@ -3331,6 +3337,14 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, ConfigT *conf) {
_global.discStopped = true; _global.discStopped = true;
_global.mouseEnabled = true; _global.mouseEnabled = true;
// Deep copy conf
memcpy(&_global.conf, conf, sizeof(ConfigT));
_global.conf.dataDir = strdup(conf->dataDir);
_global.conf.dataDirBase = strdup(conf->dataDirBase);
_global.conf.scriptFile = strdup(conf->scriptFile);
_global.conf.videoFile = strdup(conf->videoFile);
// Input mappings
_global.controlMappings[INPUT_UP].name = "INPUT_UP"; _global.controlMappings[INPUT_UP].name = "INPUT_UP";
_global.controlMappings[INPUT_LEFT].name = "INPUT_LEFT"; _global.controlMappings[INPUT_LEFT].name = "INPUT_LEFT";
_global.controlMappings[INPUT_DOWN].name = "INPUT_DOWN"; _global.controlMappings[INPUT_DOWN].name = "INPUT_DOWN";
@ -3510,6 +3524,7 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, ConfigT *conf) {
lua_register(_global.luaContext, "singeDisablePauseKey", apiSingeDisablePauseKey); lua_register(_global.luaContext, "singeDisablePauseKey", apiSingeDisablePauseKey);
lua_register(_global.luaContext, "singeEnablePauseKey", apiSingeEnablePauseKey); lua_register(_global.luaContext, "singeEnablePauseKey", apiSingeEnablePauseKey);
lua_register(_global.luaContext, "singeGetDataPath", apiSingeGetDataPath);
lua_register(_global.luaContext, "singeGetHeight", apiDaphneGetHeight); lua_register(_global.luaContext, "singeGetHeight", apiDaphneGetHeight);
lua_register(_global.luaContext, "singeGetPauseFlag", apiSingeGetPauseFlag); lua_register(_global.luaContext, "singeGetPauseFlag", apiSingeGetPauseFlag);
lua_register(_global.luaContext, "singeGetScriptPath", apiSingeGetScriptPath); lua_register(_global.luaContext, "singeGetScriptPath", apiSingeGetScriptPath);

View file

@ -156,6 +156,7 @@ HEADERS += \
indexing.h \ indexing.h \
controls_cfg.h \ controls_cfg.h \
Menu_singe.h \ Menu_singe.h \
FreeSansBold_ttf.h \
menuBackground_mkv.h \ menuBackground_mkv.h \
Manual_pdf.h Manual_pdf.h

View file

@ -46,6 +46,20 @@ static bool _consoleEnabled = true;
static FILE *_utilTraceFile = NULL; static FILE *_utilTraceFile = NULL;
bool utilChMod(const char *path, const mode_t mode) {
bool result = true;
#ifdef _WIN32
(void)path;
(void)mode;
#else
result = (chmod(path, mode) >= 0);
#endif
return result;
}
char *utilCreateString(char *format, ...) { char *utilCreateString(char *format, ...) {
va_list args; va_list args;
char *string; char *string;

View file

@ -35,6 +35,7 @@
#define UTIL_PATH_MAX 1024 #define UTIL_PATH_MAX 1024
bool utilChMod(const char *path, const mode_t mode);
char *utilCreateString(char *format, ...); char *utilCreateString(char *format, ...);
char *utilCreateStringVArgs(char *format, va_list args); char *utilCreateStringVArgs(char *format, va_list args);
void utilDie(char *fmt, ...); void utilDie(char *fmt, ...);