First working menu script.
This commit is contained in:
parent
98ff928553
commit
9535e202b9
12 changed files with 175 additions and 39 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -49,3 +49,5 @@ Makefile.in
|
|||
singe/menuBackground.mkv
|
||||
singe/menuBackground_mkv.h
|
||||
singe/Menu_singe.h
|
||||
singe/FreeSansBold_ttf.h
|
||||
singe/Manual_pdf.h
|
||||
|
|
|
@ -56,6 +56,20 @@ function utilDump(o)
|
|||
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 = {
|
||||
A = { name = "A", value = 4 },
|
||||
B = { name = "B", value = 5 },
|
||||
|
|
BIN
singe/Manual.odt
(Stored with Git LFS)
BIN
singe/Manual.odt
(Stored with Git LFS)
Binary file not shown.
107
singe/Menu.singe
107
singe/Menu.singe
|
@ -29,28 +29,13 @@ function compareTitles(a, b)
|
|||
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)
|
||||
local words = {}
|
||||
local line = ""
|
||||
local lastLine = ""
|
||||
local lastWord = ""
|
||||
local newLine = false
|
||||
local trimBreak = utilTrim(WRAP_BREAK)
|
||||
|
||||
-- Break input into words
|
||||
for w in text:gmatch("%S+") do
|
||||
|
@ -66,14 +51,26 @@ function wrapText(text, maxWidth)
|
|||
line = lastWord
|
||||
newLine = false
|
||||
end
|
||||
line = trim(line .. " " .. word)
|
||||
line = utilTrim(line .. " " .. word)
|
||||
|
||||
-- Create a temporary sprite to see how wide this is
|
||||
if string.len(line) > 0 then
|
||||
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
|
||||
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
|
||||
line = ""
|
||||
lastWord = word
|
||||
|
@ -102,8 +99,10 @@ function loadGameAssets(firstGame)
|
|||
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
|
||||
|
||||
|
@ -113,13 +112,38 @@ function loadGameAssets(firstGame)
|
|||
videoPlay(VIDEO_ATTRACT)
|
||||
videoSeek(VIDEO_ATTRACT, GAME_LIST[GAME_SELECTED].ATTRACT_START)
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
GAME_SELECTED = GAME_SELECTED - 1
|
||||
if GAME_SELECTED < 1 then
|
||||
|
@ -137,6 +161,11 @@ function onInputPressed(what)
|
|||
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
|
||||
-- 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])
|
||||
end
|
||||
|
||||
|
@ -147,6 +176,8 @@ function onOverlayUpdate()
|
|||
|
||||
local x = 0
|
||||
local y = 0
|
||||
local c = 0
|
||||
local t = 0
|
||||
|
||||
overlayClear()
|
||||
|
||||
|
@ -169,9 +200,27 @@ function onOverlayUpdate()
|
|||
-- 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)
|
||||
y = y + spriteGetHeight(handle) + 1
|
||||
end
|
||||
y = y + TEXT_LINE_HEIGHT + 1
|
||||
c = c + 1
|
||||
end
|
||||
end
|
||||
t = t + 1
|
||||
end
|
||||
|
||||
return(OVERLAY_UPDATED)
|
||||
|
@ -252,8 +301,24 @@ 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 = {}
|
||||
|
||||
-- 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
|
||||
GAME_SELECTED = 1
|
||||
loadGameAssets(true)
|
||||
|
|
|
@ -65,7 +65,7 @@ function doBuild() {
|
|||
|
||||
echo "Compressing ${TARGET}..."
|
||||
${CROSS}-strip "${TARGET}"
|
||||
upx -q -9 "${TARGET}"
|
||||
upx -9 "${TARGET}"
|
||||
|
||||
popd
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "Framework_singe.h"
|
||||
#include "controls_cfg.h"
|
||||
#include "Menu_singe.h"
|
||||
#include "FreeSansBold_ttf.h"
|
||||
#include "menuBackground_mkv.h"
|
||||
#include "Manual_pdf.h"
|
||||
|
||||
|
|
26
singe/main.c
26
singe/main.c
|
@ -440,7 +440,7 @@ bool extractFile(char *filename, unsigned char *data, int32_t length) {
|
|||
if (!out) utilDie("Unable to create %s", filename);
|
||||
fwrite(data, length, 1, out);
|
||||
fclose(out);
|
||||
utilSay("Created File: %s", filename);
|
||||
utilSay(">>> Created File: %s", filename);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -652,6 +652,7 @@ void queueScript(ConfigT *conf) {
|
|||
__attribute__((noreturn))
|
||||
void showUsage(char *name, char *message) {
|
||||
char *temp = NULL;
|
||||
char *data = NULL;
|
||||
bool created = false;
|
||||
int32_t result = 0;
|
||||
|
||||
|
@ -700,17 +701,36 @@ void showUsage(char *name, char *message) {
|
|||
free(temp);
|
||||
// Singe/Manual.pdf
|
||||
temp = utilCreateString("Singe%Manual.pdf", utilGetPathSeparator());
|
||||
created |= extractFile(temp, Manual_pdf, Manual_pdf_len);
|
||||
//created |= extractFile(temp, Manual_pdf, Manual_pdf_len);
|
||||
free(temp);
|
||||
// Singe/Menu.singe
|
||||
temp = utilCreateString("Singe%cMenu.singe", utilGetPathSeparator());
|
||||
created |= extractFile(temp, Menu_singe, Menu_singe_len);
|
||||
free(temp);
|
||||
// Singe/FreeSansBold.ttf
|
||||
temp = utilCreateString("Singe%cFreeSansBold.ttf", utilGetPathSeparator());
|
||||
created |= extractFile(temp, FreeSansBold_ttf, FreeSansBold_ttf_len);
|
||||
free(temp);
|
||||
// Singe/menuBackground.mkv
|
||||
temp = utilCreateString("Singe%cmenuBackground.mkv", utilGetPathSeparator());
|
||||
created |= extractFile(temp, menuBackground_mkv, menuBackground_mkv_len);
|
||||
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;
|
||||
|
||||
if (created) utilSay("");
|
||||
|
@ -741,9 +761,9 @@ int main(int argc, char *argv[]) {
|
|||
while (_scriptQueue) {
|
||||
q = _scriptQueue;
|
||||
conf = (ConfigT *)&q->conf;
|
||||
LL_DELETE(_scriptQueue, q);
|
||||
launcher(exeName, conf);
|
||||
destroyConf(&conf);
|
||||
LL_DELETE(_scriptQueue, q);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -589,6 +589,9 @@ createEmbeddedBinary controls.cfg controls_cfg.h CONTROLS_CFG_H
|
|||
# === Singe Menu App ===
|
||||
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 ===
|
||||
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
|
||||
|
|
|
@ -280,6 +280,7 @@ int32_t apiScriptPush(lua_State *L);
|
|||
|
||||
int32_t apiSingeDisablePauseKey(lua_State *L);
|
||||
int32_t apiSingeEnablePauseKey(lua_State *L);
|
||||
int32_t apiSingeGetDataPath(lua_State *L);
|
||||
int32_t apiSingeGetPauseFlag(lua_State *L);
|
||||
int32_t apiSingeGetScriptPath(lua_State *L);
|
||||
int32_t apiSingeSetGameName(lua_State *L);
|
||||
|
@ -2131,9 +2132,14 @@ int32_t apiVideoLoad(lua_State *L) {
|
|||
name = lua_tostring(L, 1);
|
||||
// Create data directory based on video path.
|
||||
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));
|
||||
if (!video) luaDie(L, "videoLoad", "Unable to allocate new video.");
|
||||
// Load this video.
|
||||
video->handle = videoLoad((char *)name, data, false, _global.renderer);
|
||||
if (video->handle < 0) luaDie(L, "videoLoad", "Failed to load video: %s", name);
|
||||
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) {
|
||||
// Adds the name of the singe game to the window's title bar.
|
||||
// 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) {
|
||||
// Returns the path to the singe script.
|
||||
// e.g. lua code,
|
||||
//
|
||||
// sGameDirectory = singeGetScriptPath()
|
||||
//
|
||||
|
||||
luaTrace(L, "singeSetScriptPath", "%s", _global.conf.scriptFile);
|
||||
luaTrace(L, "singeGetScriptPath", "%s", _global.conf.scriptFile);
|
||||
lua_pushstring(L, _global.conf.scriptFile);
|
||||
return 1;
|
||||
}
|
||||
|
@ -3313,7 +3320,6 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, ConfigT *conf) {
|
|||
|
||||
// Set up globals
|
||||
memset(&_global, 0, sizeof(GlobalT));
|
||||
memcpy(&_global.conf, conf, sizeof(ConfigT));
|
||||
_global.colorForeground.r = 255;
|
||||
_global.colorForeground.g = 255;
|
||||
_global.colorForeground.b = 255;
|
||||
|
@ -3331,6 +3337,14 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, ConfigT *conf) {
|
|||
_global.discStopped = 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_LEFT].name = "INPUT_LEFT";
|
||||
_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, "singeEnablePauseKey", apiSingeEnablePauseKey);
|
||||
lua_register(_global.luaContext, "singeGetDataPath", apiSingeGetDataPath);
|
||||
lua_register(_global.luaContext, "singeGetHeight", apiDaphneGetHeight);
|
||||
lua_register(_global.luaContext, "singeGetPauseFlag", apiSingeGetPauseFlag);
|
||||
lua_register(_global.luaContext, "singeGetScriptPath", apiSingeGetScriptPath);
|
||||
|
|
|
@ -156,6 +156,7 @@ HEADERS += \
|
|||
indexing.h \
|
||||
controls_cfg.h \
|
||||
Menu_singe.h \
|
||||
FreeSansBold_ttf.h \
|
||||
menuBackground_mkv.h \
|
||||
Manual_pdf.h
|
||||
|
||||
|
|
14
singe/util.c
14
singe/util.c
|
@ -46,6 +46,20 @@ static bool _consoleEnabled = true;
|
|||
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, ...) {
|
||||
va_list args;
|
||||
char *string;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#define UTIL_PATH_MAX 1024
|
||||
|
||||
|
||||
bool utilChMod(const char *path, const mode_t mode);
|
||||
char *utilCreateString(char *format, ...);
|
||||
char *utilCreateStringVArgs(char *format, va_list args);
|
||||
void utilDie(char *fmt, ...);
|
||||
|
|
Loading…
Add table
Reference in a new issue