Skip to content

Commit

Permalink
v1.01 wip
Browse files Browse the repository at this point in the history
new: PZX support
fix: ZXDB picking wrong titles after "*2">"*II*" patch  (dan dare 3 vs 2)
fix: loading settings from ini
fix: bug that prevented window from being closed while 128 menu was in HALT state (see: cursor right key)
chg: switch AY core back to floooh's
chg: display zxdb version within About dialog
chg: bumped up zxdb to latest
chg: moved titlebar logic to a separate file
fix: added U+011A Ě, U+011B ě czech glyphs
fix: window was being prevented from being closed by SCR viewer

fix: alt-enter not going fullscreen on linux/osx (3rd_tigr)
fix: allow ini fields to be read in any order
fix: local browser setting was preventing ZXDB browser to show up in fresh restarts
  • Loading branch information
r-lyeh committed Jul 14, 2024
1 parent 4f67871 commit 975be30
Show file tree
Hide file tree
Showing 16 changed files with 236 additions and 62 deletions.
9 changes: 5 additions & 4 deletions MAKE.bat
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ if "%1"=="" (
)

if "%1"=="-h" (
echo make [dbg^|dev^|opt^|rel] [compiler-flags]
echo make [deb^|dev^|opt^|rel] [compiler-flags]
exit /b
)

Expand Down Expand Up @@ -124,13 +124,14 @@ if "%1"=="dev" (
src\res\embed Spectral.exe src\res\zxdb\Spectral.db.gz
src\res\embed Spectral.exe @SpectralEmBeDdEd

tasklist /fi "ImageName eq remedybg.exe" 2>NUL | find /I "exe">NUL || (where /q remedybg.exe && start remedybg -q -g Spectral.exe)

exit /b
)

if "%1"=="dbg" (
if "%1"=="deb" (
call make dev /fsanitize=address %ALL_FROM_2ND% || goto error

tasklist /fi "ImageName eq remedybg.exe" 2>NUL | find /I "exe">NUL || (where /q remedybg.exe && start remedybg -q -g Spectral.exe)

exit /b
)

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Code is highly experimental and prone to change in the future. I will keep alter
- [x] Kempston mouse. <!-- @todo: AMX mouse.-->
- [x] Kempston/Fuller/Cursor/Sinclair joysticks.
- [x] RF/CRT experience (not physically accurate though).
- [x] TAP/TZX/CSW tapes. Z80/SNA snaps. ROM/IF2 roms. <!-- @todo: tzx info on window title -->
- [x] TAP/TZX/PZX/CSW tapes. Z80/SNA snaps. ROM/IF2 roms. <!-- @todo: tzx info on window title -->
- [x] DSK/EDSK/TRD/SCL/FDI/MGT/IMG/HOBETA disks.
- [x] SCR/PNG screenshots. <!-- @todo: ulaplus screenshots. video recording -->
- [x] ZIP/RAR/GZ archives.
Expand Down
1 change: 1 addition & 0 deletions src/3rd.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "3rd_tigrobjc.h"
#include "3rd_tigrmousecursor.h"
#include "3rd_tigrdragndrop.h"
#include "3rd_tigrtitle.h"
#undef border
#undef run

Expand Down
38 changes: 38 additions & 0 deletions src/3rd_tigr.h
Original file line number Diff line number Diff line change
Expand Up @@ -4042,6 +4042,11 @@ void _tigrOnCocoaEvent(id event, id window) {
}
}

#if 1 // @r-lyeh
if( win->keys[TK_ALT] && win->keys[TK_RETURN] && !win->prev[TK_RETURN] )
objc_msgSend_void_id(window, sel("toggleFullScreen:"), window);
#endif

// Pass through cmd+key
if (win->keys[TK_LWIN]) {
break;
Expand Down Expand Up @@ -5283,6 +5288,32 @@ static void tigrInterpretChar(TigrInternal* win, Window root, unsigned int keyco
}
}

#if 1 // @r-lyeh
#ifndef _NET_WM_STATE_TOGGLE
#define _NET_WM_STATE_TOGGLE 2
#endif
static int tigrToggleFullscreen(TigrInternal* win)
{
XEvent xev;
long evmask = SubstructureRedirectMask | SubstructureNotifyMask;

xev.type = ClientMessage;
xev.xclient.window = win->win;
xev.xclient.message_type = XInternAtom(dpy, "_NET_WM_STATE", False); //_NET_WM_STATE;
xev.xclient.format = 32;
xev.xclient.data.l[0] = _NET_WM_STATE_TOGGLE; /* action */
xev.xclient.data.l[1] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); /* first property to toggle */
xev.xclient.data.l[2] = 0; /* no second property to toggle */
xev.xclient.data.l[3] = 1; /* source indication: application */
xev.xclient.data.l[4] = 0; /* unused */

if(!XSendEvent(win->dpy, DefaultRootWindow(win->dpy), 0, evmask, &xev)) {
return -1;
}
return 0;
}
#endif

static void tigrProcessInput(TigrInternal* win, int winWidth, int winHeight) {
{
Window focused;
Expand Down Expand Up @@ -5348,6 +5379,13 @@ static void tigrProcessInput(TigrInternal* win, int winWidth, int winHeight) {
}
memcpy(prevKeys, keys, 32);

#if 1 // @r-lyeh
if( win->keys[TK_LALT] || win->keys[TK_RALT] )
if( win->keys[TK_RETURN] && !win->prev[TK_RETURN] ) {
tigrToggleFullscreen(win);
}
#endif

XEvent event;
while (XCheckTypedWindowEvent(win->dpy, win->win, ClientMessage, &event)) {
if (event.xclient.data.l[0] == wmDeleteMessage) {
Expand Down
20 changes: 20 additions & 0 deletions src/3rd_tigrtitle.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
char* tigrTitle(Tigr *win, const char *title) {
static char copy[128] = {0};
if( title ) {
#ifdef __APPLE__

#elif defined _WIN32
SetWindowTextA((HWND)(win->handle), title);
#else
XTextProperty prop;
int result = Xutf8TextListToTextProperty(dpy, (char**)&title, 1, XUTF8StringStyle, &prop);
if (result == Success) {
Atom wmName = XInternAtom(dpy, "_NET_WM_NAME", 0);
XSetTextProperty(tigrInternal(win)->dpy, tigrInternal(win)->win, &prop, wmName);
XFree(prop.value);
}
#endif
snprintf(copy, 128, "%s", title);
}
return copy;
}
32 changes: 25 additions & 7 deletions src/app.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
// key2/3, +2a/+3, fdc, dsk, autotape, gui, KL modes, load "" code, +3 fdc sounds, +3 speedlock, issue 2/3,
// pentagon, trdos, trdos (boot), translate game menus, 50/60 hz, game2exe,
// zxdb, custom tiny zxdb fmt, embedded zxdb, zxdb cache, zxdb download on demand, zxdb gallery
// ay player,
// ay player, pzx,
// glue sequential tzx/taps in zips (side A) -> side 1 etc)
// sequential tzx/taps/dsks do not reset model

#define SPECTRAL "v1.0"
#define SPECTRAL "v1.01 wip"

#define README \
"Spectral can be configured with a mouse.\n\n" \
Expand Down Expand Up @@ -58,6 +58,11 @@
// [ ] db interface (F2 to rename)
// on hover: show animated state if exists. show loading screen otherwise.
// [ ] embed torrent server/client to mirror the WOS/ZXDB/NVG/Archive.org distros
// http://www.kristenwidman.com/blog/33/how-to-write-a-bittorrent-client-part-1/
// https://wiki.theory.org/BitTorrentSpecification
// http://bittorrent.org/beps/bep_0003.html
// https://github.com/willemt/yabtorrent
// https://github.com/jech/dht
//
// idea: when stop-block is off
// - turn autoplay=off
Expand Down Expand Up @@ -167,8 +172,11 @@ int screenshot(const char *filename) {
int load_config() {
int errors = 0;
if( !ZX_PLAYER ) for( FILE *fp = fopen(".Spectral/Spectral.ini", "rt"); fp; fclose(fp), fp = 0 ) {
#define INI_LOAD(opt) errors += fscanf(fp, "%*[^=]=%d\n", &opt) > 1;
INI_OPTIONS(INI_LOAD)
while( !feof(fp) ) {
int tmp; char buf[128]; errors += fscanf(fp, "%[^=]=%d ", buf, &tmp) > 1;
#define INI_LOAD(opt) if( strcmpi(buf, #opt) == 0 ) opt = tmp; else
INI_OPTIONS(INI_LOAD) {}
}
}
return !errors;
}
Expand Down Expand Up @@ -216,6 +224,11 @@ void input() {
if(window_pressed(app, TK_ALT)) ZXKey(ZX_CTRL);
}

// z80_opdone() returns 0 indefinitely while z80 is in halt state, often seen during BASIC sessions.
// hack: force a benign keypress when user wants to close window; so z80_opdone() returns 1 hopefully.
if( !window_alive(app) ) {
static int flip = 0; if( flip ^= 1 ) ZXKey(ZX_SHIFT);
}

// prepare command keys
if( window_trigger(app, TK_ESCAPE) ) cmdkey = 'ESC';
Expand Down Expand Up @@ -255,8 +268,9 @@ void help() {
char *help = va(
"Spectral " SPECTRAL " (Public Domain).\n"
"https://github.com/r-lyeh/Spectral\n\n"
"Library: %d games found (%d%%)\n\n"
README "\n", numgames, 100 - (numerr * 100 / (total + !total)));
"ZXDB %s: %d entries\n"
"Local Library: %d games found (%d%%)\n\n"
README "\n", ZXDB_VERSION, zxdb_count(), numgames, 100 - (numerr * 100 / (total + !total)));
(alert)("Spectral " SPECTRAL, help);
}

Expand Down Expand Up @@ -1123,7 +1137,11 @@ if( do_runahead == 0 ) {
active *= window_alive(app);

#if NEWCORE
} while( !z80_opdone(&cpu) );
// ensure there is no pending opcode before exiting main loop: spectral.sav would be corrupt otherwise.
// also, do not loop indefinitely on invalid DI+HALT combo, which we use in our .SCR viewer.
// update: moved logic that bypasses z80_opdone(&cpu) in HALT state. rewritten as a forced/benign ZX_Key(ZX_SHIFT) operation; see: input() function.
// printf("%d %018llx %d %d %d\n", z80_opdone(&cpu), cpu.pins, cpu.step, IFF1(cpu), IFF2(cpu));
} while( z80_opdone(&cpu) ? 0 : 1 ); // (cpu.pins & Z80_HALT) && (cpu.step == 1) ? 0 : 1 ); // (cpu.pins & Z80_HALT) && (cpu.step == 1 || cpu.step==_z80_special_optable[4] || cpu.step==_z80_special_optable[5]) ? 0 : 1 );
#endif

} while( window_alive(app) );
Expand Down
Binary file modified src/res/zxdb/Spectral.db.gz
Binary file not shown.
1 change: 1 addition & 0 deletions src/res/zxdb/ZXDB_version.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#define ZXDB_VERSION "Version 1.0.197"
3 changes: 2 additions & 1 deletion src/res/zxdb/make.bat
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
rem checks. compile if needed
where /q python.exe || (echo cannot find python.exe in path && exit /b)
where /q sqlite3.exe || (cl sqlite3.c shell.c /MT || exit /b)
where /q zxdb2txt.exe || (cl zxdb2txt.c sqlite3.c /MT /Ox /Oy || exit /b)
where /q zxdb2txt.exe || (cl zxdb2txt.c sqlite3.c -DDEV=0 /MT /Ox /Oy || exit /b)
if exist *.obj del *.obj
if exist Z*.sql* del Z*.sql*

rem clone
rd /q /s ZXDB >nul 2>nul
git clone --depth 1 https://github.com/ZXDB/ZXDB && ^
pushd ZXDB && (git log --oneline --pretty="#define ZXDB_VERSION \"%%s\"" ZXDB_mysql.sql.zip > ..\ZXDB_version.h) && popd && ^
python -m zipfile -e ZXDB\ZXDB_mysql.sql.zip . && ^
python ZXDB\scripts\ZXDB_to_SQLite.py && ^
rd /q /s ZXDB >nul 2>nul
Expand Down
5 changes: 3 additions & 2 deletions src/sys_ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ CUSTOM_GLYPHS,
{0x007c,0x1818181818181800},{0x007d,0x3018180e18183000},{0x007e,0x316b460000000000},
#endif
{0x00a0,0x0000000000000000},
#if 1 // USE_BESCII. added U+010C Č, U+010D č
#if 1 // USE_BESCII. added U+010C Č, U+010D č, U+011A Ě, U+011B ě
{0x00a1,0x0018001818181818},{0x00a2,0x00083e6868683e08},{0x00a3,0x3c6660f86060fe00},{0x00a4,0x00423c66663c4200},
{0x00a5,0x66663c183c181800},{0x00a6,0x1818180000181818},{0x00a7,0x3c607c663e063c00},{0x00a8,0x2400000000000000},
{0x00a9,0x38449aa29a443800},{0x00aa,0x060a060000000000},{0x00ab,0x0000366cd86c3600},{0x00ac,0x0000007e06060000},
Expand All @@ -384,7 +384,8 @@ CUSTOM_GLYPHS,
{0x00f9,0x1008666666663e00},{0x00fa,0x0810666666663e00},{0x00fb,0x1028666666663e00},{0x00fc,0x2400666666663e00},
{0x00fd,0x08106666663e063c},{0x00fe,0x0000303c363c3000},{0x00ff,0x24006666663e063c},

{0x010c,0x3c3c666060663c00},{0x010d,0x28103c6660663c00}, // @fixme
{0x010c,0x3c3c666060663c00},{0x010d,0x28103c6660663c00}, // @fixme: replace with better glyphs
{0x011a,0x3c7e607c60607e00},{0x011b,0x28103c667e603c00}, // @fixme: replace with better glyphs

{0x0131,0x0000181818180c00},
{0x0141,0x606078e060607e00},{0x0142,0x18181c3818180c00},{0x0152,0x7ed8d8dcd8d87e00},{0x0153,0x00006c929e906c00},
Expand Down
25 changes: 1 addition & 24 deletions src/sys_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#define window_pressed(win, keycode) (!!(tigrKeyDown(win, keycode) || tigrKeyHeld(win, keycode)))
#define window_trigger(win, keycode) (!!tigrKeyDown(win, keycode))
void window_override_icons();
char* window_title(window *win, const char *title);
#define window_title(win, title) tigrTitle(win,title)


int window_keyrepeat(window *app, unsigned char vk) {
Expand Down Expand Up @@ -82,28 +82,6 @@ char* prompt(const char *title, const char *body, const char *defaults ) {

#endif


char* window_title(window *win, const char *title) {
static char copy[128] = {0};
if( title ) {
#ifdef __APPLE__

#elif defined _WIN32
SetWindowTextA((HWND)(((Tigr*)win)->handle), title);
#else
XTextProperty prop;
int result = Xutf8TextListToTextProperty(dpy, (char**)&title, 1, XUTF8StringStyle, &prop);
if (result == Success) {
Atom wmName = XInternAtom(dpy, "_NET_WM_NAME", 0);
XSetTextProperty(tigrInternal(win)->dpy, tigrInternal(win)->win, &prop, wmName);
XFree(prop.value);
}
#endif
snprintf(copy, 128, "%s", title);
}
return copy;
}

int (alert)(const char *title, const char *body) {
#ifdef _WIN32
MessageBoxA(0, body, title, MB_OK);
Expand All @@ -126,4 +104,3 @@ void die(const char *msg) {
#endif
exit(-1);
}

32 changes: 22 additions & 10 deletions src/zx.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// known issues:

// starts in turborom mode ??? see battle valley
// woodster "for me any tstate count is relative to /INT going low and the ULA starts contention on 14335 tstates"

// runahead
// - bonanza bros.dsk
Expand All @@ -14,11 +14,15 @@
// - crash by pressing F12 after main window is displayed

// media
// scl not being saved on exit (borderbreak)
// - scl not being saved on exit (borderbreak)

// ini
// - add setting: games folder

// pzx
// - load_flowcontrol\HollywoodPoker.pzx
// - load_gdb\Book Of The Dead - Part 1 (CRL).pzx

// gui
// - utf8 no czech/hungarian/slovak: ČĎĚŇŘŠŤŽ čďěňřšťž Ýý ÔôŐő ŰűŮů Ĺĺ Ľľ Ŕŕ (see tabs: d,k,m,p,r,t)
// - no selector (pok)
Expand All @@ -33,13 +37,12 @@
// - infos get lost between different .sav sessions
// - overlay: no scroll (mouse/key), no zooming
// - JACKNIP.TAP ; difficult to get it right without hyphenation. best we could do for now is search JACKNIP%
// - reset does not clear zxdb
// - instructions: wordwrap, utf8 bom
// - no mp3s
// - no ays
// - maps: battle valley

// zxplayer
// embedded zxplayer
// - cant load zip/rar files because FILE *fp is not pointing at seek pos

// trdos+L mode
Expand Down Expand Up @@ -119,7 +122,7 @@ int ZX_FREQ;
int ZX_RF = !DEV;
int ZX_CRT = !DEV;
int ZX; // 16, 48, 128, 200 (+2), 210 (+2A), 300 (+3)
int ZX_AY = 1; // 0: no, 1: ayumi, 2: flooh's, 3: both (ZX_AY is a mask)
int ZX_AY = 2; // 0: no, 1: ayumi, 2: flooh's, 3: both (ZX_AY is a mask)
int ZX_TURBOROM = 0; // 0: no, 1: patch rom so loading standard tape blocks is faster (see: .tap files)
int ZX_JOYSTICK = 3; // 0: no, 1: sinclair1, 2: sinclair2, 3: cursor/fuller/kempston/protek, 4..: other mappings
int ZX_AUTOPLAY = 0; // yes/no: auto-plays tapes based on #FE port reads
Expand Down Expand Up @@ -162,7 +165,7 @@ int ZX_ALTROMS = 0; // 0:(no, original), 1:(yes, custom)
X(ZX_FPS) \
X(ZX_AUTOLOCALE) \
X(ZX_FASTTAPE) \
X(ZX_BROWSER) \
/*X(ZX_BROWSER)*/ \
X(ZX_ALTROMS)

void logport(word port, byte value, int is_out);
Expand Down Expand Up @@ -304,10 +307,10 @@ int medias;
void media_reset() { medias = 0; for(int i=0;i<16;++i) media[i].bin = realloc(media[i].bin, media[i].len = media[i].pos = 0); }
void media_mount(const byte *bin, int len) { media[medias].bin = memcpy(realloc(media[medias].bin, media[medias].len = len), bin, len), media[medias].pos = 0, medias++; }

enum { ALL_FILES = 0, GAMES_ONLY = 6*4, TAPES_AND_DISKS_ONLY = 9*4, DISKS_ONLY = 12*4 };
enum { ALL_FILES = 0, GAMES_ONLY = 6*4, TAPES_AND_DISKS_ONLY = 9*4, DISKS_ONLY = 13*4 };
int file_is_supported(const char *filename, int skip) {
const char *ext = strrchr(filename ? filename : "", '.');
return ext && strstri(skip+".gz .zip.rar.pok.scr.ay .rom.sna.z80.tap.tzx.csw.dsk.img.mgt.trd.fdi.scl.$b.$c.$d.", ext);
return ext && strstri(skip+".gz .zip.rar.pok.scr.ay .rom.sna.z80.tap.tzx.pzx.csw.dsk.img.mgt.trd.fdi.scl.$b.$c.$d.", ext);
}

#include "zx_ay.h"
Expand Down Expand Up @@ -358,6 +361,15 @@ int loadbin_(const byte *ptr, int size, int preloader) {
}

// tapes first
if( pzx_load(ptr, (int)size) ) {
int slots[] = { [1]=0,[3]=1,[8]=2,[12]=3,[13]=4,[18]=5 };
int is_bin = tape_type == 3, choose = slots[ZX/16] + 6 * is_bin;
if(preloader) preload_snap(bins[choose], lens[choose]);
//if(tape_has_turbo) rom_restore(); // rom_restore(), rom_patch(tape_has_turbo ? 0 : do_rompatch);
ZX_AUTOSTOP = tape_num_stops > 1 ? 0 : size > 65535;
//alert(va("numstops:%d", tape_num_stops));
return 2;
}
if( tzx_load(ptr, (int)size) ) {
int slots[] = { [1]=0,[3]=1,[8]=2,[12]=3,[13]=4,[18]=5 };
int is_bin = tape_type == 3, choose = slots[ZX/16] + 6 * is_bin;
Expand Down Expand Up @@ -460,7 +472,7 @@ void *zip_read(const char *filename, size_t *size) {
rar_free(r,data);

// keeping glueing tapes
if(strstr(rar_name(r,i),".tap") || strstr(rar_name(r,i),".tzx")) {
if(strstr(rar_name(r,i),".tap") || strstr(rar_name(r,i),".tzx") || strstr(rar_name(r,i),".pzx")) {
continue;
}
break;
Expand Down Expand Up @@ -504,7 +516,7 @@ void *zip_read(const char *filename, size_t *size) {
free(data);

// keeping glueing tapes
if(strstr(zip_name(z,i),".tap") || strstr(zip_name(z,i),".tzx")) {
if(strstr(zip_name(z,i),".tap") || strstr(zip_name(z,i),".tzx") || strstr(zip_name(z,i),".pzx")) {
continue;
}
break;
Expand Down
Loading

0 comments on commit 975be30

Please sign in to comment.