dmagnetic-0.37/0000755000175000017500000000000014430741336012121 5ustar useruserdmagnetic-0.37/Makefile0000644000175000017500000001031214430741336013556 0ustar useruser#!/usr/bin/make -f # #Copyright 2023, dettus@dettus.net # #Redistribution and use in source and binary forms, with or without modification, #are permitted provided that the following conditions are met: # #1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # #2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # #THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED #WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE #DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE #FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL #DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR #SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER #CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, #OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PREFIX?=/usr/local DESTDIR?=/usr/local MYPREFIX=$(DESTDIR) INSTALLBIN=$(MYPREFIX)/bin INSTALLSHARE=$(MYPREFIX)/share/games INSTALLMAN=$(MYPREFIX)/share/man CC?=gcc AR?=ar #CFLAGS=-g -O0 CFLAGS?=-O3 CFLAGS+=-Wall #CFLAGS+=-Werror #CPPFLAGS+=-DEXPERIMENTAL_SAVEGAME_SLOTS PROJ_HOME=./ INCFLAGS= -I$(PROJ_HOME)src/frontends/default/ -I$(PROJ_HOME)src/frontends -I$(PROJ_HOME)src/loader -I$(PROJ_HOME)src/libdmagnetic -I$(PROJ_HOME)src/libdmagnetic/vm68k -I$(PROJ_HOME)src/libdmagnetic/linea -I$(PROJ_HOME)src/libdmagnetic/include OBJDIR=$(PROJ_HOME)obj/ LINK=$(CC) LDFLAGS+="-L"$(OBJDIR) SOURCES_LOADER= \ src/loader/loader_appleii.c \ src/loader/loader_archimedes.c \ src/loader/loader_atarixl.c \ src/loader/loader_common.c \ src/loader/loader_d64.c \ src/loader/loader_dsk.c \ src/loader/loader_msdos.c \ src/loader/loader_mw.c \ src/loader/maggfxloader.c SOURCES_LIBDMAGNETIC= \ src/libdmagnetic/dMagnetic.c SOURCES_LINEA= \ src/libdmagnetic/linea/gfxloader.c \ src/libdmagnetic/linea/linea.c SOURCES_VM68K= \ src/libdmagnetic/vm68k/vm68k.c \ src/libdmagnetic/vm68k/vm68k_decode.c \ src/libdmagnetic/vm68k/vm68k_loadstore.c SOURCES_FRONTEND= \ src/frontends/default/configuration.c \ src/frontends/default/default_callbacks.c \ src/frontends/default/default_palette.c \ src/frontends/default/default_render.c \ src/frontends/default/helpscreens.c \ src/frontends/default/main.c OBJ_LOADER=${SOURCES_LOADER:.c=.o} OBJ_ENGINE=${SOURCES_LIBDMAGNETIC:.c=.o} OBJ_LINEA=${SOURCES_LINEA:.c=.o} OBJ_VM68K=${SOURCES_VM68K:.c=.o} OBJ_DEFAULT=${SOURCES_FRONTEND:.c=.o} all: dMagnetic dMagnetic.ini # strip dMagnetic clean: rm -rf dMagnetic dMagnetic.ini libdmagnetic.a rm -rf $(OBJ_LOADER) rm -rf $(OBJ_ENGINE) rm -rf $(OBJ_LINEA) rm -rf $(OBJ_VM68K) rm -rf $(OBJ_DEFAULT) install: all dMagnetic.6 dMagneticini.5 install -m 755 -d $(INSTALLBIN) install -m 755 -d $(INSTALLMAN)/man5/ install -m 755 -d $(INSTALLMAN)/man6/ install -m 755 -d $(INSTALLSHARE)/dMagnetic/ install -m 755 -s dMagnetic $(INSTALLBIN)/dMagnetic install -m 644 dMagneticini.5 $(INSTALLMAN)/man5/dMagneticini.5 install -m 644 dMagnetic.6 $(INSTALLMAN)/man6/dMagnetic.6 install -m 644 README.txt $(INSTALLSHARE)/dMagnetic/README.txt install -m 644 LICENSE.txt $(INSTALLSHARE)/dMagnetic/LICENSE.txt cp dMagnetic.ini $(INSTALLSHARE)/dMagnetic/ dMagnetic: $(OBJ_LOADER) $(OBJ_DEFAULT) $(OBJ_ENGINE) $(OBJ_LINEA) $(OBJ_VM68K) $(LINK) $(LDFLAGS) -o $@ $(OBJ_LOADER) $(OBJ_ENGINE) $(OBJ_LINEA) $(OBJ_VM68K) $(OBJ_DEFAULT) dMagnetic.ini: dMagnetic ./dMagnetic -helpini >dMagnetic.ini .c.o: $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_EXTRA) $(INCFLAGS) -c -o $@ $< # initial inkling for a dmagnetic backend library libdmagnetic.a: $(OBJ_ENGINE) $(OBJ_LINEA) $(OBJ_VM68K) $(AR) rs libdmagnetic.a $(OBJ_ENGINE) $(OBJ_LINEA) $(OBJ_VM68K) ## in case some post-compilation checks are needed ## if not, please comment out this line include ./checks.mk dmagnetic-0.37/README.txt0000644000175000017500000001755314430741336013632 0ustar useruser*** dMagnetic *** Use at your own risk *** Copyright 2023 by dettus@dettus.net *************************************** Welcome, brave adventurer. This is Version 0.36. If you are interested in playing "The Pawn", just follow these simple steps. STEP 1: BUILD just run % make all STEP 2: CHECK BASE FUNCTIONALITY run % ./dMagnetic -mag testcode/minitest.mag you should see a colourful X and the request to press enter. Alternatively, on OpenBSD, NetBSD or FreeBSD you could simply run % make check On Linux, the command would be % make SHA256_CMD=sha256sum check STEP 3: GET THE BINARIES Get the files pawn.mag and pawn.gfx. For example from this excellent website https://msmemorial.if-legends.org/games.htm/pawn.php Alternatively, if you have the MS-DOS version of the games, copy them onto your harddrive. Try them out by running % ./dMagnetic -mag pawn.mag (for example) STEP 4: CONFIGURE edit dMagnetic.ini, especially the lines that start with one of ????mag= and ???gfx=, ???msdosdir= or ???tworsc=. Make sure that only one of those methods is being commented in. To see an example of the .ini file, type in % ./dMagnetic -helpini STEP 5: RUN run one of % ./dMagnetic -ini dMagnetic.ini pawn % ./dMagnetic -ini dMagnetic.ini guild % ./dMagnetic -ini dMagnetic.ini jinxter % ./dMagnetic -ini dMagnetic.ini corruption % ./dMagnetic -ini dMagnetic.ini fish % ./dMagnetic -ini dMagnetic.ini myth % ./dMagnetic -ini dMagnetic.ini wonderland Remember that for some games you have to type in GRAPHICS before you see them. Alternatively, you can select the .mag files like this: % ./dMagnetic -ini dMagnetic.ini -mag /usr/local/share/games/pawn.mag TO SEE GRAPHICS IN WONDERLAND OR ANY GAME FROM THE MAGNETIC SCROLLS COLLECTION, you have to type in 'GRAPHICS'. To see the EGA version of those pictures, run % ./dMagnetic -ega -ini dMagnetic.ini wonderland To play using the binaries from the MS DOS release, simply run % ./dMagnetic -ini dMagnetic.ini -msdosdir /C/GAMES/THEPAWN/ To play using the resource files from the Magnetic Scrolls Collection or Wonderland, the parameter -tworsc can be used to provide the location of the most important resource file % ./dMagnetic -ini dMagnetic.ini -tworsc /C/GAMES/WONDER/TWO.RSC % ./dMagnetic -ini dMagnetic.ini -tworsc /C/GAMES/MSC/CTWO.RSC % ./dMagnetic -ini dMagnetic.ini -tworsc /C/GAMES/MSC/FTWO.RSC % ./dMagnetic -ini dMagnetic.ini -tworsc /C/GAMES/MSC/GTWO.RSC If you wish to play using .d64 images from the Commodore 64 (C64) releases, you have to provide both sides of the floppy disks as filenames: % ./dMagnetic -ini dMagnetic.ini -d64 pawn1.d64,pawn2.d64 For Amstrad CPC/Schneider CPC enthusiasts, there is the possibility to use image files in the DSK format. % ./dMagnetic -ini dMagnetic.ini -amstradcpc DISK1.DSK,DISK2.DSK For the Spectrum+3/Spectrum128 releases, the commandline would be this one: % ./dMagnetic -ini dMagnetic.ini -spectrum DISKIMAGE.DSK For the Apple ][ releases, the .nib,.2mg and .woz format can be used: % ./dMagnetic -ini dMagnetic.ini -appleii PAWN.NIB % ./dMagnetic -ini dMagnetic.ini -appleii GUILD.2MG % ./dMagnetic -ini dMagnetic.ini -appleii CorrA.woz,CorrB.woz,CorrC.woz STEP 6: GRAPHICS You can select output modes by using one of the following parameters: % ./dMagnetic -ini dMagnetic.ini pawn -vmode none -vrows 40 -vcols 120 % ./dMagnetic -ini dMagnetic.ini pawn -vmode monochrome -vrows 40 -vcols 120 % ./dMagnetic -ini dMagnetic.ini pawn -vmode monochrome_inv -vrows 40 -vcols 120 % ./dMagnetic -ini dMagnetic.ini pawn -vmode low_ansi -vrows 40 -vcols 120 % ./dMagnetic -ini dMagnetic.ini pawn -vmode low_ansi2 -vrows 40 -vcols 120 % ./dMagnetic -ini dMagnetic.ini pawn -vmode high_ansi -vrows 40 -vcols 120 % ./dMagnetic -ini dMagnetic.ini pawn -vmode high_ansi2 -vrows 40 -vcols 120 % ./dMagnetic -ini dMagnetic.ini pawn -vmode sixel -sres 1024x768 -vcols 120 % ./dMagnetic -ini dMagnetic.ini pawn -vmode utf -vrows 40 -vcols 120 The defaut mode is "low_ansi", since it works on most terminals. The mode called "high_ansi" provides the richest amount of colors, even though the graphics are slightly block-y. When playing the PC version, the high_ansi2 mode is recommended. if your terminal does not support them, please try one of the others. The sixel mode can be used in certain terminal emulators, such as mlterm, or some variants of xterm, when run with % xterm -ti vt340. The UTF mode should work on terminals that can handle the high_ansi modes as well. STEP 6: LOGGING In case you would like to retrace our steps, you can use -vlog LOGFILE.log and -vecho to be able to see what you have typed in before. This helps when trying to figure out what you did wrong, and why you have been killed. :) ------------------------------------------------------------------------------- What about GLK? I must admit that I am not the biggest fan of GLK. I only started looking into it, so the whole interaction is quite shaky. Plus, there has not been a modern X11 frontend on https://www.eblong.com/zarf/glk/ in some time. If you are interested in building it, please contact me at dettus@dettus.net. I need some help with that. Thank you in advance! ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- I am running dMagnetic in XTERM, but the colors look bleak in low_ansi mode? The reason might be that the colors for the low ansi mode have been calibrated to the ANSI rgb values. Especially the brown color (3) is vastly different in the default xterm settings. One solution could be to set the default rgb values for xterm, by adding the following lines to your $HOME/.Xresources: xterm*background: black xterm*foreground: grey xterm*color0: rgb:00/00/00 xterm*color1: rgb:aa/00/00 xterm*color2: rgb:00/aa/00 xterm*color3: rgb:aa/55/00 xterm*color4: rgb:00/00/aa xterm*color5: rgb:aa/00/aa xterm*color6: rgb:00/aa/aa xterm*color7: rgb:aa/aa/aa xterm*color8: rgb:55/55/55 xterm*color9: rgb:ff/55/55 xterm*color10: rgb:55/ff/55 xterm*color11: rgb:ff/ff/55 xterm*color12: rgb:55/55/ff xterm*color13: rgb:ff/55/ff xterm*color14: rgb:55/ff/ff xterm*color15: rgb:ff/ff/ff Afterwards, run % xrdb -merge ~/.Xresources and open a new xterm window. In this, you can play dMagnetic with proper colors. ------------------------------------------------------------------------------- ABOUT THE CHECKS ------------------------------------------------------------------------------- They are being run in the check.mk file, so that they can be easily patched out of the Makefile if they are not necessary. They require three programs: SHA256, AWK and ECHO. They way the work is by piping an input through dMagnetic, and checking the SHA256 sum of the output. Since the program to calculate a SHA256 sum differs between operating systems, I decided to make them parameterizable to the test. The default run would be the equivalent of % make SHA256_CMD=sha256 ECHO_CMD=echo AWK_CMD=awk check On Linux, the checks may have to be invoked via % make SHA256_CMD=sha256sum ECHO_CMD=echo AWK_CMD=awk check And on some BSD derivates, where awk behaves differently, it could be % make SHA256_CMD=sha256sum ECHO_CMD=echo AWK_CMD=gawk check Why awk? Because the output of SHA256 also behaves slightly different. Sometimes, if simply prints out the sum, sometimes it adds a "-" at the end. And that breaks the check. ------------------------------------------------------------------------------- FOR INTERESTED DEVELOPERS ------------------------------------------------------------------------------- If you enjoy the magic of Magnetic Scrolls, but are not satisfied with the frontend, please have a look at the src/frontends/helloworld/ directory. It contains an example. ------------------------------------------------------------------------------- Thomas Dettbarn dmagnetic-0.37/dMagnetic.60000644000175000017500000002751414430741336014114 0ustar useruser.\" Process this file with .\" groff -man -Tascii dMagnetic.6 .\" .Dd January 22nd, 2023 .Os OpenBSD .Dt dMagnetic 6 . .Sh NAME .Nm dMagnetic .Nd A Magnetic Scrolls Interpreter . . .Sh INTRODUCTION You wake up on a sunny August morning with birds singing, and the air fresh and clear. However, your joints are stiff and you have not woken up in your bedroom as you would have expected. Trying to recall what happened the night before, you manage to piece together a few brief glimpses to give the following account... .Sh SYNOPSIS . .Nm .Op Fl mag Ar MAGFILE.mag .Op Fl gfx Ar GFXFILE.gfx .br .Nm .Op Fl msdosdir Ar DIRECTORY/ .Op Fl vmode Ar high_ansi2 .br .Nm .Op Fl tworsc Ar DIRECTORY/TWO.RSC .Op Fl vmode Ar high_ansi .br .Nm .Op Fl d64 Ar DIRECTORY/FILE1.d64,DIRECTORY/FILE2.d64 .Op Fl vmode Ar sixel .br .Nm .Op Fl amstradcpc Ar DIRECTORY/FILE1.DSK,DIRECTORY/FILE2.DSK .Op Fl vmode Ar utf .br .Nm .Op Fl spectrum Ar DIRECTORY/DISKIMAGE.DSK .Op Fl vmode Ar none .br .Nm .Op Fl archimedes Ar DIRECTORY/DISKIMAGE.adf .br .Nm .Op Fl atarixl Ar DIRECTORY/FILE1.ATR,DIRECTORY/FILE2.ATR .br .Nm .Op Fl appleii Ar 1.NIB,2.2MG,3.WOZ .br .Nm .Op Fl ini Ar ini-file .Op GAME .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vrows Ar ROWS .Op Fl vcols Ar COLUMNS .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar none .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar monochrome .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar low_ansi .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar low_ansi2 .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar high_ansi .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar high_ansi2 .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar utf .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar sixel .Op Fl sres Ar 1024x768 .Op Fl sforce .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl valign Ar left .br .Nm .Op Fl savegames Ar advanced .br .Nm .Op Fl savegames Ar classic .br .Nm .Op Fl vlog Ar LOGFILE .Op Fl vecho .Nm .Op Fl help .Op Fl helpini .Op Fl nodoc .Op Fl v .Op Fl bsd .Sh DESCRIPTION .Nm is an interpreter for classic text adventure games, or interactive fiction, such as "The Pawn" and "The Guild of Thieves". The following games are being supported: .Bl -tag -width 10 .It "The Pawn" .It "The Guild of Thieves" .It "Jinxter" .It "Fish!" .It "Myth" .It "Corruption" .It "Wonderland" .El . .Sh GAME BINARIES Currently, binaries in the .mag and .gfx format from the magnetic scrolls memorial at https://msmemorial.if-legends.org/memorial.php are being supported. Once downloaded, their position has to be made available to .Nm by editing dMagnetic.ini first. .br Other possibilities are loading the games from original releases, such as .Bl -tag -width 10 .It the MS-DOS binaries .It the Magnetic Windows Resource Files .It the Commodore C64 images in the d64 format .It Amstrad CPC and Spectrum128 images in the DSK format .It Acorn Archimedes images in the ADF/ADL (aka ADFS) format .It AtariXL/Atari800 images in the ATR format .It Apple II images in the 2MG, NIB or WOZ format .El . .Sh STARTING THE GAME Once the game(s) have been downloaded, the .mag and .gfx files can be provided via .Pp .Nm -mag pawn.mag -gfx pawn.gfx .Pp Alternatively, binaries from the MSDOS version of "The Pawn" or "The Guild of Thieves" can be used. The directory, in which they are, has to be provided via .Pp .Nm -msdosdir DIRECTORY/ -vmode high_ansi .Pp It is recommended to use the .Op Fl vmode Ar high_ansi2 option, since this renders the half-tone images slightly different. .Pp A third option is to use the resource files from "Wonderland" or the magnetic scrolls collection. For this, the location of the file ending with "TWO.RSC" has to be provided. .Pp .Nm -tworsc DIRECTORY/TWO.RSC -vmode high_ansi .br .Nm -tworsc DIRECTORY/GTWO.RSC -vmode high_ansi -ega .br The names of the other resource files is being calculated from that. With the optional parameter -ega, the EGA versions of the pictures are being shown. .Pp A fourth option is to use D64 image files from the Commodore 64 releases. They come in pairs, since both sides of the floppy disk were occupied. So the parameter requires a list of them, separated by komma: .Pp .Nm -d64 DIRECTORY/FILE1.d64,DIRECTORY/FILE2.d64 -vmode sixel .Pp A fifth option is to use DSK image files from the Amstrad CPC releases. They come in pairs, since noth sides of the floppy disk were occupied. So the parameter requires a list of them, separated by komma: .Pp .Nm -amstradcpc DIRECTORY/FILE1.DSK,DIRECTORY/FILE2.DSK -vmode sixel .Pp A sixth option is to use DSK image files from the Spectrum releases. Since this platform did not have the pictures, a single floppy disk was enough: .Pp .Nm -spectrum DIRECTORY/DISKIMAGE.DSK -vmode none .Pp A seventh option is to use ADF/ADL/ADFS image files from the Acorn Archimedes releases. .Pp .Nm -archimedes DIRECTORY/DISKIMAGE.ADF .Pp An eigth option is to use ATR image files from the AtariXL/Atari800 releases. Those come in pairs. .Pp .Nm -atarixl DIRECTORY/FILE1.ATR,DIRECTORY/FILE2.ATR .Pp An ninth option is to use image files from the Apple II releases. .Nm can handle NIB, 2MG and WOZ. For this platform, up to three disks were used, so three floppy images are required. .br .Pp .Nm -appleii 1.NIB,2.NIB,3.NIB .br .Nm -appleii 1.2MG,2.2MG,3.2MG .br .Nm -appleii 1.WOZ,2.WOZ,3.WOZ .br .Pp It is also possible to edit the dMagnetic.ini file and start a specific game, by using one of .Pp .Nm -ini dMagnetic.ini pawn .br .Nm -ini dMagnetic.ini guild .br .Nm -ini dMagnetic.ini jinxter .br .Nm -ini dMagnetic.ini corruption .br .Nm -ini dMagnetic.ini fish .br .Nm -ini dMagnetic.ini myth .br .Nm -ini dMagnetic.ini wonderland .Pp When you see the prompt, the game is ready to accept your commands. For example EXAMINE CLOTHES. Or GO EAST. Or ASK KRONOS ABOUT THE WRISTBAND. .br .Sh LEAVING THE GAME Type in "QUIT". Duh! .Sh SAVING/LOADING PROGRESS The game can be saved at any type by typing SAVE, and loaded by typing LOAD. Followed by a filename. .Pp SAVE myprogress1.sav .br LOAD myprogress1.sav .Sh TEXT ALIGNMENT Changing the alignment of the output text can be done with the .ini file or the commandline. .br .Nm Fl valign Ar left .br .Nm Fl valign Ar block .br .Nm Fl valign Ar right .br .Sh GRAPHIC MODES .Nm has been developed with ANSI-consoles in mind. To select a video output that best suits your needs, please try one of the following . .Pp .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar none .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar monochrome .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar monochrome_inv .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar low_ansi .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar low_ansi2 .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar high_ansi .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar high_ansi2 .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar utf .Pp .br To change the number of rows/columns to render the images, use .Pp .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vcols Ar COLUMNS .br .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vrows Ar ROWS .Pp Sixels are a signalling mode for some terminal emulators (like mlterm, or xterm -ti vt340 , for example), that offer the possibility of drawing high resolution images. It can be selected with .Pp .Nm .Op Fl ini Ar ini-file .Op GAME .Op Fl vmode Ar sixel .Op Fl sres Ar 1024x768 .Op Fl sforce .Pp where the -sres option offers a way to scale the images. The maximum allowed width is 4096 pixels. Instead of automatically using a matching aspect ratio, the -sforce option can be used to force the resolution. .Sh GRAPHICS IN WONDERLAND AND THE MAGNETIC SCROLLS COLLECTION Before you can see the beautiful graphics in "Wonderland", or from the Magnetic Scrolls Collection, you have to type in "graphics" to enable them. .Sh COMMAND LINE OPTIONS .Op Fl bsd .br Shows the license. .Pp .Op Fl Fl help .br Shows the detailed help. .Pp .Op Fl Fl helpini .br Shows an example for a working dMagnetic.ini file. .Pp .Op Fl nodoc .br Allows playing without the original documentation. .Pp .Op Fl mag Ar MAGFILE.mag .Op Fl gfx Ar GFXFILE.gfx .br .Op Fl msdosdir Ar DIRECTORY/ .br .Op Fl tworsc Ar DIRECTORY/TWO.RSC .br .Op Fl d64 Ar DIRECTORY/FILE1.d64,DIRECTORY/FILE2.d64 .br .Op Fl amstradcpc Ar DIRECTORY/FILE1.DSK,DIRECTORY/FILE2.DSK .br .Op Fl spectrum Ar DIRECTORY/DISKIMAGES.DSK .br .Op Fl archimedes Ar DIRECTORY/DISKIMAGES.ADF .br .Op Fl atarixl Ar DIRECTORY/FILE1.ATR,DIRECTORY/FILE2.ATR .br .Op Fl appleii Ar 1.NIB,2.2MG,3.WOZ .br .Nm is a Magnetic Scrolls Interpreter. To actually play the games, their binaries have to be provided. Either in the .mag and .gfx format from https://msmemorial.if-legends.org/magnetic.php, as the name of the directory in which the original MS-DOS version can be found, the location and the name of the second resource file TWO.RSC, as D64-images from the Commodore 64 releases, as DSK-images from the Amstrad/Schneider CPC releases, as DSK-images from the Spectrum releases, as ADF/ADL/ADFS images from the Acorn Archimedes releases, as ATR images from the AtariXL/Atari800 releases, or even as NIB/2MG/WOZ images from the Apple II releases. .Pp .Op Fl ini Ar dMagnetic.ini pawn .br .Op Fl ini Ar dMagnetic.ini guild .br .Op Fl ini Ar dMagnetic.ini jinxter .br .Op Fl ini Ar dMagnetic.ini corruption .br .Op Fl ini Ar dMagnetic.ini fish .br .Op Fl ini Ar dMagnetic.ini pawn .br .Op Fl ini Ar dMagnetic.ini myth .br .Op Fl ini Ar dMagnetic.ini wonderland .br An alternative way to provide the location of the .mag and .gfx, the image files or the directory name, is through a dMagnetic.ini file. See helpini or .Xr dMagneticini 5 for an example of a working dMagnetic.ini file. .br IT SHOULD BE NOTED that the default location for dMagnetic.ini is in the user's home directory. If the file is located there, "The Pawn", for example, can be started by typing .br .Nm pawn .br .Pp .Op Fl rmode Ar pseudo .Op Fl rseed Ar SEED .br .Op Fl rmode Ar real .br Certain elements of the game rely on chance. For this, the virtual machine within .Nm offers two possibilities: Playing against a "pseudo" random generator, which results in a certain degree of determinism. On the other hand, playing with "real" random values from the operating system, allows for a completely new experience. .Pp .Op Fl savegames Ar advanced .br .Op Fl savegames Ar classic .br Saving and restoring game states is a major part of playing interactive fiction. .Nm is capable of loading the "classic" formats, to allow players to continue their 30 year old game. However, it prefers the "advanced" format, which is more robust. .Pp .Op Fl vrows Ar ROWS .br .Op Fl vcols Ar COLUMNS .br .Nm has been designed with terminal windows as main output in mind. The terminal window has a fixed number of rows and columns to print out text. Its size is limited, but it is used by .Nm to render the beautiful BEAUTIFUL pictures as well. To restrict the space in which they are rendered (in glorious ANSI art), those options can be used to set the upper limits. .br .Pp .Op Fl vecho .br When trying to run .Nm and redirecting the output into a file, the inputs are missing. This option reprints what was typed in, to allow for a spoilery script. .Pp .Op Fl vlog Ar LOGFILE.log .br Sort of a travel journal, this option lets you write the commands that where typed into a file. .br .Pp .Op Fl vmode Ar none .br .Op Fl vmode Ar monochrome .br .Op Fl vmode Ar low_ansi .br .Op Fl vmode Ar low_ansi2 .br .Op Fl vmode Ar high_ansi .br .Op Fl vmode Ar high_ansi2 .br .Op Fl vmode Ar utf .br .Op Fl vmode Ar sixel .Op Fl sres Ar 1024x768 .Op Fl sforce .br This option allows for selecting a different mode to render the images, should the actual one prove to be unsuitable for the preferred terminal program. .br .Pp .Op Fl version .br Shows the current version of .Nm . .Sh BUGS Report bugs to .An Aq dettus@dettus.net . Make sure to include DMAGNETIC somewhere in the subject. .Sh AUTHOR Written by .An Thomas Dettbarn .Sh SEE ALSO .Xr dMagneticini 5 dmagnetic-0.37/dMagneticini.50000644000175000017500000002410714430741336014606 0ustar useruser.\" Process this file with .\" groff -man -Tascii dMagneticini.5 .\" . .Dd January 22nd, 2023 .Os OpenBSD .Dt dMagneticini 5 . .Sh NAME .Nm dMagnetic.ini .Nd Configuration file for dMagnetic. . . .Sh SHORTCUT Run dMagnetic -helpini to see an example. .Sh SYNOPSIS The configuration for .Xr dMagnetic 6 , and the location of the game binaries is stored in a .ini file. .Sh ELEMENTS The elements within the .ini files are .Bl -tag -width 10 .It Sections Sections are in square brackets. .Pp [FILE] .Pp [DEFAULTGUI] . .It Entries entries have a = in them. .Pp rows=40 .Pp pawnmag=/usr/local/share/games/magneticscrolls/pawn.mag . .It Comments ;This is a comment .Pp . Comments start with a ; .Pp To use a ; in an entry, it has to be preceded by a backslash \\; .br Consequently, using a backslash requires preceding it with a backslash as well. \\\\ .Pp .El .Sh SECTION [FILE] Like any .ini file, it is broken down into sections, each section contains entries. One section has to be [FILES]. It should contain the names of the game binaries, so that the game engine can find them. For example: .br [FILES] .br pawnmag=/usr/local/share/games/magneticscrolls/pawn.mag .br pawngfx=/home/games/magneticscrolls/pawn.gfx .br ;pawnmsdos=/home/games/magneticscrolls/msdosversions/PAWN .Pp The .mag and .gfx packages can be downloaded from https://msmemorial.if-legends.org/memorial.php, btw. If you are lucky enough to have a copy of the MSDOS version stored on your harddrive, you can provide the directory with the pawnmsdos entry. .Pp ;wonderlandtworsc=/games/magneticscrolls/wonderland/TWO.RSC .br ;guildtworsc=/games/magneticscrolls/MSC/GTWO.RSC .br ;corruptiontworsc=/games/magneticscrolls/MSC/CTWO.RSC .br ;fishtworsc=/games/magneticscrolls/MSC/FTWO.RSC .Pp "Wonderland", and the "Magnetic Scrolls Collections" used a different framework, called "Magnetic Windows". This framework utilized a collection of resource files. By providing the name and the location of the second resource file "TWO.RSC", the names of the other ones can be computed. .Pp Another option comes in the form of using .d64 images from the Commodore 64 release of the games. Those image files come in pairs, since the games occupied both sides of the floppy. A komma is used to separate those two. .br ;pawnd64=/games/d64/pawn1.d64, /games/d64/pawn2.d64 .br ;guildd64=/games/d64/guild1.d64, /games/d64/guild2.d64 .br ;jinxterd64=/games/d64/jinxter1.d64, /games/d64/jinxter2.d64 .br ;corruptiond64=/games/d64/corruption1.d64, /games/d64/corruption2.d64 .br ;fishd64=/games/d64/fish1.d64, /games/d64/fish2.d64 .br ;mythd64=/games/d64/myth.d64 .Pp One other option is to use .DSK images from the Amstrad/Schneider CPC releases of the games. Just as the D64 images, they have to be provided as a pair. .br ;pawnamstradcpc=/games/amstradcpc/pawn1.DSK, /games/amstradcpc/pawn2.DSK .br ;guildamstradcpc=/games/amstradcpc/guild1.DSK, /games/amstradcpc/guild2.DSK .br ;jinxteramstradcpc=/games/amstradcpc/jinxter1.DSK, /games/amstradcpc/jinxter2.DSK .br ;corruptionamstradcpc=/games/amstradcpc/corruption1.DSK, /games/amstradcpc/corruption2.DSK .br .Pp There is also the option to use .DSK images from the Spectrum releases of the games. This platform did not have the pictures, so the games were shipped in single floppies. Thus, only one DSK file is needed .br ;pawnspectrum=/games/spectrum/thepawn.DSK .br ;guildspectrum=/games/spectrum/theguildofthieves.DSK .br ;jinxterspectrum=/games/spectrum/jinxter.DSK .br ;corruptionspectrum=/games/spectrum/corruption.DSK .br ;fishspectrum=/games/spectrum/fish.DSK .br ;mythspectrum=/games/spectrum/myth.DSK .br .Pp To use ADF/ADL/ADFS images from the Acorn Archimedes releases, the parameters are as followed: .br ;pawnarchimedes=/games/archimedes/thepawn.ADF .br ;guildarchimedes=/games/archimedes/theguildofthieves.ADF .br ;jinxterarchimedes=/games/archimedes/jinxter.ADF .br ;corruptionarchimedes=/games/archimedes/corruption.ADF .br ;fisharchimedes=/games/archimedes/fish.ADF .br ;mytharchimedes=/games/archimedes/myth.ADF .br .Pp To use ATR images from the AtariXL/Atari800 releases, the parameters are as followed: .br ;pawnatarixl=/atr/PAWN1.ATR,/atr/PAWN2.ATR .br ;guildatarixl=/atr/GUILD1.ATR,/atr/GUILD2.ATR .br ;jinxteratarixl=/atr/JINXTER1.ATR,/atr/JINXTER2.ATR .br .Pp To use NIB,2MG or WOZ images from the Apple II releases, the parameters are as followed: .br ;pawnappleii=/nib/pawn.nib .br ;guildappleii=/2mg/guild.2mg .br ;jinxterappleii=/nib/jinxter1.nib,/nib/jinxter2.nib .br ;corruptionappleii=/woz/corruptA.woz,/woz/corruptB.woz,/woz/corruptC.woz .Pp To avoid any form of misunderstanding, it is recommended to make sure that the .ini file is providing only one of the five methods. The other four should be commented out. .Sh SECTION [RANDOM] This section configures the random number generator. .Pp .br mode=pseudo .br ;mode=real .br seed=12345 .Pp The mode can be one of two: "pseudo" or "real". Each one of them offers a different experience whilst playing. When testing the software, or playing with scripts, the "pseudo" option should be chosen. This offers a certain degree of determinsm. .br Legal values for seed are in the range of 1 to 2147483647. .Pp .Sh SECTION [DEFAULTGUI] This section is configuring the default output Interface. .Pp .br [DEFAULTGUI] .br rows=40 .br columns=100 .br ;align=left .br align=block .br ;align=right .br ;mode=none .br ;mode=monochrome .br ;mode=low_ansi .br ;mode=low_ansi2 .br mode=high_ansi .br ;mode=high_ansi2 .br ;mode=sixel .br low_ansi_characters=\\\\/=|\\; .br monochrome_characters= .:-=+*x#@$X .br sixel_resolution=1024x768 .br sixel_forceresolution=No .Pp Changing the numbers of rows will make pictures longer, changing the number of columns makes them wider. To change the output mode, comment it in, and comment the current one out. .br The characters that will be used in the low ansi rendering mode can be configured with the low_ansi_characters entry. Note that the backslash and semicolon need to be escaped. .br The monochrome_characters entry is a the actual shade that will be displayed. The leftmost part start with the lowest intensity, it goes up until the right. .br The sixel resolution is setting the amount of pixels in which the images are being rendered with sixel mode. .br Not forcing the resolution will render the pictures with the actual aspect ratio. .br .Sh EXAMPLE .Pp .br ;you can download the files from https://msmemorial.if-legends.org/magnetic.php .br [FILES] .br pawnmag=/usr/local/share/games/magneticscrolls/pawn.mag .br pawngfx=/usr/local/share/games/magneticscrolls/pawn.gfx .br ;pawnmsdos=/usr/local/share/games/magneticscrolls/msdosversions/PAWN .br ;pawnd64=/d64/PAWN1.d64,/d64/PAWN2.d64 .br ;pawnamstradcpc=/dsk/PAWN1.DSK,/dsk/PAWN2.DSK .br ;pawnspectrum=/dsk/PAWNspectrum.DSK .br ;pawnarchimedes=/adf/PAWNarchimedes.adf .br ;pawnatarixl=/atr/PAWN1.ATR,/atr/PAWN2.ATR .br ;pawnappleii=/nib/pawn.nib .br guildmag=/usr/local/share/games/magneticscrolls/guild.mag .br guildgfx=/usr/local/share/games/magneticscrolls/guild.gfx .br ;guildmsdos=/usr/local/share/games/magneticscrolls/msdosversions/GUILD .br ;guildtworsc=/usr/local/share/games/magneticscrolls/MSC/GTWO.RSC .br ;guild64=/d64/GUILD1.d64,/d64/GUILD2.d64 .br ;guildamstradcpc=/dsk/GUILD1.DSK,/dsk/GUILD2.DSK .br ;guildspectrum=/dsk/GUILDspectrum.DSK .br ;guildarchimedes=/adf/GUILDarchimedes.adf .br ;guildatarixl=/atr/GUILD1.ATR,/atr/GUILD2.ATR .br ;guildappleii=/nib/guild.nib .br jinxtermag=/usr/local/share/games/magneticscrolls/jinxter.mag .br jinxtergfx=/usr/local/share/games/magneticscrolls/jinxter.gfx .br ;jinxtermsdos=/usr/local/share/games/magneticscrolls/msdosversions/JINXTER .br ;jinxterd64=/d64/JINXTER1.d64,/d64/JINXTER2.d64 .br ;jinxteramstradcpc=/dsk/JINXTER1.DSK,/dsk/JINXTER2.DSK .br ;jinxterspectrum=/dsk/JINXTERspectrum.DSK .br ;jinxterarchimedes=/adf/JINXTERarchimedes.adf .br ;jinxteratarixl=/atr/JINXTER1.ATR,/atr/JINXTER2.ATR .br ;jinxterappleii=/nib/jinxter1.nib,/nib/jinxter2.nib .br corruptionmag=/usr/local/share/games/magneticscrolls/ccorrupt.mag .br corruptiongfx=/usr/local/share/games/magneticscrolls/ccorrupt.gfx .br ;corruptionmsdos=/usr/local/share/games/magneticscrolls/msdosversions/CORRUPT .br ;corruptiontworsc=/usr/local/share/games/magneticscrolls/MSC/CTWO.RSC .br ;corruptiond64=/d64/CORRUPT1.d64,/d64/CORRUPT2.d64 .br ;corruptionamstradcpc=/dsk/CORRUPTION1.DSK,/dsk/CORRUPTION2.DSK .br ;corruptionspectrum=/dsk/CORRUPTIONspectrum.DSK .br ;corruptionarchimedes=/adf/CORRUPTIONarchimedes.adf .br ;corruptionappleii=/woz/corruptA.woz,/woz/corruptB.woz,/woz/corruptC.woz .br fishmag=/usr/local/share/games/magneticscrolls/fish.mag .br fishgfx=/usr/local/share/games/magneticscrolls/fish.gfx .br ;fishmsdos=/usr/local/share/games/magneticscrolls/msdosversions/FISH .br ;fishtworsc=/usr/local/share/games/magneticscrolls/MSC/FTWO.RSC .br ;fishd64=/d64/FISH1.d64,/d64/FISH2.d64 .br ;fishamstradcpc=/dsk/FISH1.DSK,/dsk/FISH2.DSK .br ;fishspectrum=/dsk/FISHspectrum.DSK .br ;fisharchimedes=/adf/FISHarchimedes.adf .br mythmag=/usr/local/share/games/magneticscrolls/myth.mag .br mythgfx=/usr/local/share/games/magneticscrolls/myth.gfx .br ;mythmsdos=/usr/local/share/games/magneticscrolls/msdosversions/MYTH .br ;mythd64=/usr/local/share/games/magneticscrolls/MYTH.d64 .br ;mythamstradcpc=/dsk/MYTH1.DSK,/dsk/MYTH2.DSK .br ;mythspectrum=/dsk/MYTHspectrum.DSK .br ;mytharchimedes=/adf/MYTHarchimedes.adf .br wonderlandmag=/usr/local/share/games/magneticscrolls/wonder.mag .br wonderlandgfx=/usr/local/share/games/magneticscrolls/wonder.gfx .br ;wonderlandtworsc=/usr/local/share/games/magneticscrolls/WONDER/TWO.RSC .br [RANDOM] .br mode=pseudo .br ;mode=real .br seed=12345 .br [DEFAULTGUI] .br rows=40 .br columns=120 .br ;align=left .br align=block .br ;align=right .br ;mode=none .br ;mode=monochrome .br ;mode=monochrome_inv .br ;mode=low_ansi .br mode=low_ansi2 .br ;mode=high_ansi .br ;mode=high_ansi2 .br ;mode=sixel .br low_ansi_characters=\\/|=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ .br monochrome_characters= .-=+*x#@$X .br sixel_resolution=800x600 .br sixel_forceresolution=No .br .Pp .Sh BUGS Report bugs to .An Aq dettus@dettus.net . Make sure to include DMAGNETIC somewhere in the subject. .Sh AUTHOR Written by .An Thomas Dettbarn .Sh SEE ALSO .Xr dMagnetic 6 dmagnetic-0.37/src/0000755000175000017500000000000014430741336012710 5ustar useruserdmagnetic-0.37/src/loader/0000755000175000017500000000000014430741336014156 5ustar useruserdmagnetic-0.37/src/loader/loader_appleii.c0000644000175000017500000005561514430741336017307 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // the purpose of this file is to read the D64 disk image files // and to translate them into the .mag/.gfx format. #include #include #include #include "loader_appleii.h" #include "loader_common.h" #include "configuration.h" #include "vm68k_macros.h" #define PREAMBLE_SIZE 3 #define EPILOGUE_SIZE 3 #define ADDRBUF_SIZE 8 #define DATABUF_SIZE 343 #define MAXDISKS 3 #define MAXTRACKS 35 #define MAXSECTORS 16 #define SECTORBYTES 256 #define DSKTRACKSIZE (MAXSECTORS*SECTORBYTES) #define DSKSIZE (MAXTRACKS*MAXSECTORS*SECTORBYTES) #define NIBTRACKSIZE (MAXSECTORS*416) // TODO: why 416? #define GAME_PAWN 0 #define GAME_GUILD 1 #define GAME_JINXTER 2 #define GAME_CORRUPTION 3 #define MAXPICTURES 32 #define WOZ_MAXQUARTERTRACKS (4*MAXTRACKS) // TODO: not sure what to do with this... #define WOZ_BLOCKSIZE 512 // the woz format has been designed to work on SD cards. So blocks are aligned to 512 to speed up processing // the most important information, parsed from the WOZ header typedef struct _tWozInfo { int quarterTrack[WOZ_MAXQUARTERTRACKS]; // TODO: not sure what to do with this... int trackStart[WOZ_MAXQUARTERTRACKS]; // the block offset within the WOZ file int trackBits[WOZ_MAXQUARTERTRACKS]; // the number of bits for a track unsigned int crc32_expected; } tWozInfo; int loader_appleii_woz_parseheader(unsigned char* pWozBuf,int wozsize,tWozInfo *pWozInfo) { int idx; int len; unsigned char donemask; idx=0; donemask=0; while (idxcrc32_expected=READ_INT32BE(pWozBuf,idx+8); idx+=12; } else if (memcmp(&pWozBuf[idx],"INFO",4)==0) { len=READ_INT32LE(pWozBuf,idx+4); idx+=8; // skip the info chunk idx+=len; } else if (memcmp(&pWozBuf[idx],"TMAP",4)==0) { int i; len=READ_INT32LE(pWozBuf,idx+4); idx+=8; for (i=0;i=0 && xquarterTrack[i]=x; } } idx+=len; donemask|=1; } else if (memcmp(&pWozBuf[idx],"TRKS",4)==0) { int i; int idx2; len=READ_INT32LE(pWozBuf,idx+4); idx+=8; idx2=idx; for (i=0;itrackStart[i]=WOZ_BLOCKSIZE*READ_INT16LE(pWozBuf,idx2);idx2+=2; idx2+=2; //skip the block count pWozInfo->trackBits[i]=READ_INT32LE(pWozBuf,idx2);idx2+=4; } idx+=len; donemask|=2; } else if (memcmp(&pWozBuf[idx],"META",4)==0) { len=READ_INT32LE(pWozBuf,idx+4); idx+=8; // skip the info chunk idx+=len; } else { fprintf(stderr,"Unknown Tag in WOZ2 detected %02X %02X %02X %02X\n",pWozBuf[idx+0],pWozBuf[idx+1],pWozBuf[idx+2],pWozBuf[idx+3]); return -1; } } if (donemask!=3) { fprintf(stderr," Error parsing the WOZ header\n"); return -1; } return 0; } // when the woz bit stream is synchronized, it can be interpreted as a nib stream. int loader_appleii_woz_synchronize(unsigned char* trackbuf,unsigned char* wozbuf,int len) { unsigned char byte; unsigned int reg; int addrcnt; int datacnt; int part_cnt; int outidx; int i; reg=0; byte=0; addrcnt=0; datacnt=0; part_cnt=0; outidx=0; for (i=0;i>(7-wozbit))&1; byte<<=1; byte|=bit; if (byte&0x80) // byte is synchronized when the highest bit is set. { reg<<=8; reg|=((unsigned int)byte)&0xff; reg&=0x00ffffff; if (part_cnt==0) { if (reg==0xD5AA96) // addr preamble found { addrcnt++; trackbuf[outidx++]=0xD5; // write the preamble trackbuf[outidx++]=0xAA; trackbuf[outidx++]=0x96; part_cnt=ADDRBUF_SIZE+EPILOGUE_SIZE; // collect 11 bytes } if (reg==0xD5AAAD) // data preamble found { datacnt++; trackbuf[outidx++]=0xD5; // write the preamble trackbuf[outidx++]=0xAA; trackbuf[outidx++]=0xAD; part_cnt=DATABUF_SIZE+EPILOGUE_SIZE; // collect 346 bytes } } else { trackbuf[outidx++]=byte; part_cnt--; } byte=0; } i++; } // at this point, the trackbuf contains the NIB stream, even though there is no padding between the sectors. // the nib decoder will be able to handle it, even though a physical drive might not be able to. return 0; } int loader_appleii_decode_addrbuf(unsigned char* pAddrBuf,unsigned char* volume,unsigned char* track,unsigned char* sector,unsigned char* checksum) { const unsigned char loader_appleii_deinterleave[16]={ 0x0,0x7,0xe,0x6,0xd,0x5,0xc,0x4,0xb,0x3,0xa,0x2,0x9,0x1,0x8,0xf }; int ridx; unsigned char x; unsigned char check; ridx=0; check=0; #define ROL(x) ((((x)&0x80)>>7|(x)<<1)&0xff) x=pAddrBuf[ridx++];x=ROL(x);x&=pAddrBuf[ridx++];*volume=x;check^=x; x=pAddrBuf[ridx++];x=ROL(x);x&=pAddrBuf[ridx++];*track=x;check^=x; x=pAddrBuf[ridx++];x=ROL(x);x&=pAddrBuf[ridx++];*sector=loader_appleii_deinterleave[x&0xf];check^=x; x=pAddrBuf[ridx++];x=ROL(x);x&=pAddrBuf[ridx++];*checksum=x;check^=x; if (check) { fprintf(stderr,"Warning. Checksum mismatch\n"); } return check; } int loader_appleii_decodenibtrack(unsigned char* pTrackBuf,int track,unsigned char* pDskBuf) { #define PREAMBLESIZE 3 #define DECODEROFFS 0x96 #define SECTORLSB 86 const unsigned char loader_appleii_addr_preamble[PREAMBLESIZE]={0xD5,0xAA,0x96}; const unsigned char loader_appleii_data_preamble[PREAMBLESIZE]={0xD5,0xAA,0xAD}; //const unsigned char loader_appleii_epilog[PREAMBLESIZE]={0xDE,0xAA,0xEB}; const unsigned char loader_appleii_translatetab[106]={ 0x00,0x01,0xFF,0xFF,0x02,0x03,0xFF,0x04, 0x05,0x06,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x07,0x08,0xFF,0xFF,0xFF,0x09,0x0A,0x0B, 0x0C,0x0D,0xFF,0xFF,0x0E,0x0F,0x10,0x11, 0x12,0x13,0xFF,0x14,0x15,0x16,0x17,0x18, 0x19,0x1A,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0x1B,0xFF,0x1C, 0x1D,0x1E,0xFF,0xFF,0xFF,0x1F,0xFF,0xFF, 0x20,0x21,0xFF,0x22,0x23,0x24,0x25,0x26, 0x27,0x28,0xFF,0xFF,0xFF,0xFF,0xFF,0x29, 0x2A,0x2B,0xFF,0x2C,0x2D,0x2E,0x2F,0x30, 0x31,0x32,0xFF,0xFF,0x33,0x34,0x35,0x36, 0x37,0x38,0xFF,0x39,0x3A,0x3B,0x3C,0x3D, 0x3E,0x3F}; unsigned char addr_track=0; unsigned char addr_sector=0; unsigned char addr_volume=0; unsigned char addr_checksum=0; int volumeid; int ridx; int state; volumeid=-1; ridx=0; state=0; while (ridx<(NIBTRACKSIZE+SECTORBYTES+SECTORLSB+1+9*PREAMBLESIZE)) { switch(state) { case 0: // find the ADDR preamble { if ( pTrackBuf[(ridx+0)%NIBTRACKSIZE]==loader_appleii_addr_preamble[0] && pTrackBuf[(ridx+1)%NIBTRACKSIZE]==loader_appleii_addr_preamble[1] && pTrackBuf[(ridx+2)%NIBTRACKSIZE]==loader_appleii_addr_preamble[2]) { ridx+=PREAMBLE_SIZE; state=1; } else ridx++; } break; case 1: // decode the ADDR data { unsigned char addrbuf[ADDRBUF_SIZE]; int i; for (i=0;i>=1; pDskBuf[widx]<<=1;pDskBuf[widx]|=(1&lsbbuf[j%SECTORLSB]);lsbbuf[j%SECTORLSB]>>=1; ridx++; } ridx+=PREAMBLESIZE; // skip over the epilogue state=0; // search for the next ADDR preamble } break; } } return volumeid; } int loader_appleii_mkgfx(unsigned char *gfxbuf,int* gfxsize,int gameid,int diskcnt,int *pDskOffs) { #define PICTURE_HOTFIX1 0x80000000 #define PICTURE_HOTFIX2 0x40000000 #define PICTURE_HOTFIX3 0x20000000 #define PICTURENUM 26 #define CODESECTIONS 5 #define TOTALSECTIONS (PICTURENUM+CODESECTIONS) unsigned int hotfix1=(1<<1)|(1<< 7); unsigned int hotfix2=(1<<2)|(1<<13); unsigned int hotfix3=(1<<16); if (gameid!=GAME_CORRUPTION) { *gfxsize=0; return 0; } if (diskcnt!=MAXDISKS) { fprintf(stderr,"wrong number of floppy disks\n"); return -1; } *gfxsize=4+4*32+diskcnt*DSKSIZE; { unsigned char mask; unsigned char byte; #define UNHUFFSTART 0x00a00 #define DIR_START 0x997 #define DIR_END 0x9d5 int outidx; int tracks[MAXPICTURES]={0}; int sectors[MAXPICTURES]={0}; int i; int cnt; int unhuffsize; unsigned char terminal; int treeoffs; int bitidx; int unhuffoffs; int treeidx=0; outidx=0; cnt=0; unhuffoffs=4+4*MAXPICTURES+pDskOffs[0]+UNHUFFSTART; terminal=0; unhuffsize=READ_INT16LE(gfxbuf,unhuffoffs); treeoffs=unhuffoffs+2; bitidx=treeoffs+16+16; treeidx=0; mask=0; byte=0; while (outidx>=1; if (branch&0x80) { treeidx=branch&0xf; } else { treeidx=0; terminal<<=4; terminal|=(branch&0xf); terminal&=0xff; if (outidx>=(DIR_START*2) && outidx<=(DIR_END*2) && ((outidx&1)==1)) { if (cnt>=1; hotfix2>>=1; hotfix3>>=1; WRITE_INT32BE(gfxbuf,4+i*4,offs); } } gfxbuf[0]='M';gfxbuf[1]='a';gfxbuf[2]='P';gfxbuf[3]='8'; return 0; } typedef struct _tSection { int track; int sector; int disk; int len; int scrambled; int rle; } tSection; int loader_appleii_readsection(unsigned char* pOut,tSection section,unsigned char* pDskBuf,int diskcnt,int* pDskOffs,int pivot) { int idx; int outidx; int firstsector; int rle; int rlecutoff=DSKSIZE; unsigned char tmp[SECTORBYTES]; unsigned char lc; int i; rle=section.rle; outidx=0; firstsector=1; idx=(section.track*MAXSECTORS+section.sector)*SECTORBYTES+pDskOffs[section.disk]; lc=0xff; while (outidxsection.len) outidx=section.len; return outidx; } int loader_appleii_mkmag(unsigned char* magbuf,int* magsize,int gameid,unsigned char* pDskBuf,int diskcnt,int* pDskOffs) { int magidx; int codesize; int stringidx0; int string1size; int string2size; int dictsize; int huffmantreeidx; int i; typedef struct _tGameInfo { int name_track; int name_sector; tSection code_section; tSection code2_section; int pivot_code2; tSection string1_section; tSection string2_section; tSection dict_section; int version; char gamename[21]; } tGameInfo; const tGameInfo loader_appleii_gameInfo[4]={ {.gamename="The Pawn", .version=0, .name_track=0x01, .name_sector=0x0, .code_section ={.track=0x04,.sector=0x0,.disk= 0,.len=65536,.scrambled=1,.rle=0}, .code2_section ={.track= -1,.sector= -1,.disk=-1,.len= -1,.scrambled=0,.rle=0}, .pivot_code2=-1, .string1_section={.track=0x12,.sector=0x0,.disk= 0,.len=49152,.scrambled=0,.rle=0}, .string2_section={.track=0x1e,.sector=0x0,.disk= 0,.len= 2816,.scrambled=0,.rle=0}, .dict_section ={.track= -1,.sector= -1,.disk=-1,.len= 0,.scrambled=0,.rle=0} }, {.gamename="The Guild of Thieves", .version=1, .name_track=0x00, .name_sector=0x9, .code_section ={.track=0x03,.sector=0x9,.disk= 0,.len=65536,.scrambled=1,.rle=1}, .code2_section ={.track= -1,.sector= -1,.disk=-1,.len= -1,.scrambled=0,.rle=0}, .pivot_code2=-1, .string1_section={.track=0x12,.sector=0xb,.disk= 0,.len=61696,.scrambled=0,.rle=0}, .string2_section={.track=0x21,.sector=0xc,.disk= 0,.len= 3584,.scrambled=0,.rle=0}, .dict_section ={.track= -1,.sector= -1,.disk=-1,.len= 0,.scrambled=0,.rle=0} }, {.gamename="Jinxter", .version=2, .name_track=0x00, .name_sector=0x9, .code_section ={.track=0x08,.sector=0x2,.disk= 0,.len=13056,.scrambled=1,.rle=1}, .code2_section ={.track=0x00,.sector=0x0,.disk= 1,.len=52480,.scrambled=1,.rle=0}, .pivot_code2=7, .string1_section={.track=0x0c,.sector=0xc,.disk= 1,.len= 57344,.scrambled=0,.rle=0}, .string2_section={.track=0x1a,.sector=0xc,.disk= 1,.len= 24832,.scrambled=0,.rle=0}, .dict_section ={.track=0x06,.sector=0x0,.disk= 0,.len= 8704,.scrambled=1,.rle=0} }, {.gamename="Corruption", .version=3, .name_track=0x00, .name_sector=0x9, .code_section ={.track=0x04,.sector=0x0,.disk= 0,.len=16896,.scrambled=1,.rle=0}, .code2_section ={.track=0x00,.sector=0x0,.disk= 1,.len=48640,.scrambled=1,.rle=0}, .pivot_code2=2, .string1_section={.track=0x0b,.sector=0xe,.disk= 1,.len= 57344,.scrambled=0,.rle=0}, .string2_section={.track=0x19,.sector=0xe,.disk= 1,.len= 37120,.scrambled=0,.rle=0}, .dict_section ={.track=0x08,.sector=0x2,.disk= 0,.len= 7680,.scrambled=1,.rle=0} } }; { int offs; int i; unsigned char c; printf("Detected '%s'\n",loader_appleii_gameInfo[gameid].gamename); offs=(loader_appleii_gameInfo[gameid].name_track*MAXSECTORS+loader_appleii_gameInfo[gameid].name_sector)*SECTORBYTES; offs+=3; i=0; c=0; printf("["); while (i<0x2c && c!=0xa9) { c=pDskBuf[pDskOffs[0]+offs]; i++; offs++; if (c>=' ' && c<127) printf("%c",c); } printf("]\n"); } magidx=42; codesize=loader_appleii_readsection(&magbuf[magidx],loader_appleii_gameInfo[gameid].code_section,pDskBuf,diskcnt,pDskOffs,0); codesize+=loader_appleii_readsection(&magbuf[magidx+codesize],loader_appleii_gameInfo[gameid].code2_section,pDskBuf,diskcnt,pDskOffs,loader_appleii_gameInfo[gameid].pivot_code2); magidx+=codesize; stringidx0=magidx; string1size=loader_appleii_readsection(&magbuf[magidx],loader_appleii_gameInfo[gameid].string1_section,pDskBuf,diskcnt,pDskOffs,0); magidx+=string1size; string2size=loader_appleii_readsection(&magbuf[magidx],loader_appleii_gameInfo[gameid].string2_section,pDskBuf,diskcnt,pDskOffs,0); magidx+=string2size; dictsize=loader_appleii_readsection(&magbuf[magidx],loader_appleii_gameInfo[gameid].dict_section,pDskBuf,diskcnt,pDskOffs,0); magidx+=dictsize; { int j; int matchcnt; huffmantreeidx=0; for (i=string1size;i=4) { huffmantreeidx=i; } } } if (gameid==GAME_CORRUPTION) for (i=0x212a;i<0x232a;i++) magbuf[i]=0; // finishing touches on corruption loader_common_addmagheader(magbuf,magidx,loader_appleii_gameInfo[gameid].version,codesize,string1size,string2size,dictsize,huffmantreeidx); *magsize=magidx; return 0; } int loader_appleii(char *appleiiname, char *magbuf,int* magsize, char *gfxbuf,int* gfxsize, int nodoc) { unsigned char* pDskBuf; char filename[1024]; unsigned char trackbuf[NIBTRACKSIZE]={0}; int i,l; int j; int diskcnt; int volumeids[MAXDISKS]={0}; int dskidx; int gameid; int diskoffs[MAXDISKS]={0}; tWozInfo wozInfo; int retval; FILE *f; #define SIZE_NIBIMAGE 232960 #define SIZE_2MGIMAGE 143424 #define SIZE_DSKIMAGE 143360 pDskBuf=(unsigned char*)&gfxbuf[4+MAXPICTURES*4]; l=strlen(appleiiname); j=0; diskcnt=0; dskidx=0; for (i=0;i=0 && disknum #include #include #include "vm68k_macros.h" #include "loader_common.h" #include "loader_dsk.h" #define DSK_IMAGESIZE 194816 #define MAXFILENAMELEN (8+3) // filenames in CPM are 8 bytes long. with a 3 byte extension #define EXTENDLEN 4 // this part is 4 bytes long #define MAXBLOCKS 16 // 16 pointers per directory entry #define MAXBLOCKSIZE 1024 // one pointer is pointing towards 1 kByte #define MINSECTORSIZE 128 // the smallest number of bytes in a sector #define MAXOFFSETSPERENTRY ((MAXBLOCKSIZE/MINSECTORSIZE)*MAXBLOCKS) #define SIZE_FILEHEADER 256 #define SIZE_TRACKHEADER 256 #define SIZE_SECTORHEADER 8 #define SIZE_DIRENTRY 32 #define MAX_DIRENTRIES 32 // TODO??? #define MAX_SECTORNUMPERTRACK 64 #define MAX_SECTORNUMPERDISK (DSK_IMAGESIZE/MINSECTORSIZE) #define MAX_DISKS 2 #define NUM_GAMES 6 #define MAX_TRACKNUM 128 // TODO?? #define FILESUFFIX1 1 #define FILESUFFIX2 2 #define FILESUFFIX3 3 #define FILESUFFIX4 4 #define FILESUFFIX5 5 #define FILESUFFIX6 6 #define FILESUFFIX7 7 #define FILESUFFIX8 8 typedef struct _tGames { char gamename[32]; char gamefilename[MAXFILENAMELEN+1]; int version; unsigned short expectedsuffixes_amstradcpc; unsigned short expectedsuffixes_spectrum; } tGames; const tGames loader_dsk_knownGames[NUM_GAMES]={ // name,game file names, version number {.gamename="The Pawn", .gamefilename="PAWN", .version=0, .expectedsuffixes_amstradcpc=(1<>8))&0xff; outputbuf[i]^=key; } } int loader_dsk_readfile(unsigned char* inputbuf,unsigned char* outputbuf,int fileID,tDirEntry* pDirEntries,int entrycnt,int sectorsize) { int i; int outputidx; outputidx=0; for (i=0;i2) { fprintf(stderr,"Please provide no more than 2 filenames, separated by ,\n"); return -1; } } } // load the game for (i=0;i=MAX_SECTORNUMPERTRACK) { fprintf(stderr,"this file has too many sectors\n"); return -1; } // after the track header comes the sector header for (k=0;ksectorids[order[l]]) { // swap them order[k]^=order[l]; order[l]^=order[k]; order[k]^=order[l]; } } } for (k=0;k='0' && dirEntries[entrycnt].name[strlen(loader_dsk_knownGames[m].gamefilename)]<='8') ) { gamedetected=m; validfilename=1; } } } if (gamedetected!=-1) { int m; m=strlen(loader_dsk_knownGames[gamedetected].gamefilename); if (strncmp(dirEntries[entrycnt].name,loader_dsk_knownGames[gamedetected].gamefilename,m)==0) { dirEntries[entrycnt].fileID=dirEntries[entrycnt].name[m]-'0'; if (dirEntries[entrycnt].fileID>=0 && dirEntries[entrycnt].fileID<=8) { foundsuffixes|=(1<>5)&4; dirEntries[entrycnt].attrs|=(ptr[10]>>6)&2; dirEntries[entrycnt].attrs|=(ptr[11]>>7)&1; for (l=0;l>=1; } fprintf(stderr,"\n"); s=foundsuffixes&0x1fe; fprintf(stderr,"found "); for (i=0;i<9;i++) { if (s&1) fprintf(stderr,"%s%d ",loader_dsk_knownGames[gamedetected].gamefilename,i); s>>=1; } fprintf(stderr,"\n"); fprintf(stderr,"!! Some files missing. Sorry\n\n"); return 1; } if (amstrad0spectrum1) { retval=loader_dsk_spectrum_mag((unsigned char*)magbuf,magsize, dskimage,diskcnt, (unsigned char*)gfxbuf,2*DSK_IMAGESIZE, gamedetected, dirEntries,entrycnt,sectorsize, nodoc); *gfxsize=0; } else { // start with the magbuf. use the first half of the empty gfx buf as tmpbuf for the unhuff retval=loader_dsk_amstradcpc_mag((unsigned char*)magbuf,magsize, dskimage,diskcnt, (unsigned char*)gfxbuf,2*DSK_IMAGESIZE, gamedetected, dirEntries,entrycnt,sectorsize, nodoc); if (retval) return retval; retval=loader_dsk_amstradcpc_gfx((unsigned char*)gfxbuf,gfxsize, dskimage,diskcnt, gamedetected, dirEntries,entrycnt,sectorsize); } return retval; } dmagnetic-0.37/src/loader/loader_mw.h0000644000175000017500000000265214430741336016305 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LOADER_MW_H #define LOADER_MW_H int loader_magneticwindows(char* two_rsc, char *magbuf,int* magsize, char* gfxbuf,int* gfxsize); #endif dmagnetic-0.37/src/loader/loader_msdos.h0000644000175000017500000000266414430741336017012 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LOADER_MSDOS_H #define LOADER_MSDOS_H int loader_msdos(char* msdosdir, char *magbuf,int* magsize, char* gfxbuf,int* gfxsize, int nodoc); #endif dmagnetic-0.37/src/loader/loader_atarixl.h0000644000175000017500000000267514430741336017333 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LOADER_ATARIXL_H #define LOADER_ATARIXL_H int loader_atarixl(char* atarixlname, char *magbuf,int* magsize, char* gfxbuf,int* gfxsize, int nodoc); #endif dmagnetic-0.37/src/loader/maggfxloader.h0000644000175000017500000000355414430741336016776 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef MAGGFXLOADER_H #define MAGGFXLOADER_H #define MAXFILENAMESIZE 1024 typedef enum _eBinType { BINTYPE_NONE, BINTYPE_MAGGFX, BINTYPE_MSDOS, BINTYPE_TWORSC, BINTYPE_D64, BINTYPE_AMSTRADCPC, BINTYPE_SPECTRUM, BINTYPE_ARCHIMEDES, BINTYPE_ATARIXL, BINTYPE_APPLEII } eBinType; typedef enum _eFileType { FILETYPE_UNKNOWN, FILETYPE_MAG, FILETYPE_GFX } eFileType; int maggfxloader(char *magbuf,int* magsize, char* gfxbuf,int* gfxsize, int nodoc, eBinType binType,char* magfilename,char* gfxfilename,char* binfilename); int maggfxguesstype(char* filename,eFileType *pFileType); #endif dmagnetic-0.37/src/loader/loader_msdos.c0000644000175000017500000002136514430741336017004 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // the purpose of this file is to read the MS DOS Game binaries, // and to translate them into the .mag/.gfx format. #include #include #include #include "loader_msdos.h" #include "loader_common.h" #include "configuration.h" #include "vm68k_macros.h" typedef enum _eGame { GAME_UNKNOWN, GAME_JINXTER, GAME_CORRUPTION, GAME_FISH, GAME_MYTH, GAME_PAWN, GAME_GUILD } eGame; typedef struct _tGameInfo { char gamename[24]; eGame game; char prefix[8]; // the prefix for the game's binaries. int disk1size; // the size of the DISK1.PIX file is in an indicator for the game being used. int version; // the interpreter version. } tGameInfo; // The others have some opcodes which I can not decode (yet) #define KNOWN_GAMES 6 const tGameInfo gameInfo[KNOWN_GAMES]={ {.gamename="The Pawn", .version=0, .game=GAME_PAWN, .prefix="PAWN", .disk1size=209529, }, // THE PAWN {.gamename="The Guild of Thieves", .version=1, .game=GAME_GUILD, .prefix="GUILD", .disk1size=185296, }, // THE GUILD OF THIEVES {.gamename="Jinxter", .version=2, .game=GAME_JINXTER, .prefix="JINX", .disk1size=159027, }, // JINXTER {.gamename="Corruption", .version=3, .game=GAME_CORRUPTION, .prefix="CORR", .disk1size=160678, }, // CORRUPTION {.gamename="Fish!", .version=3, .game=GAME_FISH, .prefix="FILE", .disk1size=162541, }, // FISH {.gamename="Myth", .version=3, .game=GAME_MYTH, .prefix="FILE", .disk1size=67512, } // MYTH }; int loader_huffman_unpack(char* magbuf,int* decodedbytes,FILE *f) { unsigned char huffsize; unsigned char hufftab[256]; int huffidx; unsigned short todo; unsigned char byte; unsigned char mask; unsigned char tmp[3]; int threebytes; int n; // this variable is getting rid of some compiler warnings. int magidx; magidx=0; n=fread(&huffsize,sizeof(char),1,f); if (huffsize==0x49) { magbuf[0]=(char)huffsize;magidx=1; magidx+=fread(&magbuf[1],sizeof(char),(1<<20),f); } else { n+=fread(hufftab,sizeof(char),huffsize,f); n+=fread(&todo,sizeof(short),1,f); // what are those two bytes? mask=0; huffidx=0; threebytes=0; while (!feof(f) || mask) { unsigned char branch0,branch1; unsigned char branch; if (mask==0) { n+=fread(&byte,sizeof(char),1,f); mask=0x80; } branch1=hufftab[2*huffidx+0]; branch0=hufftab[2*huffidx+1]; branch=(byte&mask)?branch1:branch0; if (branch&0x80) // the highest bit signals a terminal symbol. { huffidx=0; branch&=0x7f; // the terminal symbols from the tree are only 6 bits wide. // to extend this to 8 bits, the fourth symbol contains the // two MSB for the previous three: // 00AAAAAA 00BBBBBB 00CCCCCC 00aabbcc if (threebytes==3) { int i; threebytes=0; for (i=0;i<3;i++) { // add the 2 MSB to the existing 6. tmp[i]|=((branch<<2)&0xc0); // MSB first magbuf[magidx]|=(char)tmp[i]; magidx++; branch<<=2; // MSB first } } else { tmp[threebytes]=branch; threebytes++; } } else { huffidx=branch; } mask>>=1; } } *decodedbytes=magidx; return (n==0); } int loader_msdos(char* msdosdir, char *magbuf,int* magsize, char* gfxbuf,int* gfxsize, int nodoc) { #define OPENFILE(filename) \ f=fopen((filename),"rb"); \ if (!f) \ { \ fprintf(stderr,"ERROR. Unable to open %s. Sorry.\n",(filename)); \ return -1; \ } FILE *f; char filename[1024]; int i; int gameID; int magsize0; int gfxsize0; char postfix; magsize0=*magsize; gfxsize0=*gfxsize; *magsize=0; *gfxsize=0; // clear the headers. memset(gfxbuf,0,16); memset(magbuf,0,42); postfix=0; { int sizedisk1,sizedisk2,sizeindex; /////////////////////// GFX packing // the header of the GFX is always 16 bytes. // values are stored as BigEndians // 0.. 3 are the magic word 'MaP3' // 4.. 7 are the size of the GAME4 (index) file (always 256) // 8..11 are the size of the DISK1.PIX file // 12..15 are the size of the DISK2.PIX file // then the INDEX file (beginning at 16) // then the DISK1.PIX file // then the DISK2.PIX file // step 1: find out which game it is. snprintf(filename,1024,"%s/DISK1.PIX",msdosdir); OPENFILE(filename); sizedisk1=fread(&gfxbuf[16+256],sizeof(char),gfxsize0-16-256,f); fclose(f); gameID=-1; for (i=0;i=KNOWN_GAMES) { fprintf(stderr,"ERROR: Unable to recognize game\n"); return -2; } else { printf("Detected %s\n",gameInfo[gameID].gamename); } // step 2: read the binary files, and store them in the gfxbuffer snprintf(filename,1024,"%s/DISK2.PIX",msdosdir); f=fopen(filename,"rb"); if (!f) // MYTH does not have a second disc. { sizedisk2=0; } else { sizedisk2=fread(&gfxbuf[16+256+sizedisk1],sizeof(char),gfxsize0-sizedisk1-16-256,f); fclose(f); } // some versions have filenames ending in a . { snprintf(filename,1024,"%s/%s4%c",msdosdir,gameInfo[gameID].prefix,postfix); f=fopen(filename,"rb"); if (!f) postfix='.'; else fclose(f); } snprintf(filename,1024,"%s/%s4%c",msdosdir,gameInfo[gameID].prefix,postfix); OPENFILE(filename); sizeindex=fread(&gfxbuf[16],sizeof(char),256,f); fclose(f); // step 3: add the header to the gfx buffer gfxbuf[0]='M';gfxbuf[1]='a';gfxbuf[2]='P';gfxbuf[3]='3'; WRITE_INT32BE(gfxbuf, 4,sizeindex); WRITE_INT32BE(gfxbuf, 8,sizedisk1); WRITE_INT32BE(gfxbuf,12,sizedisk2); *gfxsize=16+sizeindex+sizedisk1+sizedisk2; } ////////////////////////// done with GFX packing ////////////////////////// MAG packing { int codesize=0; int dictsize=0; int string1size=0; int string2size=0; int magidx=0; magidx=42; // the program for the 68000 machine is stored in the file ending with 1. snprintf(filename,1024,"%s/%s1%c",msdosdir,gameInfo[gameID].prefix,postfix); OPENFILE(filename); // sometimes, this file is packed. // this can be checked quite easily, because in the other cases it starts with the instruction // 49FA= LEA. // if (loader_huffman_unpack(&magbuf[magidx],&codesize,f)) return -1; fclose(f); magidx+=codesize; // the strings for the game are stored in the files ending with 3 and 2 snprintf(filename,1024,"%s/%s3%c",msdosdir,gameInfo[gameID].prefix,postfix); OPENFILE(filename); string1size=fread(&magbuf[magidx],sizeof(char),magsize0-magidx,f); fclose(f); magidx+=string1size; snprintf(filename,1024,"%s/%s2%c",msdosdir,gameInfo[gameID].prefix,postfix); OPENFILE(filename); string2size=fread(&magbuf[magidx],sizeof(char),magsize0-magidx,f); fclose(f); magidx+=string2size; if (gameInfo[gameID].version>=2) { // dictionaries are packed. dictsize=0; snprintf(filename,1024,"%s/%s0%c",msdosdir,gameInfo[gameID].prefix,postfix); OPENFILE(filename); if (loader_huffman_unpack(&magbuf[magidx],&dictsize,f)) return -1; fclose(f); magidx+=dictsize; } // TODO: what about the 5? if (nodoc) { int i; unsigned char* ptr=(unsigned char*)&magbuf[0]; for (i=0;i #include #include #include "loader_common.h" #include "vm68k_macros.h" #define READ_INT24LE(ptr,idx) (\ (((unsigned int)((ptr)[((idx)+2)])&0xff)<<16) |\ (((unsigned int)((ptr)[((idx)+1)])&0xff)<< 8) |\ (((unsigned int)((ptr)[((idx)+0)])&0xff)<< 0) |\ 0) /////////////////////////////////////////// #define ADFS_IMAGESIZE 819200 #define ADFS_NICKMAP_OFFSET 0x40 #define ADFS_NICKMAP_END 0x400 #define RECURSIVEMAX 5 typedef struct _tGames { char magicdirname[10]; char version; char gamename[32]; unsigned int expectedmask; unsigned char pictureorder[32]; } tGames; #define MAXGAMES 5 #define MAXFILENAMENUM 10 #define F6CODE 6 #define F7DICT 7 #define F8STRING2 8 #define F9STRING1 9 #define F10PICTURES 10 const tGames loader_archimedes_cGames[MAXGAMES]={ {.gamename="The Pawn", .version=0, .magicdirname="Pawn", .expectedmask=(1<>=8; indfound=0; // go through the allocation map, search for the indicator starti=-1; while (i='0' && name[1]<='9') { num*=10; num+=name[1]-'0'; } else num=-1; if (name[2]>='0' && name[2]<='9') { num*=10; num+=name[2]-'0'; } else if (name[2]!=0xd && name[2]!=0x00) num=-1; if (num>=0 && num<=MAXFILENAMENUM) { pOffsets[num]=offset; pLengths[num]=length; found|=(1<' ') match=0; } if (match) { *pGameId=j; } } return found; } return retval; } unsigned int loader_archimedes_findfiles(unsigned char* dskimg,int dsksize, int *pGameId,int* pOffsets,int* pLengths) // pOffsets are pointers to the files F6 (code), F7 (dict), f8 (string2), f9(string1), F10 (pictures). IN THAT ORDER. the same goes for the lengths { int hugo0nick1; int sectorsize; int indlen; int bytespermapbit; int diridx; unsigned int filemask; if (dsksize!=ADFS_IMAGESIZE) { fprintf(stderr,"The diskiamge size is off: %d was expected, but %d bytes were read\n",ADFS_IMAGESIZE,dsksize); return LOADER_NOK; } // step 1: find out, if the disk image contains a file system in the 'Hugo' or 'Nick' format. hugo0nick1=-1; // do it the lazy way. Just check if at the position where the root directory is expected to be, // the magic word "Hugo" or "Nick" appears. #define HUGOOFFS 0x400 #define NICKOFFS 0x800 // for Hugo disks, this is at the beginning of sector1, so @0x401-0x404 if (dskimg[HUGOOFFS+1]=='H' && dskimg[HUGOOFFS+2]=='u' && dskimg[HUGOOFFS+3]=='g' && dskimg[HUGOOFFS+4]=='o') hugo0nick1=0; // for the Nick disks, this is at the beginning of sector2, so 0x801-0x803 else if (dskimg[NICKOFFS+1]=='N' && dskimg[NICKOFFS+2]=='i' && dskimg[NICKOFFS+3]=='c' && dskimg[NICKOFFS+4]=='k') hugo0nick1=1; if (hugo0nick1==-1) { fprintf(stderr,"unable to determine the file system type.\n"); return LOADER_NOK; } sectorsize=1024; indlen=-1; bytespermapbit=-1; diridx=0x400; // step 2: if it is the "Nick" format, it comes with a header if (hugo0nick1==1) { int rootind; // according to //http://www.riscos.com/support/developers/prm/filecore.html#85862 // the header for the "Nick" format is in the first 36 bytes of the dsk image: // since the archimedes had an ARM processor, the values are little endian // 0..3: 4 bytes UNKNOWN // 4: 1 byte log2sector size * // 5: 1 byte sectors/track // 6: 1 bytes heads/track // 7: 1 byte density // 8: 1 byte indlen * // 9: 1 byte byterspermapbit * // 10: 1 byte skey // 11: 1 byte boot option // 12: 1 byte lowsector // 13: 1 byte number of zones // 14: 2 byes zone spare // 16: 4 bytes root indicator * // 20: 4 bytes disk size // 24: 4 bytes disk id // 26..35: disk name // only the ones with the * are important sectorsize=1< ",loader_archimedes_cGames[*pGameId].gamename); if ((filemask&loader_archimedes_cGames[*pGameId].expectedmask)!=loader_archimedes_cGames[*pGameId].expectedmask) { fprintf(stderr,"FAIL\n"); fprintf(stderr,"insufficient files on the disk. sorry\n"); return LOADER_NOK; } fprintf(stderr,"okay\n"); return LOADER_OK; } return LOADER_NOK; } int loader_archimedes_mkmag(unsigned char *dskimg,int dsksize,unsigned char* magbuf,int* magsize, int gameId,int* offsets,int *lengths,int nodoc) { int magidx; int codesize; int string1size; int string2size; int dictsize; magidx=42; // the game code is stored in F6, it is packed codesize=loader_common_unhuffer(&dskimg[offsets[F6CODE]],lengths[F6CODE],&magbuf[magidx]); magidx+=codesize; // the string1 is stored in F9 memcpy(&magbuf[magidx],&dskimg[offsets[F9STRING1]],lengths[F9STRING1]); string1size=lengths[F9STRING1]; magidx+=string1size; // string2 is in F8, it is packed string2size=loader_common_unhuffer(&dskimg[offsets[F8STRING2]],lengths[F8STRING2],&magbuf[magidx]); magidx+=string2size; if (loader_archimedes_cGames[gameId].version) // the pawn did not have a dictionary file. { // the dict is stored in F7, it is packed dictsize=loader_common_unhuffer(&dskimg[offsets[F7DICT]],lengths[F7DICT],&magbuf[magidx]); magidx+=dictsize; } else { dictsize=0; } if (nodoc) { int i; unsigned char* ptr=(unsigned char*)&magbuf[0]; for (i=0;i260!!! gfxbuf[0]='M';gfxbuf[1]='a';gfxbuf[2]='P';gfxbuf[3]='i'; WRITE_INT32BE(gfxbuf,4,*gfxsize); idx=offsets[F10PICTURES]; gfxcnt=0; while (idx<(offsets[F10PICTURES]+lengths[F10PICTURES]-48)) { // i do not know where the index for the pictures is. // their format is as followed: there is a 48 byte header. then comes the picture data. // luckily, within this header, at position 0x10, there is a magic sequece 00 00 07 77. // that is sufficient to find the end of the header, and consequently, the beginning of the picture data. if (dskimg[idx+0x10]==0x00 && dskimg[idx+0x11]==0x00 && dskimg[idx+0x12]==0x07 && dskimg[idx+0x13]==0x77) { WRITE_INT32BE(gfxbuf,4*loader_archimedes_cGames[gameId].pictureorder[gfxcnt]+8,idx+48); gfxcnt++; } idx++; } return LOADER_OK; } int loader_archimedes(char* filename,char* magbuf,int* magsize,char* gfxbuf,int* gfxsize,int nodoc) { FILE *f; int newgfxsize; int offsets[MAXFILENAMENUM+1]={0}; int lengths[MAXFILENAMENUM+1]={0}; int gameId=-1; // read the input file. // actually, use the gfx buffer for that one. the data will be read linearly, the // pictures are already in the GFX1 format. all that is needed is a header and // an index. newgfxsize=*gfxsize; f=fopen(filename,"rb"); newgfxsize=fread(gfxbuf,sizeof(char),newgfxsize,f); fclose(f); if (loader_archimedes_findfiles((unsigned char*)gfxbuf,newgfxsize,&gameId,offsets,lengths)!=LOADER_OK) { return LOADER_NOK; } // since the data is in the gfx buf, we need to generate the magbuf first. if (loader_archimedes_mkmag((unsigned char*)gfxbuf,newgfxsize,(unsigned char*)magbuf,magsize, gameId,offsets,lengths,nodoc)!=LOADER_OK) { return LOADER_NOK; } // now all the relevant data from the gfxbuf has been read, it is okay to overwrite it. if (loader_archimedes_mkgfx((unsigned char*)gfxbuf,newgfxsize,(unsigned char*)gfxbuf,gfxsize, gameId,offsets,lengths)!=LOADER_OK) { return LOADER_NOK; } *gfxsize=newgfxsize; return LOADER_OK; } dmagnetic-0.37/src/loader/loader_dsk.h0000644000175000017500000000271514430741336016443 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LOADER_DSK_H #define LOADER_DSK_H int loader_dsk(char* amstradcpcname, char *magbuf,int* magsize, char* gfxbuf,int* gfxsize, int amstrad0spectrum1, int nodoc); #endif dmagnetic-0.37/src/loader/loader_archimedes.h0000644000175000017500000000270614430741336017766 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LOADER_ARCHIMEDES_H #define LOADER_ARCHIMEDES_H int loader_archimedes(char* archimedesname, char *magbuf,int* magsize, char* gfxbuf,int* gfxsize,int nodoc); #endif dmagnetic-0.37/src/loader/loader_d64.h0000644000175000017500000000265514430741336016262 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LOADER_D64_H #define LOADER_D64_H int loader_d64(char* d64name, char *magbuf,int* magsize, char* gfxbuf,int* gfxsize, int nodoc); #endif dmagnetic-0.37/src/loader/maggfxloader.c0000644000175000017500000001330714430741336016766 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // the purpose of this file is to figure out what kind of binaries the // user has. is it the .mag/.gfx one? or is it the original MS-DOS version? #include #include #include #include "maggfxloader.h" #include "configuration.h" #include "vm68k_macros.h" #include "loader_msdos.h" #include "loader_mw.h" #include "loader_d64.h" #include "loader_dsk.h" #include "loader_archimedes.h" #include "loader_atarixl.h" #include "loader_appleii.h" // the purpose of this function is to guess int maggfxguesstype(char* filename,eFileType *pFileType) { FILE *f; int n; unsigned char buf[4]; int retval; retval=0; *pFileType=FILETYPE_UNKNOWN; f=fopen(filename,"rb"); if (f) { n=fread(buf,sizeof(char),4,f); if (n==4) { if (buf[0]=='M' && buf[1]=='a' && buf[2]=='S' && buf[3]=='c') *pFileType=FILETYPE_MAG; if (buf[0]=='M' && buf[1]=='a' && buf[2]=='P') *pFileType=FILETYPE_GFX; // MaPi, MaP2, MaP3...MaP8 } fclose(f); } return retval; } int maggfxloader(char *magbuf,int* magsize, char* gfxbuf,int* gfxsize, int nodoc, eBinType binType,char* magfilename,char* gfxfilename,char* binfilename) { FILE *f; int retval; int n; switch (binType) { case BINTYPE_TWORSC: retval=loader_magneticwindows(binfilename,magbuf,magsize,gfxbuf,gfxsize);break; case BINTYPE_D64: retval=loader_d64(binfilename,magbuf,magsize,gfxbuf,gfxsize,nodoc);break; case BINTYPE_AMSTRADCPC: retval=loader_dsk(binfilename,magbuf,magsize,gfxbuf,gfxsize,0,nodoc); break; case BINTYPE_SPECTRUM: retval=loader_dsk(binfilename,magbuf,magsize,gfxbuf,gfxsize,1,nodoc);break; case BINTYPE_ARCHIMEDES: retval=loader_archimedes(binfilename,magbuf,magsize,gfxbuf,gfxsize,nodoc); break; case BINTYPE_ATARIXL: retval=loader_atarixl(binfilename,magbuf,magsize,gfxbuf,gfxsize,nodoc); break; case BINTYPE_APPLEII: retval=loader_appleii(binfilename,magbuf,magsize,gfxbuf,gfxsize,nodoc); break; case BINTYPE_MSDOS: retval=loader_msdos(binfilename,magbuf,magsize,gfxbuf,gfxsize,nodoc); break; case BINTYPE_MAGGFX: retval=0; if (magfilename[0] && !gfxfilename[0]) { // deducing the name of the gfx file from the mag file int l; int found; found=0; l=strlen(magfilename); fprintf(stderr,"Warning! -mag given, but not -gfx. Deducing filename\n"); if (l>=4) { if (strncmp(&magfilename[l-4],".mag",4)==0) { memcpy(gfxfilename,magfilename,l+1); found=1; gfxfilename[l-4]='.'; gfxfilename[l-3]='g'; gfxfilename[l-2]='f'; gfxfilename[l-1]='x'; } } if (!found) { fprintf(stderr,"filename did not end in .mag (lower case)\n"); return 1; } } if (!magfilename[0] && gfxfilename[0]) { // deducing the name of the mag from the gfx int l; int found; found=0; l=strlen(gfxfilename); fprintf(stderr,"warning! -gfx given, but not -mag. Deducing filename\n"); if (l>=4) { if (strncmp(&gfxfilename[l-4],".gfx",4)==0) { memcpy(magfilename,gfxfilename,l+1); found=1; magfilename[l-4]='.'; magfilename[l-3]='m'; magfilename[l-2]='a'; magfilename[l-1]='g'; } } if (!found) { fprintf(stderr,"filename did not end in .gfx (lower case)\n"); return 1; } } if ((!magfilename[0] || !gfxfilename[0])) { return 1; } f=fopen(magfilename,"rb"); if (f==NULL) { fprintf(stderr,"ERROR: unable to open [%s]\n",magfilename); fprintf(stderr,"This interpreter needs a the game's binaries in the .mag and .gfx\n"); fprintf(stderr,"format from the Magnetic Scrolls Memorial website. For details, \n"); fprintf(stderr,"see https://msmemorial.if-legends.org/memorial.php\n\n"); return -2; } n=fread(magbuf,sizeof(char),*magsize,f); fclose(f); *magsize=n; f=fopen(gfxfilename,"rb"); if (f==NULL) { fprintf(stderr,"ERROR: unable to open [%s]\n",gfxfilename); fprintf(stderr,"This interpreter needs a the game's binaries in the .mag and .gfx\n"); fprintf(stderr,"format from the Magnetic Scrolls Memorial website. For details, \n"); fprintf(stderr,"see https://msmemorial.if-legends.org/memorial.php\n\n"); return -2; } n=fread(gfxbuf,sizeof(char),*gfxsize,f); *gfxsize=n; fclose(f); break; case BINTYPE_NONE: fprintf(stderr,"Please provide the game binaries\n"); retval=-1; break; default: retval=0; break; } // at this point, they are stored in magbuf and gfxbuf. return retval; } dmagnetic-0.37/src/loader/loader_common.c0000644000175000017500000001312314430741336017140 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // the purpose of this file is to read the MS DOS Game binaries, // and to translate them into the .mag/.gfx format. #include #include #include #include "loader_common.h" #include "vm68k_macros.h" #define READ_INT24LE(ptr,idx) (\ (((unsigned int)((ptr)[((idx)+2)])&0xff)<<16) |\ (((unsigned int)((ptr)[((idx)+1)])&0xff)<< 8) |\ (((unsigned int)((ptr)[((idx)+0)])&0xff)<< 0) |\ 0) // most of the time, a huffman tree encoding was employed. // It would start with 1 byte treesize, then the branches in the order LRLRLRLR... (Left Right) // and then the bitstream. // within the tree, there are branches and terminal symbols. // the terminal symbols have bit 7 set, and are thus smaller than 8 bit. // to be able to encode full 8 bit bytes, 4 symbols are being combined into 3 bytes. int loader_common_unhuffer(unsigned char* input,int length,unsigned char* output) { int outputidx; unsigned char byte; unsigned char mask; int bitidx; int treesize; int treeidx; int threecnt; treeidx=0; treesize=input[0]; bitidx=3+treesize; // start decoding the bitstream directly after the tree threecnt=0; outputidx=0; mask=0; byte=0; while (bitidx>=1; if (branch&0x80) // the branch was a terminal symbol. { branch&=0x7f; if (threecnt==3) // this is the fourth symbol. distribute the bits within this symbol to the previous three. { output[outputidx-3]|=((branch>>4)&0x3)<<6; output[outputidx-2]|=((branch>>2)&0x3)<<6; output[outputidx-1]|=((branch>>0)&0x3)<<6; threecnt=0; } else { output[outputidx++]=branch; threecnt++; } treeidx=0; } else { treeidx=branch; // follow the branch } } return outputidx; } int loader_common_addmagheader(unsigned char* magbuf,int magsize,int version,int codesize,int string1size,int string2size,int dictsize,int huffmantreeidx) { if (huffmantreeidx==-1) { huffmantreeidx=string1size; } if (string1size>0x10000) { int x; x=string1size+string2size; string1size=0x10000; string2size=x-string1size; } magbuf[0]='M';magbuf[1]='a';magbuf[2]='S';magbuf[3]='c'; // 0.. 3: the magic word WRITE_INT32BE(magbuf, 4 ,magsize); // 4.. 7: the total size WRITE_INT32BE(magbuf, 8 ,42); // 8..11: the size of the header WRITE_INT16BE(magbuf,12 ,version); // 12..13: the version for the virtual machine WRITE_INT32BE(magbuf,14 ,codesize); // 14..17 the size of the game code WRITE_INT32BE(magbuf,18 ,string1size); // 18..21 the size of the string1 WRITE_INT32BE(magbuf,22 ,string2size); // 22..25 the size of the string2 WRITE_INT32BE(magbuf,26 ,dictsize); // 26..29 the size of the dictionary WRITE_INT32BE(magbuf,30 ,huffmantreeidx) // 30..33 the beginning of the huffman tree within the string buffer WRITE_INT32BE(magbuf,34 ,0); // 34..37: undo size WRITE_INT32BE(magbuf,38 ,0); // 38..41: undo pc return LOADER_OK; } #define BLOCKSIZE 256 #define MAXPIVOT 8 int loader_common_descramble(unsigned char* inptr,unsigned char* outptr,int pivot,unsigned char *lastchar,int rle) { unsigned char tmp[BLOCKSIZE]; int i; int j; int outcnt; unsigned char lc; pivot%=MAXPIVOT; lc=0xff; if (lastchar!=NULL) lc=*lastchar; outcnt=0; // step 1: reverse the input block for (i=0;ipivot) { tmp[i]^=tmp[i-(1+pivot)]; } // step 3: run length encoding. 00 is followed by the amount of zeros following if (lc==0 && rle) { for (j=0;j #include #include #include "loader_common.h" #include "loader_mw.h" #include "configuration.h" #include "vm68k_macros.h" // the purpose of this function is to use the naming of the "???one.rsc" file as a blueprint. int loader_mw_substituteTwoRsc(char* two_rsc,int num,char* output) { int i; int l; int onestart; int uppercase; char *names[12]={"zero","one","two","three","four","five","six","seven","eight","nine","title.vga","title.ega"}; l=strlen(two_rsc); onestart=-1; uppercase=0; for (i=0;i=12 || num<0) return 0; memcpy(output,two_rsc,strlen(two_rsc)); memcpy(&output[onestart],&names[num][0],strlen(names[num])+1); if (uppercase) { for (i=0;i0x10000) text2size=text1size+dictsize-0x10000; else text2size=text1size+dictsize-0xe000; loader_common_addmagheader((unsigned char*)magbuf,magidx,4,codesize,(text1size>=0x10000)?0x10000:0xe000,text2size,wtabsize,text1size); *bytes=magidx; return 1; } int loader_magneticwindows(char* two_rsc, char *magbuf,int* magsize, char* gfxbuf,int* gfxsize) { int bytes; int sizes[10]={0}; printf("Pondering...\n"); if (!loader_mw_collectSizes(two_rsc,sizes,gfxbuf)) { fprintf(stderr,"unable to find resource files\n"); fprintf(stderr," please make sure that they are named TWO.RSC\n"); fprintf(stderr," CTWO.RSC or something similar.\n"); return 1; } if (loader_mw_mkmag(two_rsc,sizes,magbuf,&bytes)) { *magsize=bytes; loader_mw_mkgfx(two_rsc,sizes,gfxbuf,gfxsize); } else { *magsize=0; *gfxsize=0; } return 0; } dmagnetic-0.37/src/loader/loader_common.h0000644000175000017500000000352614430741336017153 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // the purpose of this file is to read the MS DOS Game binaries, // and to translate them into the .mag/.gfx format. #ifndef LOADER_COMMON_H #define LOADER_COMMON_H #define LOADER_OK 0 #define LOADER_NOK -1 int loader_common_unhuffer(unsigned char* input,int length,unsigned char* output); int loader_common_addmagheader(unsigned char* magbuf,int magsize,int version,int codesize,int string1size,int string2size,int dictsize,int huffmantreeidx); int loader_common_descramble(unsigned char* inptr,unsigned char* outptr,int pivot,unsigned char *lastchar,int rle); #endif dmagnetic-0.37/src/loader/loader_appleii.h0000644000175000017500000000267514430741336017312 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LOADER_APPLEII_H #define LOADER_APPLEII_H int loader_appleii(char* appleiiname, char *magbuf,int* magsize, char* gfxbuf,int* gfxsize, int nodoc); #endif dmagnetic-0.37/src/loader/loader_d64.c0000644000175000017500000004477614430741336016267 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // the purpose of this file is to read the D64 disk image files // and to translate them into the .mag/.gfx format. #include #include #include #include "loader_d64.h" #include "loader_common.h" #include "configuration.h" #include "vm68k_macros.h" #define D64_IMAGESIZE 174848 #define D64_TRACKNUM 40 #define D64_SECTORSIZE 256 #define D64_MAXENTRIES 64 #define D64_BITMASKSIZE 6080 // pictures have a resolution of 160x152 pixels typedef enum _eGame { GAME_UNKNOWN, GAME_JINXTER, GAME_CORRUPTION, GAME_FISH, GAME_MYTH, GAME_PAWN, GAME_GUILD } eGame; typedef enum _eFileType { TYPE_UNKNOWN, TYPE_CODE1, TYPE_CODE2, TYPE_CODE1_ENCRYPTED, TYPE_CODE2_ENCRYPTED, TYPE_STRING1, TYPE_STRING2, TYPE_DICTIONARY, TYPE_PICTURE, TYPE_CAMEO } eFileType; typedef struct _tFileEntry { unsigned int offset; // where in the image is the file? unsigned char track; // track unsigned char sector; // sector int len; // do not trust this one int side; // A, B or both? eFileType fileType; } tFileEntry; typedef struct _tGameInfo { eGame game; // the enumeration for the game int sides; // how many floppy sides did this game occupy? char magicword[5]; // the word, that is hidden in the second sector of the disk image int version; // the virtual machine's version char gamename[32]; // human readable signed char pictureorder[32]; // the order in which the pictures can be found in the images are not the same as in other releases. } tGameInfo; const tGameInfo loader_d64_gameinfo[6]= { // the pictures are ordered different from the other releases. there, this list would have been 0 1 2 3 4... // but the C64 had only a limited amount of floppy disk space. {.gamename="The Pawn", .version=0, .game=GAME_PAWN, .sides=2, .magicword="PAWN", .pictureorder={ 4,26,13,23, 0, 8,29, 5, 18,19, 3, 9, 12,11,16,22, 17,21,28, 6, 27,25,24, 2, 1,20,14, 7, 15,10,-1,-1 } }, {.gamename="The Guild of Thieves", .version=1, .game=GAME_GUILD, .sides=2, .magicword="SWAG", .pictureorder={ 9,17,20, 0,26,19,11,12, 4, 5, 2,13,14, 8, 6, 1,15,16, 3,24,21,28,22,25,18,23, 7,10,27,-1,-1,-1} }, {.gamename="Jinxter", .version=2, .game=GAME_JINXTER, .sides=2, .magicword="ARSE", .pictureorder={ 4, 0, 5, 6, 7,-1, 8, 1, 9,10,11,12, 13,14,15,16, 17, 2, 3,27, 18,19,20,21, 22,23,24,25, 26,27,26,26} }, {.gamename="Corruption", .version=3, .game=GAME_CORRUPTION, .sides=2, .magicword="COKE", .pictureorder={ 24, 8, 9,25, 10,13,15,16, 17, 1,18,23, 21, 6, 5, 4, 12,14, 2, 3, 11,20, 7,22, 19, 0,-1,-1, -1,-1,-1,-1 } }, {.gamename="Fish!", .version=3, .game=GAME_FISH, .sides=2, .magicword="GLUG", .pictureorder={ 3,21, 8,11, 18,16,17, 4, 2, 5, 1, 6, 9,10,14,20, 22,24,25, 0, 15,23, 7,19, 13,12,26,-1, -1,-1,-1,-1 } }, {.gamename="Myth", .version=3, .game=GAME_MYTH, .sides=1, .magicword="GODS", .pictureorder={ 0, 1, 2, 3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1} } }; int loader_d64_detectgame(unsigned char* diskram) { // find the magic word int i; char tmp[5]; #define MAGIC_WORD_LOCATION 0x1eb // at those positions in the image, a magic word is hidden. // it can be used to detect the game tmp[0]=diskram[MAGIC_WORD_LOCATION+0]; tmp[1]=diskram[MAGIC_WORD_LOCATION+1]; tmp[2]=diskram[MAGIC_WORD_LOCATION+2]; tmp[3]=diskram[MAGIC_WORD_LOCATION+3]; tmp[4]=0; for (i=0;i<6;i++) { if (strncmp(tmp,loader_d64_gameinfo[i].magicword,4)==0) return i; } return -1; } const unsigned char loader_d64_sectorcnt[D64_TRACKNUM]= { 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, // track 1-17 19,19,19,19,19,19,19, // track 18-24 18,18,18,18,18,18, // track 25-30 17,17,17,17,17,17,17,17,17,17 // track 31-40 }; void loader_d64_readEntries(unsigned char* d64image,int d64size,tFileEntry* pEntries,int* pEntrynum) { int i; int offsets[40]; int cnt; // first step: calculate the offsets for the tracks offsets[0]=0; for (i=1;iD64_IMAGESIZE) { return; } for (i=0;i<256;i++) buf[i]=d64image[offset++]; } void loader_d64_identifyEntries(unsigned char* d64image,tFileEntry* pEntries,int entryNum) { int i; unsigned char tmp1[256]={0}; unsigned char tmp2[256]={0}; int sideoffsets[4]={-1,-1,-1,-1}; // some file types have a fixed position in the list pEntries[2].fileType=TYPE_STRING1; pEntries[3].fileType=TYPE_STRING2; pEntries[4].fileType=TYPE_CAMEO; // first: find the code block for (i=0;i=D64_IMAGESIZE) offset=D64_IMAGESIZE; track=pEntries[i].track; sect =pEntries[i].sector; len =pEntries[i].len; } } rle=0; inputsize=len*D64_SECTORSIZE; for (i=0;i=D64_IMAGESIZE) offset=D64_IMAGESIZE; track=pEntries[i].track; sect =pEntries[i].sector; len =pEntries[i].len; } } outcnt=0; for (i=0;i=D64_IMAGESIZE) { offset=D64_IMAGESIZE; } else { offset=0; } for (j=0;j2) { fprintf(stderr,"Please provide no more than 2 filenames, separated by ,\n"); return -1; } } } // load the game for (i=0;i=D64_IMAGESIZE) offset=D64_IMAGESIZE; else offset=0; len =entries[i].len; picoffs[piccnt]=gfxidx; for (j=0;j #include #include #include "loader_common.h" #include "loader_atarixl.h" #include "vm68k_macros.h" #define LOADER_ATARIXL_OK 0 #define LOADER_ATARIXL_NOK -1 typedef struct _tGameInfo { unsigned char name[22]; unsigned char version; // 0=The Pawn. 1=The Guild of Thieves. 2=Jinxter unsigned int offs_code1; // the offset of the first code section unsigned int offs_code2; // the offset to the second code section unsigned int offs_string1; // the offset to the string1 section unsigned int offs_string2; // the offset to the string2 section unsigned int offs_dict; // the offset to the dict section unsigned int offs_pictures[30]; // the offset to the pictures within the .ATR files. } tGameInfo; #define DISK_SIZE 133136 #define DISK_OFFSETMASK 0x3ffff #define DISK1_FLAG 0x40000 #define DISK2_FLAG 0x80000 // i was not able to find the proper directory. // but i was able to find the code/string/dict sections through correlations and other means. // this is how the following table has been created. #define GAMENUM 3 const tGameInfo loader_atarixl_cGameInfo[GAMENUM]={ {.name="The Pawn", .version=0, .offs_code1=0x3990|DISK1_FLAG, .offs_code2=0, .offs_string1=0x11310|DISK1_FLAG, .offs_string2=0x1c710|DISK1_FLAG, .offs_dict=0, .offs_pictures={ DISK2_FLAG|0x00010,DISK2_FLAG|0x00a90,DISK2_FLAG|0x01a10,DISK2_FLAG|0x02410, DISK2_FLAG|0x1d190,DISK2_FLAG|0x03310,DISK2_FLAG|0x04110,DISK2_FLAG|0x04a10, DISK2_FLAG|0x05210,DISK2_FLAG|0x05890,DISK2_FLAG|0x1f910,DISK2_FLAG|0x06390, DISK2_FLAG|0x11310,DISK2_FLAG|0x11f10,DISK2_FLAG|0x12e90,DISK2_FLAG|0x13990, DISK2_FLAG|0x14190,DISK2_FLAG|0x14f90,DISK2_FLAG|0x15790,DISK2_FLAG|0x1ef10, DISK2_FLAG|0x06f90,DISK2_FLAG|0x16510,DISK2_FLAG|0x17010,DISK2_FLAG|0x1dd90, DISK2_FLAG|0x1e510,DISK2_FLAG|0x18010,DISK2_FLAG|0x18600,DISK2_FLAG|0x18f90, DISK2_FLAG|0x19e10,DISK2_FLAG|0x1a610 } }, {.name="The Guild Of Thieves", .version=1, .offs_code1=0x3890|DISK1_FLAG, .offs_code2=0x10|DISK2_FLAG, .offs_string1=0xc010|DISK2_FLAG, .offs_string2=0x1b110|DISK2_FLAG, .offs_dict=0, .offs_pictures={ DISK1_FLAG|0x1b310,DISK1_FLAG|0x09690,DISK1_FLAG|0x14210,DISK2_FLAG|0x1be90, DISK1_FLAG|0x11d90,DISK1_FLAG|0x08990,DISK1_FLAG|0x10090,DISK1_FLAG|0x17490, DISK1_FLAG|0x11110,DISK1_FLAG|0x08190,DISK2_FLAG|0x1e010,DISK1_FLAG|0x0d610, DISK1_FLAG|0x0ed10,DISK1_FLAG|0x18490,DISK1_FLAG|0x1f710,DISK2_FLAG|0x1d190, DISK1_FLAG|0x0c690,DISK1_FLAG|0x0e010,DISK1_FLAG|0x1ea10,DISK1_FLAG|0x16c90, DISK1_FLAG|0x0aa10,DISK1_FLAG|0x15910,DISK1_FLAG|0x0cc10,DISK1_FLAG|0x09f90, DISK1_FLAG|0x0ba10,DISK1_FLAG|0x19210,DISK1_FLAG|0x1a810,DISK1_FLAG|0x12b90, DISK1_FLAG|0x14d90,0 } }, {.name="Jinxter", .version=2, .offs_code1=0x3790|DISK1_FLAG, .offs_code2=0x10|DISK2_FLAG, .offs_string1=0xc710|DISK2_FLAG, .offs_string2=0x1a710|DISK2_FLAG, .offs_dict=0x6490|DISK1_FLAG, .offs_pictures={ DISK1_FLAG|0x08690,DISK1_FLAG|0x09990,DISK1_FLAG|0x0a690,DISK1_FLAG|0x0b390, DISK1_FLAG|0x0c490, 0, 0,DISK1_FLAG|0x0d840, DISK1_FLAG|0x0e690, 0,DISK1_FLAG|0x0f210,DISK1_FLAG|0x10090, DISK1_FLAG|0x10f10,DISK1_FLAG|0x11d10,DISK1_FLAG|0x12c10,DISK1_FLAG|0x13910, DISK1_FLAG|0x14590,DISK1_FLAG|0x15290,DISK1_FLAG|0x15d10, 0, DISK1_FLAG|0x16d90,DISK1_FLAG|0x17f90,DISK1_FLAG|0x18810,DISK1_FLAG|0x19590, DISK1_FLAG|0x1a410,DISK1_FLAG|0x1b910,DISK1_FLAG|0x1c510, 0, DISK1_FLAG|0x1ce90, 0 } } }; #define BLOCKSIZE 256 #define MAXPIVOT 8 #define GETIDX(idx,offset,disk1offs,disk2offs) \ idx=(offset); \ if ((idx)&DISK1_FLAG) idx=((idx)&DISK_OFFSETMASK)+(disk1offs); \ if ((idx)&DISK2_FLAG) idx=((idx)&DISK_OFFSETMASK)+(disk2offs); int loader_atarixl_detectgame(unsigned char* diskbuf,int* disk1offs,int* disk2offs) { int d1,d2; int found; int i; unsigned char tmp[256]; d1=*disk1offs; d2=*disk2offs; // the game code always starts with the same instruction 49FA FFFE. // in each game release, this is at a different postion within the // floppy disks. this can be used to determine which game it is. // since the game code has been scrambled, it needs to be descrambled first. // first, assume that the first argument was also the first floppy disk found=-1; for (i=0;iversion!=0) { magidx-=2; rle=1; } pivot=0; lc=0xff; GETIDX(idx,pGameInfo->offs_code1,disk1offs,disk2offs); n=loader_common_descramble(&diskbuf[idx],&magbuf[magidx],pivot,&lc,rle); if (pGameInfo->version!=0) { codeleft=READ_INT16BE(magbuf,magidx); } code1size+=n; idx+=BLOCKSIZE; magidx+=n; while (codeleft>=BLOCKSIZE) { pivot=(pivot+1)%MAXPIVOT; n=loader_common_descramble(&diskbuf[idx],&magbuf[magidx],pivot,&lc,rle); codeleft-=BLOCKSIZE; idx+=BLOCKSIZE; magidx+=n; code1size+=n; } if (codeleft!=0) { codeleft=0x100-codeleft; code1size-=(codeleft+2); magidx-=codeleft; } GETIDX(idx,pGameInfo->offs_code2,disk1offs,disk2offs); codeleft=0x10000-code1size; while (codeleft>0) { pivot=(pivot+1)%MAXPIVOT; n=loader_common_descramble(&diskbuf[idx],&magbuf[magidx],pivot,&lc,0); codeleft-=BLOCKSIZE; idx+=BLOCKSIZE; magidx+=n; code2size+=n; } } // the strings are not scrambled. they are being copied over from the disk buffer into the .mag buffer. string1size=0; string2size=0; { int idx1; int idx2; int nxtdisk; int magidx0; magidx0=magidx; GETIDX(idx1,pGameInfo->offs_string1,disk1offs,disk2offs); GETIDX(idx2,pGameInfo->offs_string2,disk1offs,disk2offs); nxtdisk=disksize; if (idx1version==0) { huffmantreeidx=string1size; } else { int i; huffmantreeidx=0; // the huffman tree starts with the sequence 01 02 03 ?? 05. for (i=magidx0;i=0x10000) { int x; x=string1size+string2size; string1size=0x10000; string2size=x-string1size; } } dictsize=0; if (pGameInfo->offs_dict!=0) { int n; int idx; int pivot; unsigned char lc; GETIDX(idx,pGameInfo->offs_dict,disk1offs,disk2offs); pivot=0; while (dictsize<8704) // TODO: magic number { lc=0xff; n=loader_common_descramble(&diskbuf[idx],&magbuf[magidx],pivot,&lc,0); magidx+=n; dictsize+=n; pivot=(pivot+1)%MAXPIVOT; idx+=BLOCKSIZE; } } loader_common_addmagheader(magbuf,magidx,pGameInfo->version,code1size+code2size,string1size,string2size,dictsize,huffmantreeidx); *magbufsize=magidx; return LOADER_ATARIXL_OK; } int loader_atarixl_mkgfx(unsigned char* gfxbuf,int *gfxbufsize,int disk1offs,int disk2offs,const tGameInfo* pGameInfo) { int i; int idx; int gfxidx; // i am lazy // just translate the pre-determined offsets into the gfx buffer index gfxidx=0; gfxbuf[gfxidx++]='M'; gfxbuf[gfxidx++]='a'; gfxbuf[gfxidx++]='P'; gfxbuf[gfxidx++]='7'; for (i=0;i<30;i++) { GETIDX(idx,pGameInfo->offs_pictures[i],disk1offs,disk2offs); WRITE_INT32BE(gfxbuf,gfxidx,idx);gfxidx+=4; } // that's it return LOADER_ATARIXL_OK; } int loader_atarixl(char* atarixlname, char* magbuf,int* magbufsize, char* gfxbuf,int* gfxbufsize, int nodoc) { int disk1offs; int disk2offs; int disksize; int detectedgame; FILE *f; char* diskfile1; char* diskfile2; int i; diskfile1=atarixlname; diskfile2=atarixlname; for (i=0;i #include #include #include "linea.h" #include "gfxloader.h" #include "vm68k_datatypes.h" #include "vm68k_macros.h" #define MAGICVALUE 0x42696e61 // ="Lina" #define MAXINPUTBUFFER 256 #define MAXTEXTBUFFER 4096 // 4kByte ought to be enough #define MAXHEADLINEBUFFER 256 typedef struct _tProperties { tVM68k_ubyte unknown1[5]; tVM68k_ubyte flags1; tVM68k_ubyte flags2; tVM68k_ubyte unknown2; tVM68k_uword parentobject; tVM68k_ubyte unknown3[2]; tVM68k_uword endflags; } tProperties; typedef struct _tLineA { tVM68k_ulong magic; tVM68k_ubyte version; // pointers to important memory sections. tVM68k_ubyte* pMem; tVM68k_ulong memsize; tVM68k_ulong codesize; tVM68k_ubyte* pStrings1; tVM68k_ulong string1size; // the strings for some adventures were stored in two parts, since they have gotten too big tVM68k_ulong string2size; // tVM68k_ubyte* pDict; tVM68k_ulong dictsize; tVM68k_ulong decsize; tVM68k_ubyte* pStringHuffman; tVM68k_ubyte* pUndo; tVM68k_ulong undosize; tVM68k_slong undopc; // pointers to the callback functions cbLineANewOutput pcbNewOutput; void* contextNewOutput; cbLineAInputString pcbInputString; void* contextInputString; cbLineADrawPicture pcbDrawPicture; void* contextDrawPicture; tdMagneticPicture* pPicture; cbLineASaveGame pcbSaveGame; void* contextSaveGame; cbLineALoadGame pcbLoadGame; void* contextLoadGame; // persistent memory for some A0xx instructions. tVM68k_slong random_state; tVM68k_bool random_mode; tVM68k_uword properties_offset; tVM68k_uword linef_subroutine; // version >0 tVM68k_uword linef_tab; // version >1 tVM68k_uword linef_tabsize; // version >1 tVM68k_uword properties_tab; // version >2 tVM68k_uword properties_size; // version >2 tVM68k_slong interrupted_byteidx; tVM68k_ubyte interrupted_bitidx; // input buffer queue. char inputbuf[MAXINPUTBUFFER]; int level; int used; // decoding of the output char textbuf[MAXTEXTBUFFER]; int textbuf_writeidx; int textbuf_level; char headlinebuf[MAXHEADLINEBUFFER]; int headline_writeidx; int headline_level; int headlineflagged; int capital; unsigned char lastchar; int jinxterslide; // workaround for the sliding puzzle in Jinxter char picname[16]; // name or number of the picture to be shown tVM68k_ubyte *magbuf; tVM68k_ulong magsize; tVM68k_ubyte *gfxbuf; tVM68k_ulong gfxsize; // prefer ega images when set. tVM68k_bool egamode; // dump the pictures as .xpm and quit tVM68k_bool dumpxpm; } tLineA; tVM68k_ulong lineA_getrandom(tLineA* pLineA) { if (pLineA->random_mode==0) { pLineA->random_state*=1103515245ull; pLineA->random_state+=12345ull; } else { pLineA->random_state=rand(); } return pLineA->random_state&0x7fffffff; } int lineA_parsegamefiles(tLineA* pLineA,void* pMag,int magsize,void* pGfx,int gfxsize) { int idx; // first: store the mag and gfx data if (pMag==NULL) return LINEA_NOK_INVALID_PTR; pLineA->magbuf=(tVM68k_ubyte*)pMag;pLineA->magsize=magsize; // lets start with the header. // @0 4 bytes "MaSc" // @4 9 bytes TODO // @13 1 byte version // @14 4 bytes codesize // @18 4 bytes string1size // @22 4 bytes string2size // @26 4 bytes dictsize // @30 4 bytes decsize // @34 4 bytes undosize // @38 4 bytes undopc // ---------- // @42 codesize bytes code // @42+codesize string1size bytes... // @... string2size // @... dictsize // @... undo if (pLineA->magbuf[0]!='M' || pLineA->magbuf[1]!='a' || pLineA->magbuf[2]!='S' || pLineA->magbuf[3]!='c') return LINEA_NOK_INVALID_PARAM; // since the magic word seemed to be okay, start reading the header. pLineA->version=pLineA->magbuf[13]; pLineA->codesize=READ_INT32BE(pLineA->magbuf,14); pLineA->string1size=READ_INT32BE(pLineA->magbuf,18); pLineA->string2size=READ_INT32BE(pLineA->magbuf,22); pLineA->dictsize=READ_INT32BE(pLineA->magbuf,26); pLineA->decsize=READ_INT32BE(pLineA->magbuf,30); pLineA->undosize=READ_INT32BE(pLineA->magbuf,34); pLineA->undopc=READ_INT32BE(pLineA->magbuf,38); ///////////////////////////////////// // the code section has to be copied into the shared memory section. // because the same section is writable. first: drain the memory. memset(pLineA->pMem,0,pLineA->memsize); if (pLineA->memsizecodesize) return LINEA_NOK_NOT_ENOUGH_MEMORY; pLineA->memsize=pLineA->codesize; if (pLineA->memsize<0x10000) pLineA->memsize=0x10000; // miminum memory size idx=42; // the header held 42 bytes. memcpy(pLineA->pMem,&pLineA->magbuf[idx],pLineA->codesize); idx+=pLineA->codesize; // every other sections is just read-only. they can stay where they are. // for conveniance reasons, i am adding the pointers. pLineA->pStrings1=&pLineA->magbuf[idx];idx+=pLineA->string1size+pLineA->string2size; pLineA->pDict=&pLineA->magbuf[idx];idx+=pLineA->dictsize; pLineA->pUndo=&pLineA->magbuf[idx];//idx+=pLineA->undosize; pLineA->pStringHuffman=&pLineA->pStrings1[pLineA->decsize]; pLineA->gfxsize=gfxsize; if (pGfx!=NULL) { pLineA->gfxbuf=(tVM68k_ubyte*)pGfx; } if (idx>magsize) { fprintf(stderr,"ERROR: .mag file broken? %d > %d\n",idx,magsize); return LINEA_NOK; } return LINEA_OK; } int lineA_dumppics(void *hLineA) { int i; tLineA* pLineA=(tLineA*)hLineA; if (hLineA==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->pcbDrawPicture==NULL) { return LINEA_OK; } if (pLineA->version!=4) { for (i=0;i<30;i++) { snprintf(pLineA->picname,8,"%02d",i); if (gfxloader_unpackpic(pLineA->gfxbuf,pLineA->gfxsize,pLineA->version,i,NULL,pLineA->pPicture,0)==LINEA_OK) { pLineA->pcbDrawPicture(pLineA->contextDrawPicture,pLineA->pPicture,pLineA->picname,1); } } } else { int dirsize; int diridx; int entrysize; diridx=6; // first entry is at byte 6 if (pLineA->gfxbuf[3]=='4') { entrysize=14; dirsize=entrysize*(READ_INT16LE(pLineA->gfxbuf,4)); // bytes 4..5 are the number of entries } else { dirsize=READ_INT16BE(pLineA->gfxbuf,4); // bytes 4..5 are the size of the directory entrysize=16; } while (dirsize>=entrysize) { pLineA->picname[0]='v'; pLineA->picname[1]='g'; pLineA->picname[2]='a';pLineA->picname[3]='_'; memcpy(&pLineA->picname[4],&(pLineA->gfxbuf[diridx]),6); pLineA->picname[10]=0; if (gfxloader_unpackpic(pLineA->gfxbuf,pLineA->gfxsize,pLineA->version,-1,&pLineA->picname[4],pLineA->pPicture,0)==LINEA_OK) // extract the VGA picture { pLineA->picname[0]='v'; pLineA->pcbDrawPicture(pLineA->contextDrawPicture,pLineA->pPicture,pLineA->picname,2); } if (gfxloader_unpackpic(pLineA->gfxbuf,pLineA->gfxsize,pLineA->version,-1,&pLineA->picname[4],pLineA->pPicture,1)==LINEA_OK) // extract the EGA picture { pLineA->picname[0]='e'; pLineA->pcbDrawPicture(pLineA->contextDrawPicture,pLineA->pPicture,pLineA->picname,2); } dirsize-=entrysize; diridx+=entrysize; } } return LINEA_OK; } int lineA_flush(tLineA* pLineA) { if (pLineA->pcbNewOutput!=NULL) { pLineA->pcbNewOutput(pLineA->contextNewOutput,pLineA->headlinebuf,pLineA->textbuf,pLineA->picname); } pLineA->headline_level=pLineA->headline_writeidx; pLineA->textbuf_level=pLineA->textbuf_writeidx; pLineA->headline_writeidx=pLineA->textbuf_writeidx=0; return 0; } int lineA_newchar(tLineA* pLineA,unsigned char c,unsigned char controlD2,unsigned char flag_headline) { unsigned char c2; // one line, ending with a dash - // is some kind of code for a "verbatim" mode. // it is needed for a sliding puzzle in JINXTER. if (pLineA->jinxterslide) { if ((c>='a' && c<='z') || (c>='A' && c<='Z') || (c==0x5a && pLineA->lastchar=='\n')) { // The sliding puzzle ends with the phrase "As the blocks slide into their final position". Or with two newlines. pLineA->jinxterslide=0; } } else if (c==0x5e && pLineA->lastchar==0x2d) { pLineA->jinxterslide=1; } if (flag_headline && !pLineA->headlineflagged) // this starts a headline { lineA_flush(pLineA); // make sure the output buffers are being flushed pLineA->headline_writeidx=0; } if (!flag_headline && pLineA->headlineflagged) // after the headline ends, a new paragraph is beginning { int i; pLineA->capital=1; // obviously, this starts with a capital letter for (i=0;iheadline_writeidx;i++) { if (pLineA->headlinebuf[i]<' ') { pLineA->headlinebuf[i]=0; pLineA->headline_writeidx--; } } } pLineA->headlineflagged=flag_headline; //newline=0; if (c==0xff) // mark the next letter as Capital { pLineA->capital=1; } else { c2=c&0x7f; // the highest bit was an end marker for the hufman tree in the dictionary // THE RULES FOR THE OUTPUT ARE: // replace tabs and _ with space. // the headline is printed in upper case letters. // after a . there has to be a space. // and after a . The next letter has to be uppercase. // multiple spaces are to be reduced to a single one. // the characters ~ and ^ are to be translated into line feeds. // the caracter 0xff makes the next one upper case. // after a second newline comes a capital letter. // the special marker '@' is either an end marker, or must be substituted by an 's', so that "He thank@ you", and "It contain@ a key" become gramatically correct. if (!(pLineA->jinxterslide)) { if (c2==9 || c2=='_') c2=' '; if (flag_headline && (c2==0x5f || c2==0x40)) c2=' '; // in a headline, those are the control codes for a space. if (c2==0x40) // '@' is a special character { if (controlD2 || pLineA->lastchar==' ') return 0; // When D2 is set, or the last character was a whitespace, it is an end marker else c2='s'; // otherwise it must be substituted for an 's'. One example would be "It contain@ a key". } if (c2==0x5e || c2==0x7e) c2=0x0a; // ~ or ^ is actually a line feed. if (c2==0x0a && pLineA->lastchar==0x0a) // after two consequitive newlines comes a capital letter. { pLineA->capital=1; } if (c2=='.' || c2=='!' || c2==':' || c2=='?') // a sentence is ending. { pLineA->capital=1; } if (((c2>='a' && c2<='z') || (c2>='A' && c2<='Z')) && (pLineA->capital||flag_headline)) // the first letter must be written as uppercase. As well as the headline. { pLineA->capital=0; // ONLY the first character c2&=0x5f; // upper case } //newline=0; if ( (pLineA->lastchar=='.' || pLineA->lastchar=='!' || pLineA->lastchar==':' || pLineA->lastchar=='?'|| pLineA->lastchar==',' || pLineA->lastchar==';') // a sentence as ended && ((c2>='A' && c2<='Z') ||(c2>='a' && c2<='z') ||(c2>='0' && c2<='9'))) // and a new one is beginning. { // after those letters comes an extra space.otherwise,it would look weird. if (flag_headline) { if (pLineA->headline_writeidxheadlinebuf[pLineA->headline_writeidx++]=' '; } else { if (pLineA->textbuf_writeidxtextbuf[pLineA->textbuf_writeidx++]=' '; } } } if (pLineA->textbuf_writeidx>0 && pLineA->lastchar==' ' && (c2==',' || c2==';' || c2=='.' || c2=='!')) // there have been some glitches with extra spaces, right before a komma. which , as you can see , looks weird. { pLineA->textbuf_writeidx--; } if ( //allow multiple spaces in certain scenarios flag_headline || pLineA->lastchar!=' ' || c2!=' ') // combine multiple spaces into a single one. { if (c2==0x0a || (c2>=32 && c2<127 && c2!='@')) { if (flag_headline) { if (pLineA->headline_writeidx=' ') { pLineA->headlinebuf[pLineA->headline_writeidx++]=c2&0x7f; } else { pLineA->headlinebuf[pLineA->headline_writeidx++]=0; } } } else if (pLineA->textbuf_writeidxtextbuf_writeidxtextbuf[pLineA->textbuf_writeidx++]=c2; } // if (c2=='\n') newline=1; } pLineA->lastchar=c2; } } } else if (c2) { if (c2==0x5e) c2='\n'; if (c2=='_') c2=' '; pLineA->textbuf[pLineA->textbuf_writeidx++]=c2; pLineA->lastchar=c2; } } // make sure that the buffers are zero-terminated. pLineA->headlinebuf[pLineA->headline_writeidx]=0; pLineA->textbuf[pLineA->textbuf_writeidx]=0; if (pLineA->headline_writeidx>=(MAXHEADLINEBUFFER-1) || pLineA->textbuf_writeidx>=(MAXTEXTBUFFER-1)) { lineA_flush(pLineA); // the buffers are full, and flushing them is required } return 0; } int lineA_getsize(int* size) { if (size==NULL) return LINEA_NOK_INVALID_PTR; *size=sizeof(tLineA); return LINEA_OK; } int lineA_showTitleScreen(void* hLineA) { tLineA* pLineA=(tLineA*)hLineA; if (hLineA==NULL) return LINEA_NOK_INVALID_PTR; // the pc version has title screens. it would be a shame not to show them. if (pLineA->gfxsize) { if (pLineA->gfxbuf[0]=='M' && pLineA->gfxbuf[1]=='a' && pLineA->gfxbuf[2]=='P' && pLineA->gfxbuf[3]=='3') { int i; char string[]="Press Enter\n"; gfxloader_unpackpic(pLineA->gfxbuf,pLineA->gfxsize,pLineA->version,30,NULL,pLineA->pPicture,0); if (pLineA->pcbDrawPicture!=NULL) { pLineA->pcbDrawPicture(pLineA->contextDrawPicture,pLineA->pPicture,"30",1); } for (i=0;ipcbInputString!=NULL) { pLineA->level=0; pLineA->pcbInputString(pLineA->contextInputString,&pLineA->level,pLineA->inputbuf); pLineA->level=0; } } } return LINEA_OK; } // the purpose of this function is to load the properties for a specific object. int lineA_loadproperties(tLineA* pLineA,tVM68k* pVM68k,tVM68k_uword objectnum,tVM68k_ulong* retaddr,tProperties* pProperties) { tVM68k_ulong addr; int i; if (pLineA->version>2 && (objectnum>pLineA->properties_size)) { addr=(pLineA->properties_size-objectnum)^0xffff; // TODO: WTF? addr*=2; addr+=pLineA->properties_tab; objectnum=READ_INT16BE(pVM68k->memory,addr); } addr=pLineA->properties_offset+14*objectnum; for (i=0;i<5;i++) { pProperties->unknown1[i]=pVM68k->memory[addr+i]; } pProperties->flags1=pVM68k->memory[addr+5]; pProperties->flags2=pVM68k->memory[addr+6]; pProperties->unknown2=pVM68k->memory[addr+7]; pProperties->parentobject=READ_INT16BE(pVM68k->memory,addr+8); for (i=0;i<2;i++) { pProperties->unknown3[i]=pVM68k->memory[addr+i+10]; } pProperties->endflags=READ_INT16BE(pVM68k->memory,addr+12); if (retaddr!=NULL) *retaddr=addr; return LINEA_OK; } int lineA_substitute_aliases(void* hLineA,unsigned short* opcode) { tVM68k_uword inst; inst=*opcode; if ((inst&0xfe00)==0xA400) {inst&=0x01ff;inst|=0x6100;} // BSR if ((inst&0xfe00)==0xA200) {inst=0x4e75;} // RTS if ((inst&0xfe00)==0xA600) {inst&=0x01ff;inst|=0x4a00;} // TST if ((inst&0xfe00)==0xA800) {inst&=0x01ff;inst|=0x4800;} // MOVEM, register to memory (=0x4800) if ((inst&0xfe00)==0xAA00) {inst&=0x01ff;inst|=0x4C00;} // MOVEM, memory to register (=0x4C00) *opcode=inst; return LINEA_OK; } int lineA_init(void* hLineA,void* pSharedMem,int sharedmemsize,void* pMag,int magsize,void* pGfx,int gfxsize) { tLineA* pLineA=(tLineA*)hLineA; int retval; if (hLineA==NULL) return LINEA_NOK_INVALID_PTR; memset(pLineA,0,sizeof(tLineA)); pLineA->magic=MAGICVALUE; pLineA->random_mode=0; pLineA->random_state=12345; pLineA->pMem=pSharedMem; pLineA->memsize=sharedmemsize; pLineA->pcbNewOutput=NULL; pLineA->contextNewOutput=NULL; pLineA->pcbInputString=NULL; pLineA->contextInputString=NULL; pLineA->pcbDrawPicture=NULL; pLineA->contextDrawPicture=NULL;pLineA->pPicture=NULL; pLineA->pcbSaveGame=NULL; pLineA->contextSaveGame=NULL; pLineA->pcbLoadGame=NULL; pLineA->contextLoadGame=NULL; retval=lineA_parsegamefiles(pLineA,pMag,magsize,pGfx,gfxsize); return retval; } int lineA_configrandom(void* hLineA,char random_mode,unsigned int random_seed) { tLineA* pLineA=(tLineA*)hLineA; if (hLineA==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->magic!=MAGICVALUE) return LINEA_NOK_INVALID_PARAM; if (random_mode!=0 && random_mode!=1) return LINEA_NOK_INVALID_PARAM; if (random_seed<1 || random_seed>0x7fffffff) return LINEA_NOK_INVALID_PARAM; pLineA->random_mode=random_mode; pLineA->random_state=random_seed; srand(random_seed); return 0; } int lineA_setEGAMode(void* hLineA,int egamode) { tLineA* pLineA=(tLineA*)hLineA; if (hLineA==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->magic!=MAGICVALUE) return LINEA_NOK_INVALID_PARAM; if (egamode!=0 && egamode!=1) return LINEA_NOK_INVALID_PARAM; pLineA->egamode=egamode; return 0; } int lineA_getVersion(void* hLineA,int* version) { tLineA* pLineA=(tLineA*)hLineA; if (pLineA==NULL) return LINEA_NOK_INVALID_PTR; if (version==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->magic!=MAGICVALUE) return LINEA_NOK_INVALID_PARAM; *version=pLineA->version; return LINEA_OK; } int lineA_singlestep(void* hLineA,void* hVM68k,unsigned short opcode) { tLineA* pLineA=(tLineA*)hLineA; tVM68k* pVM68k=(tVM68k*)hVM68k; int retval=LINEA_NOK_UNKNOWN_INSTRUCTION; if (pLineA==NULL) return LINEA_NOK_INVALID_PTR; if (pVM68k==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->magic!=MAGICVALUE) return LINEA_NOK_INVALID_PARAM; if (pLineA->version!=0 && (opcode&0xf000)==0xf000) // version 1 introduced programmable subroutines. // with version 2, it became programmable { // PUSHLONGTOSTACK(pVM68k,&next,next.pcr); if (pLineA->version==1) { // push long to stack pVM68k->a[7]-=4; WRITE_INT32BE(pVM68k->memory,pVM68k->a[7],pVM68k->pcr); pVM68k->pcr=(pLineA->linef_subroutine)%pVM68k->memsize; } else { tVM68k_uword idx; tVM68k_sword base; idx=opcode&0x7ff; if (idx>=pLineA->linef_tabsize) { // so... at linef_tab is a list of jump-points. // the virtually, the pc jumps onto the address given by the opcode, and from there another XXXX samples on. if (!(opcode&0x0800)) // when it is a call to a subroutine { // push long to stack pVM68k->a[7]-=4; WRITE_INT32BE(pVM68k->memory,pVM68k->a[7],pVM68k->pcr); pVM68k->pcr=(pLineA->linef_subroutine)%pVM68k->memsize; } idx=(opcode|0x0800); idx^=0xffff; base=READ_INT16BE(pVM68k->memory,(pLineA->linef_tab+2*idx)); pVM68k->pcr=(pLineA->linef_tab+2*idx+base)%pVM68k->memsize; // jump, then jump again. } else { // push long to stack pVM68k->a[7]-=4; WRITE_INT32BE(pVM68k->memory,pVM68k->a[7],pVM68k->pcr); pVM68k->pcr=(pLineA->linef_subroutine)%pVM68k->memsize; } } return LINEA_OK; } if ((opcode&0xff00)!=0xa000) return LINEA_NOK_INVALID_PARAM; retval=LINEA_OK; if ((opcode&0xff)<0xdd || (pLineA->version < 4 && (opcode&0xff) < 0xe4) || (pLineA->version < 2 && (opcode&0xff) < 0xed)) { lineA_getrandom(pLineA); // advance the random generator } switch (opcode) { case 0xa000: // getchar // the way i implemented the input is this: // i have a buffer, inputbuf[256]. holding the last input string. // when this instruction is called, one character is read and written into D1. // // when the buffer is empty, the callback function for inputs is called first. { if (pLineA->level==pLineA->used) { // flush the output lineA_flush(pLineA); pLineA->level=0; pLineA->used=0; if (pLineA->pcbInputString!=NULL) { pLineA->pcbInputString(pLineA->contextInputString,&pLineA->level,pLineA->inputbuf); // callback to fill the input buffer. } else { fprintf(stderr,"INTERNAL ERROR: no input set!\n"); exit(0); } } if (pLineA->level>pLineA->used) // still characters in the buffer? { pVM68k->d[1]=pLineA->inputbuf[pLineA->used]; // yes. take one out pLineA->used++; // increase the read pointer for the next time. } } break; case 0xa0de: // version 3 (corruption) introduced this. the other implementation wrote a 1 into D1. { pVM68k->d[1]&=0xffffff00; pVM68k->d[1]|=0x01; } break; case 0xa0df: // version 3 (corruption) introduced this. { tVM68k_ubyte datatype; int i; datatype=READ_INT8BE(pVM68k->memory,pVM68k->a[1]+2); for (i=0;i<8;i++) { pLineA->picname[i]=READ_INT8BE(pVM68k->memory,pVM68k->a[1]+3+i); } pLineA->picname[8]=0; switch (datatype) { case 7: // show picture if (pLineA->gfxsize && pLineA->pcbDrawPicture!=NULL) { gfxloader_unpackpic(pLineA->gfxbuf,pLineA->gfxsize,pLineA->version,-1,pLineA->picname,pLineA->pPicture,pLineA->egamode); pLineA->pcbDrawPicture(pLineA->contextDrawPicture,pLineA->pPicture,pLineA->picname,2); } lineA_flush(pLineA); // report the new picture break; default: break; } } break; case 0xa0e0: // unknown, PROMPT_EV break; case 0xa0e1: // getstring, new feature by corruption! (version4) { int i; // the way i implemented the input is this: // i have a buffer, inputbuf[256]. holding the last input string. // when this instruction is being called, the argument is the output pointer in A1. // up to 256 bytes may be written there. A1 itself is being incremented, until // the end of the output is being reached. // // when the amount of bytes is either 256 or 1, D1 is set to 1. (TODO: why?) // // when the buffer is empty, the callback function for inputs is called first. lineA_getrandom(pLineA); // advance the random generator if (pLineA->level==pLineA->used) { pLineA->level=0; pLineA->used=0; lineA_flush(pLineA); if (pLineA->pcbInputString!=NULL) { pLineA->pcbInputString(pLineA->contextInputString,&pLineA->level,pLineA->inputbuf); // callback to fill the input buffer. } else { fprintf(stderr,"INTERNAL ERROR: no input set!\n"); exit(0); } } i=0; if (pLineA->level>pLineA->used) // still characters in the buffer? { tVM68k_ubyte c; do { c=pLineA->inputbuf[pLineA->used]; if (c==0) c='\n'; // apparently, the virtual machine wants its strings CR terminated. WRITE_INT8BE(pVM68k->memory,(pVM68k->a[1]+i),c); pLineA->used++; // increase the read pointer for the next time. i++; } while (ilevel>pLineA->used && c!='\n'); } pVM68k->a[1]+=(i-1); pVM68k->d[1]&=0xffff0000; if (i==MAXINPUTBUFFER || i==1) { pVM68k->d[1]|=1; } } break; case 0xa0e3: // this one apparently erases the picture if (pVM68k->d[1]==0) { if (pLineA->version<4 || pVM68k->d[6]==0) { // TODO: clear window } } break; case 0xa0e4: { pVM68k->a[7]+=4; // increase the stack pointer? maybe skip an entry or something? pVM68k->pcr=READ_INT32BE(pVM68k->memory,pVM68k->a[7])%pVM68k->memsize; pVM68k->a[7]+=4; } break; case 0xa0e5: // set the Z-flag, RTS, introduced with jinxter. case 0xa0e6: // clear the Z-flag, RTS, introduced with jinxter. case 0xa0e7: // set the Z-flag, introduced with jinxter. case 0xa0e8: // clear the Z-flag, introduced with jinxter. { if (opcode==0xa0e5 || opcode==0xa0e7) // set zflag { pVM68k->sr|=(1<<2); // BIT 2 is the Z-flag } else { // clear z-flag pVM68k->sr&=~(1<<2); // BIT 2 is the Z-flag } if (opcode==0xa0e4 || opcode==0xa0e5 || opcode==0xa0e6) { // RTS: poplongfromstack(pcr); pVM68k->pcr=READ_INT32BE(pVM68k->memory,pVM68k->a[7])%pVM68k->memsize; pVM68k->a[7]+=4; } } break; case 0xa0e9: { // strcpy a word from the dictionary into the memory. // source is in A1 // destination is A0 tVM68k_ubyte tmp; do { tmp=pLineA->pDict[pVM68k->a[1]++]; pVM68k->memory[pVM68k->a[0]++]=tmp; } while (!(tmp&0x80)); } break; case 0xa0ea: // print a word from the dictionary. the beginning INDEX is stored in A1. the headline flag is signalled in D1. { unsigned char c; tVM68k_ubyte* dictptr; tVM68k_uword dictidx; if (pLineA->pDict==NULL || pLineA->dictsize==0) { retval=LINEA_NOK_INVALID_PTR; } else { dictptr=pLineA->pDict; dictidx=pVM68k->a[1]&0xffff; do { c=dictptr[dictidx++]; lineA_newchar(pLineA,c,pVM68k->d[2]&0xff,pVM68k->d[1]&0xff); } while (!(c&0x80)); pVM68k->a[1]&=0xffff0000; pVM68k->a[1]|=dictidx; } } break; case 0xa0eb: // write the byte stored in D1 into the dictionary at index A1 { pLineA->pDict[pVM68k->a[1]&0xffff]=pVM68k->d[1]&0xff; } break; case 0xa0ec: // read one byte stored @A1 from the dictionary. write it into register D0. (jinxter) { pVM68k->d[1]&=0xffffff00; pVM68k->d[1]|=pLineA->pDict[pVM68k->a[1]&0xffff]&0xff; } break; case 0xa0ed: // quit { retval=LINEA_OK_QUIT; } break; case 0xa0ee: // restart { retval=LINEA_OK_RESTART; } break; case 0xa0f0: { //printf("\x1b[1;37;44mLINEA: show picture %d mode %d\x1b[0m\n",pVM68k->d[0],pVM68k->d[1]); int picnum; int picmode; picnum=pVM68k->d[0]; picmode=pVM68k->d[1]; snprintf(pLineA->picname,8,"%d",picnum); if (pVM68k->d[1]) { if (pLineA->gfxsize && pLineA->pcbDrawPicture!=NULL) { gfxloader_unpackpic(pLineA->gfxbuf,pLineA->gfxsize,pLineA->version,picnum,NULL,pLineA->pPicture,pLineA->egamode); pLineA->pcbDrawPicture(pLineA->contextDrawPicture,pLineA->pPicture,pLineA->picname,picmode); } lineA_flush(pLineA); // report the new picture } } break; case 0xa0f1: { // skip some words in the input buffer tVM68k_ubyte* inputptr; tVM68k_uword inputidx; tVM68k_ubyte cinput; int i,n; inputptr=&pVM68k->memory[pVM68k->a[1]&0xffff]; inputidx=0; n=(pVM68k->d[0])&0xffff; for (i=0;ia[1]+=inputidx; } break; case 0xa0f2: { tVM68k_uword objectnum; tProperties properties; int n; tVM68k_bool found; objectnum=(pVM68k->d[2])&0x7fff; n=pVM68k->d[4]&0x7fff; pVM68k->d[0]&=0xffff0000; pVM68k->d[0]|=pVM68k->d[2]&0xffff; found=0; retval=lineA_loadproperties(pLineA,pVM68k,objectnum,&pVM68k->a[0],&properties); do { if (properties.endflags&0x3fff) { found=1; } else { retval=lineA_loadproperties(pLineA,pVM68k,objectnum-1,NULL,&properties); if (objectnum==n) found=1; else objectnum--; } } while ((objectnum!=0) && !found); if (found) pVM68k->sr|=(1<<0); // bit 0 is the cflag pVM68k->d[2]&=0xffff0000; pVM68k->d[2]|=objectnum&0xffff; } break; case 0xa0f3: lineA_newchar(pLineA,pVM68k->d[1],pVM68k->d[2]&0xff,pVM68k->d[3]&0xff); break; case 0xa0f4: { if (pLineA->pcbSaveGame!=NULL) { pLineA->pcbSaveGame(pLineA->contextSaveGame, (char*)&pVM68k->memory[(pVM68k->a[0]&0xffff)], // filename &pVM68k->memory[(pVM68k->a[1]&0xffff)], // ptr (pVM68k->d[1]&0xffff) // len ); } } break; case 0xa0f5: { if (pLineA->pcbLoadGame!=NULL) { pLineA->pcbLoadGame(pLineA->contextLoadGame, (char*)&pVM68k->memory[(pVM68k->a[0]&0xffff)], // filename &pVM68k->memory[(pVM68k->a[1]&0xffff)], // ptr (pVM68k->d[1]&0xffff) // len ); } } break; case 0xa0f6: // get random number (word), modulo D1. { tVM68k_ulong rand; tVM68k_uword limit; rand=lineA_getrandom(pLineA); // advance the random generator limit=(pVM68k->d[1])&0xff; if (limit==0) limit=1; rand%=limit; pVM68k->d[1]&=0xffff0000; pVM68k->d[1]|=(rand&0xffff); } break; case 0xa0f7: { // get a random value between 0 and 255, and write it to D0. tVM68k_ulong rand; rand=lineA_getrandom(pLineA); // advance the random generator pVM68k->d[0]&=0xffffff00; pVM68k->d[0]|=((rand+(rand>>8))&0xff); } break; case 0xa0f8: // write string { // strings are huffman-coded. // version 0: 'string2' holds the decoding tree in the first 256 bytes. // and the offset addresses for the bit streams in string1. // modes have bit 7 set. // // when the string is terminated with a \0 it ends. // when the string terminates with the sequence " @", it will be // extended. // // the extension will have the cflag set. // tVM68k_ulong idx; tVM68k_uword tmp; tVM68k_ubyte val; tVM68k_ubyte prevval; tVM68k_ulong byteidx; tVM68k_ubyte bitidx; char c; if (!(pVM68k->sr&(1<<0))) // cflag is in bit 0. { bitidx=0; idx=pVM68k->d[0]&0xffff; if (idx==0) byteidx=idx; // version 0: string 2 holds the table to decode the strings. // the decoder table is 256 bytes long. afterwards, a bunch of pointers // to bit indexes follow. else byteidx=READ_INT16BE(pLineA->pStringHuffman,(0x100+2*idx)); tmp=READ_INT16BE(pLineA->pStringHuffman,0x100); if (tmp && idx>=tmp) { byteidx+=pLineA->string1size; } } else { byteidx=pLineA->interrupted_byteidx; bitidx=pLineA->interrupted_bitidx; } val=0; do { prevval=val; val=0; while (!(val&0x80)) // terminal symbols have bit 7 set. { tVM68k_ubyte bit; bit=pLineA->pStrings1[byteidx]; if (bit>>(bitidx)&1) { val=pLineA->pStringHuffman[0x80+val]; // =1 -> go to the right } else { val=pLineA->pStringHuffman[ val]; // =0 -> go to the left } bitidx++; if (bitidx==8) { bitidx=0; byteidx++; } } val&=0x7f; // remove bit 7. c=val; lineA_newchar(pLineA,c,pVM68k->d[2]&0xff,pVM68k->d[3]&0xff); } while (val!=0 && !(prevval==' ' && val=='@')); // end markers for the string are \0 and " @" if (prevval==' ' && val=='@') // extend the string next time this function is being called. { pVM68k->sr|=(1<<0); // set the cflag. cflag=bit 0. pLineA->interrupted_byteidx=byteidx; pLineA->interrupted_bitidx=bitidx; } else { pVM68k->sr&=~(1<<0); // clear the cflag. cflag=bit 0. } } break; case 0xa0f9: //get inventory item(d0) { // there is a list of parent objects // // apparently, the structure of the properties is as followed: // byte 0..4: UNKNOWN // byte 5: Flags. // bit 0: is_described // byte 6: some flags // =bit 7: worn // =bit 6: bodypart // =bit 3: room // =bit 2: hidden // byte 8/9: parent object. the player is =0x0000 // byte 10..13: UNKNOWN // the data structure is a list. tVM68k_bool found; tVM68k_uword objectnum1; tVM68k_uword objectnum2; tProperties properties; found=0; // go backwards from the objectnumber for (objectnum1=pVM68k->d[0];objectnum1>0 && !found;objectnum1--) { objectnum2=objectnum1; do { // search for the parent retval=lineA_loadproperties(pLineA,pVM68k,objectnum2,&pVM68k->a[0],&properties); objectnum2=properties.parentobject; if ((properties.flags1&1) //is described || (properties.flags2&0xcc)) // worn, bodypart, room or hidden { objectnum2=0; // break the loop } else if (properties.parentobject==0) found=1; if (!(properties.flags2&1)) { objectnum2=0; // break the loop } } while (objectnum2); } // set the z-flag when the object was found. otherwise clear it. pVM68k->sr&=~(1<<2); // zflag is bit 2 if (found) pVM68k->sr|=(1<<2); pVM68k->d[0]&=0xffff0000; pVM68k->d[0]|=(objectnum1+1)&0xffff; // return value } break; case 0xa0fa: { // search the properties database for a match with the entry in D2. // starting adress is stored in A0. D3 is the variable counter. // d4 is the limit. for (;D3a[0]; pattern=pVM68k->d[2]; byte0word1=pVM68k->d[5]; pVM68k->sr&=~(1<<0); // cflag is bit 0; for (i=(pVM68k->d[3]&0xffff);i<(pVM68k->d[4]&0xffff) && !found;i++) { if (byte0word1) { value=READ_INT16BE(pVM68k->memory,addr); value&=0x3fff; } else { value= READ_INT8BE(pVM68k->memory,addr); value&=0xff; } addr+=14; if (value==pattern) { found=1; pVM68k->a[0]=addr; pVM68k->sr|=(1<<0); // cflag is bit 0. } } pVM68k->d[3]=i; } break; case 0xa0fb: { // skip D2 many words in the dictionary, that is pointed at by A1 tVM68k_ubyte* dictptr; tVM68k_uword dictidx; tVM68k_ubyte cdict; int i; int n; dictidx=0; if (pLineA->version==0 || pLineA->pDict==NULL || pLineA->dictsize==0) { dictptr=&pVM68k->memory[pVM68k->a[1]&0xffff]; } else { //dictptr=pLineA->pDict; dictptr=&pLineA->pDict[pVM68k->a[1]&0xffff]; } n=(pVM68k->d[2]&0xffff); for (i=0;id[2]&=0xffff0000; // that was a counter pVM68k->a[1]+=dictidx; } break; case 0xa0fc: // skip D0 many words in the input buffer, as well as the dictionary. { tVM68k_ubyte* dictptr; tVM68k_ubyte* inputptr; tVM68k_uword dictidx; tVM68k_uword inputidx; int i,n; dictidx=0; inputidx=0; if (pLineA->version==0 || pLineA->pDict==NULL || pLineA->dictsize==0) { dictptr=&pVM68k->memory[pVM68k->a[0]&0xffff]; // TODO: version 0. } else { dictptr=&pLineA->pDict[pVM68k->a[0]&0xffff]; } inputptr=&pVM68k->memory[pVM68k->a[1]&0xffff]; n=(pVM68k->d[0])&0xffff; for (i=0;id[0]&=0xffff0000; // d0 was used as a counter pVM68k->a[0]+=dictidx; pVM68k->a[1]+=inputidx; } break; case 0xa0fd: pLineA->properties_offset=pVM68k->a[0]; if (pLineA->version!=0) { // version 1 introduced line F instructions pLineA->linef_subroutine=(pVM68k->a[3]&0xffff); if (pLineA->version>1) { // version 2 instruduced programmable instructions pLineA->linef_tab=(pVM68k->a[5])&0xffff; pLineA->linef_tabsize=(pVM68k->d[7]+1)&0xffff; } if (pLineA->version>2) { pLineA->properties_tab=(pVM68k->a[6])&0xffff; pLineA->properties_size=(pVM68k->d[6]); } } break; case 0xa0fe: { // register D0 conatins an object number. calculate the address in memory tVM68k_sword objectnum; tVM68k_ulong objectidx; if (pLineA->version>2 && (pVM68k->d[0]&0x3fff)>pLineA->properties_size) { pVM68k->d[0]&=0xffff7fff; // objectidx=((pLineA->properties_size-(pVM68k->d[0]&0x3fff))^0xffff); // TODO: I THINK THIS IS JUST A MODULO!!! objectidx=((pVM68k->d[0]&0x3fff)-pLineA->properties_size)-1; objectnum=READ_INT16BE(pVM68k->memory,pLineA->properties_tab+objectidx*2); } else { if (pLineA->version>=2) { pVM68k->d[0]&=0xffff7fff; } else { pVM68k->d[0]&=0x00007fff; } objectnum=pVM68k->d[0]&0x7fff; } objectnum&=0x3fff; pVM68k->a[0]=pLineA->properties_offset+objectnum*14; } break; case 0xa0ff: { // so, here's what i know: (version 0) // the dictonary is stored at A3. // the word entered at A6 // there is a "bank" in register D6 // // the data structure is more or less plain. but the last char of each word has bit 7 set. // special characters 0x81=ENDOFDICT 0x82=BANKSEPARATOR are used. // // input: (A6) // output: (A2) // dict: (A3) // objects: (A1) { tVM68k_ubyte* dtabptr; tVM68k_ubyte* inputptr; tVM68k_ubyte* outputptr; tVM68k_ubyte* dictptr; tVM68k_ubyte* objectptr; tVM68k_ubyte* adjptr; tVM68k_uword inputidx; tVM68k_uword outputidx; tVM68k_uword outputidx2; tVM68k_uword dictidx; tVM68k_uword adjidx; tVM68k_uword wordidx; tVM68k_ubyte bank; tVM68k_ubyte flag; tVM68k_bool matching; tVM68k_ubyte cdict; tVM68k_bool matchfound; tVM68k_ulong wordmatch; tVM68k_uword longestmatch; tVM68k_ubyte flag2; int i,j; longestmatch=0; flag2=0; inputptr =&pVM68k->memory[pVM68k->a[6]]; if (pLineA->version==0 || pLineA->pDict==NULL || pLineA->dictsize==0) { dictptr=&pVM68k->memory[pVM68k->a[3]&0xffff]; dtabptr=&pVM68k->memory[pVM68k->a[5]&0xffff]; // version>0 } else { dictptr=&pLineA->pDict[pVM68k->a[3]&0xffff]; dtabptr=&pLineA->pDict[pVM68k->a[5]&0xffff]; // version>0 } outputptr =&pVM68k->memory[pVM68k->a[2]]; objectptr =&pVM68k->memory[pVM68k->a[1]]; adjptr =&pVM68k->memory[pVM68k->a[0]]; inputidx=dictidx=outputidx=0; pVM68k->d[0]&=0xffff0000; // this regsiter was used during the adjective search. pVM68k->d[1]&=0xffff0000; // this regsiter was used during the adjective search. flag=0; bank=(pVM68k->d[6]&0xff); wordidx=0; cdict=0; matching=1; matchfound=0; pVM68k->d[0]&=0xffff0000; // the way the first loop works is this: // character by character, a word from the dictionary is compared to the input. // when a mismatch happens, the beginning of the next word is searched. -> matching=0; // while (cdict!=0x81) // 0x81 is the end marker of the dictionary { cdict=dictptr[dictidx++]; if (cdict==0x82) // bank separator { flag=0; inputidx=0; wordidx=0; bank++; matching=1; } else if (matching) { // actively comparing tVM68k_ubyte cinput1,cinput2; cinput1=inputptr[inputidx++]; // the current character cinput2=inputptr[inputidx]; // and the next onea if (pLineA->version!=0) { if (cdict==0x5f && (cinput2!=0 || cinput1==' ')) { flag=0x80; // the dictionary uses _ to signal objects that consist of longer words. "can of worms" thus becomes "can_of_worms". the matcher has to find it. cinput1='_'; // replace the space from the input with an _ to see if there is a match. } } if (cdict&0x80) // the end of an entry in the dictionary is marked by bit 7 being set. { matchfound=0; if ((cinput1&0x5f)==(cdict&0x5f)) // still a match. wonderful. { if (cinput2==0x27) // rabbit's (Wonderland) { tVM68k_ubyte cinput3; inputidx++; cinput3=inputptr[inputidx]; // store the letter after the ' into register D0. for example: rabbit's -> store the S pVM68k->d[0]&=0xffff0000; pVM68k->d[0]|=(cinput3)&0xff; pVM68k->d[0]|=0x200; } if (cdict!=0xa0 || pLineA->version<4) // corruption started using " " as word separator for multi-word objects { if (cinput2==0 || cinput2==0x20 || cinput2==0x27) matchfound=1; // and the input word ends as well. perfect match. } } else { if (pLineA->version==0 && inputidx>7) matchfound=1; // the first 7 characters matched. good enough. matching=0; } } else { // keep comparing. if (pLineA->version!=0) // version 1 introduced objects with multiple words. { if (cinput1==' ' && cdict==0x5f) // multiple word entry found { flag=1; cinput1=0x5f; // multiple word entries are marked by a _ instead of a space. this one makes sure that the next if() will work. } } if ((cinput1&0x5f)!=(cdict&0x5f) || (cdict&0x5f)==0x00 // FIXME ) { if (cinput2==' ' && pLineA->version==0 && inputidx>=7) matchfound=1; // the first 7 characters matched. good enough. matching=0; // there was a mismatch. } } } if (matchfound) { // the matches are stored in the following format: // bit 31..24 are a flag, which is =0 in version 0. // bit 23..16 is the bank. // bit 15..0 contain the matched word number in the bank. wordmatch =(((tVM68k_ulong)flag)<<24); wordmatch|=(((tVM68k_ulong)bank)<<16); wordmatch|=((tVM68k_ulong)wordidx); if (inputidx>=longestmatch) longestmatch=inputidx; WRITE_INT32BE(outputptr,outputidx,wordmatch); // store the candidates in the output location. outputidx+=4; // length of the result: 4 bytes. matchfound=0; } if (cdict&0x80 && cdict!=0x82 && !(pLineA->version>4 && cdict==0xa0)) // when the end of the word is reached. bit 7 is set. { wordidx++; matching=1; // start over inputidx=0; // start over. flag=0; } } WRITE_INT16BE(outputptr,outputidx,0xffff);// the end marker in the buffer is a 0xffff. // the output buffer holds outputidx/4 many results. if (pLineA->version!=0) // version 1 introduced synonyms. { // search the list of output words for (i=0;i>24)&0xff; bank=(wordmatch>>16)&0xff; wordidx=wordmatch&0xffff; if (bank==0x0b) { tVM68k_uword substword; substword=READ_INT16BE(dtabptr,wordidx*2); // TODO: version >1??? // the lower 5 bits are the bank. // the upper 11 bits in the substitute database are the actual word index. bank=substword&0x1f; wordidx=substword>>5; wordmatch=flag;wordmatch<<=8; wordmatch|=(bank&0xff);wordmatch<<=16; wordmatch|=wordidx&0xffff; WRITE_INT32BE(outputptr,i,wordmatch); } } } outputidx2=0; adjidx=0; for (i=0;i>24)&0xff; bank=(wordmatch>>16)&0xff; wordidx=wordmatch&0xffff; mismatch=0; obj=READ_INT16BE(objectptr,objectidx); if (obj && bank==6) { // first step: skip the adjectives that are not meant for this word. each adjective list is separated by a 0. for (j=0;jd[1]&=0xffff0000; if (mismatch==0) { flag2|=flag; wordmatch =flag2&0xff;wordmatch<<=8; wordmatch|=bank&0xff;wordmatch<<=16; wordmatch|=wordidx&0xffff; WRITE_INT32BE(outputptr,outputidx2,wordmatch); outputidx2+=4; } else { pVM68k->d[1]|=1; } } pVM68k->a[5]=pVM68k->a[6]; // flag2 being set denotes that there has been an object that is occupying multiple words. if (flag2 && outputidx) // that match is probably a better one, so move it to the front of the output word list. { for (i=0;ia[5]=pVM68k->a[6]+(longestmatch-3); } } pVM68k->a[2]+=outputidx2; // pVM68k->d[0]=0; pVM68k->a[6]=pVM68k->a[5]+1; } } break; default: printf("\n \x1b[0;37;44mUNIMPLEMENTED LINEA opcode %04X\x1b[0m\n",opcode); break; } return retval; } int lineA_setCBnewOutput(void* hLineA,cbLineANewOutput pCB,void* context) { tLineA* pLineA=(tLineA*)hLineA; if (pLineA==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->magic!=MAGICVALUE) return LINEA_NOK_INVALID_PARAM; pLineA->pcbNewOutput=pCB; pLineA->contextNewOutput=context; return LINEA_OK; } int lineA_setCBinputString(void* hLineA,cbLineAInputString pCB,void* context) { tLineA* pLineA=(tLineA*)hLineA; if (pLineA==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->magic!=MAGICVALUE) return LINEA_NOK_INVALID_PARAM; pLineA->pcbInputString=pCB; pLineA->contextInputString=context; return LINEA_OK; } int lineA_setCBdrawPicture(void* hLineA,cbLineADrawPicture pCB,void* context,tdMagneticPicture *pPicture) { tLineA* pLineA=(tLineA*)hLineA; if (pLineA==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->magic!=MAGICVALUE) return LINEA_NOK_INVALID_PARAM; pLineA->pcbDrawPicture=pCB; pLineA->contextDrawPicture=context; pLineA->pPicture=pPicture; return LINEA_OK; } int lineA_setCBloadGame(void* hLineA,cbLineALoadGame pCB,void* context) { tLineA* pLineA=(tLineA*)hLineA; if (pLineA==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->magic!=MAGICVALUE) return LINEA_NOK_INVALID_PARAM; pLineA->pcbLoadGame=pCB; pLineA->contextLoadGame=context; return LINEA_OK; } int lineA_setCBsaveGame(void* hLineA,cbLineASaveGame pCB,void* context) { tLineA* pLineA=(tLineA*)hLineA; if (pLineA==NULL) return LINEA_NOK_INVALID_PTR; if (pLineA->magic!=MAGICVALUE) return LINEA_NOK_INVALID_PARAM; pLineA->pcbSaveGame=pCB; pLineA->contextSaveGame=context; return LINEA_OK; } dmagnetic-0.37/src/libdmagnetic/linea/gfxloader.c0000644000175000017500000013045214430741336020546 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "vm68k_datatypes.h" #include "vm68k_macros.h" #include "gfxloader.h" #include "linea.h" // for the picture #include #include int gfxloader_twice_as_wide(tdMagneticPicture *pPicture) { int i; // make the picture wider for (i=pPicture->width*pPicture->height;i>=0;i--) { pPicture->pixels[2*i+0]=pPicture->pixels[i]; pPicture->pixels[2*i+1]=pPicture->pixels[i]; } pPicture->width*=2; return 0; } int gfxloader_gfx1(tVM68k_ubyte* gfxbuf,tVM68k_ulong gfxsize,int picnum,tdMagneticPicture* pPicture) { int i; int retval; int picoffs; int width; int height; int tablesize; int datasize; int treeidx; int byteidx; unsigned char curpixel; unsigned char mask; unsigned char byte; unsigned char branch; int rle; pPicture->pictureType=PICTURE_DEFAULT; retval=0; picnum&=0xffff; picoffs=READ_INT32BE(gfxbuf,8+4*picnum); // the .gfx file starts with the index pointers to the actual picture data. if (picoffs==0x00000000 || picoffs>gfxsize) { retval=-1; // the picture number was not correct return retval; } // once the offset has been calculated, the actual picture data is as followed: // bytes 0..1: UNKNOWN // bytes 0x02..0x03: X1 // bytes 0x04..0x05: X2 // X2-X1=width // bytes 0x06..0x07: height // bytes 0x08..0x1b: UNKNOWN // bytes 0x1c..0x3b: palette. // bytes 0x3c..0x3d: size of the huffmann table // bytes 0x3e..0x41: size of the data bit stream // // bytes 0x42+0x42+tablesize: huffmann decoding table // bytes 0x42+tablesize..0x42+tablesize+datasize: bitstream curpixel=0; width=READ_INT16BE(gfxbuf,picoffs+4)-READ_INT16BE(gfxbuf,picoffs+2); height=READ_INT16BE(gfxbuf,picoffs+6); // this particular graphics format has 3 bits per RGB channel for (i=0;i<16;i++) { unsigned short s; unsigned int red,green,blue; s=READ_INT16BE(gfxbuf,picoffs+0x1c+2*i); red =(s>>8)&0xf; green =(s>>4)&0xf; blue =(s>>0)&0xf; red*=PICTURE_MAX_RGB_VALUE;green*=PICTURE_MAX_RGB_VALUE;blue*=PICTURE_MAX_RGB_VALUE; red/=7;green/=7;blue/=7; pPicture->palette[i]=(red<<(2*PICTURE_BITS_PER_RGB_CHANNEL))|(green<<(1*PICTURE_BITS_PER_RGB_CHANNEL))|blue; } tablesize=READ_INT16BE(gfxbuf,picoffs+0x3c); // size of the huffmann table datasize =READ_INT32BE(gfxbuf,picoffs+0x3e); // size of the bitstream if (datasize>gfxsize) // probably not correct { return 1; } // the huffmann table contains links. if a bit in the stream is set, the upper 8 bits, otherwise the lower ones. // terminal symbols have bit 7 set. byteidx=picoffs+0x42+tablesize*2+2; mask=0x00; byte=0; rle=0; for (i=0;(i>=1; // the bitstream is being read MSB first if (!(branch&0x80)) // if the highest bit is clear { treeidx=branch; } } while ((branch&0x80)==0x00); // bit 7 denotes a terminal symbol branch&=0x7f; // remove bit 7. if (branch>=0x10) // it bits 6..4 were set, the previous pixels are being repeated { rle=branch-0x10; } else { // since there are only 16 possible pixels, this is it. curpixel=branch; rle=1; // will become 0 in the next revelation. } } pPicture->pixels[i]=curpixel; rle--; } pPicture->height=height; pPicture->width=width; // the finishing touch: each line has to be XORed with the previous one. for (i=width;ipixels[i]^=pPicture->pixels[i-width]; } return retval; } int gfxloader_gfx2(tVM68k_ubyte* gfxbuf,tVM68k_ulong gfxsize,char* picname,tdMagneticPicture* pPicture) { int directorysize; int offset; //int length; int retval; int i; int j; int found; pPicture->width=0; pPicture->height=0; pPicture->pictureType=PICTURE_DEFAULT; // only gfx3 offers halftone pictures // the gfx2 buffer starts with the magic value, and then a directory directorysize=READ_INT16BE(gfxbuf,4); retval=0; // step 1: find the correct filename found=0; offset=-1; for (i=0;i>8)&0xf; green =(s>>4)&0xf; blue =(s>>0)&0xf; red*=PICTURE_MAX_RGB_VALUE;green*=PICTURE_MAX_RGB_VALUE;blue*=PICTURE_MAX_RGB_VALUE; red/=7;green/=7;blue/=7; pPicture->palette[i]=(red<<(2*PICTURE_BITS_PER_RGB_CHANNEL))|(green<<(1*PICTURE_BITS_PER_RGB_CHANNEL))|blue; } datasize=READ_INT32ME(gfxbuf,offset+38); pPicture->width=READ_INT16LE(gfxbuf,offset+42); pPicture->height=READ_INT16LE(gfxbuf,offset+44); if (pPicture->width>PICTURE_MAX_WIDTH) { retval=-1; return retval; } // animmagic=READ_INT16LE(gfx2buf,offset+48+data_size); lumpsize=datasize/pPicture->height/4; // datasize=size of the picture. height: number of lines. thus: datasize/height=number of bytes per line. there are 4 lumps of bits per line. pixidx=0; for (y=0;yheight;y++) { tVM68k_ubyte byte0,byte1,byte2,byte3; tVM68k_ubyte mask; idx0=y*4*lumpsize+offset+48; idx1=idx0+1*lumpsize; idx2=idx0+2*lumpsize; idx3=idx0+3*lumpsize; for (x=0;xwidth;x+=8) { tVM68k_ubyte p; byte0=gfxbuf[idx0++]; byte1=gfxbuf[idx1++]; byte2=gfxbuf[idx2++]; byte3=gfxbuf[idx3++]; mask=(1<<7); // MSB FIRST for (i=0;i<8;i++) { p =(byte0&mask)?0x01:0; p|=(byte1&mask)?0x02:0; p|=(byte2&mask)?0x04:0; p|=(byte3&mask)?0x08:0; mask>>=1; if ((x+i)width) { pPicture->pixels[pixidx++]=p; } } } } } return retval; } int gfxloader_gfx3(tVM68k_ubyte* gfxbuf,tVM68k_ulong gfxsize,int picnum,tdMagneticPicture* pPicture) { // 0.. 3: 4 bytes "MaP3" // 4.. 8: 4 bytes length of index // 8..11: 4 bytes length of disk1.pix // 11..15: 4 bytes length of disk2.pix // then the index // then the disk1.pix data // then the disk2.pix data int retval; int offs1; int offs2; int offset; int indexoffs,indexlen; int disk1offs,disk1len; int disk2offs; int i,n; int huffsize; int treeidx; int byteidx; int unhufcnt; int pixelcnt; int state; unsigned char mask; unsigned char byte; unsigned int unpackedsize; int max_stipple; unsigned char pl_lut[128]={0}; // lookup table for left pixels unsigned char pr_lut[128]={0}; // lookup table for right pixels unsigned char xorbuf[PICTURE_MAX_WIDTH*2]={0}; // ring buffer, to perform an XOR over two lines of stipples unsigned char rgbbuf[16]={0}; // RGB values are 6 bits wide. 2 bits red, 2 bits green, 2 bits blue. unsigned char last_stipple; int state_cnt; int height,width; if (!gfxsize) return 0; // there is no picture data available. nothing to do. picnum&=0xffff; pPicture->pictureType=PICTURE_HALFTONE; // this format offers half tones. indexlen=READ_INT32BE(gfxbuf, 4); disk1len=READ_INT32BE(gfxbuf, 8); // disk2len=READ_INT32BE(gfxbuf,12); indexoffs=16; disk1offs=indexoffs+indexlen; disk2offs=disk1offs+disk1len; retval=0; // step 1: find the offset of the picture within the index. // the way it is stored is that the offsets within disk1 are stored in the first half, // and the offsets for disk2 are in the second half. // in case the offset is -1, it must be in the other one. offs1=(tVM68k_slong)READ_INT32LE(gfxbuf,indexoffs+picnum*4); offs2=(tVM68k_slong)READ_INT32LE(gfxbuf,indexoffs+indexlen/2+picnum*4); if (picnum!=30 && offs1!=-1 && offs2!=-1) offs2=-1; // in case one picture is stored on both disks, prefer the first one. if (picnum==30 && offs1==-1 && offs2==-1) offs1=0; // special case: the title screen for the GUILD of thieves is the first picture in DISK1.PIX if (offs1!=-1) offset=offs1+disk1offs; // in case the index was found in the first half, use disk1 else if (offs2!=-1) offset=offs2+disk2offs; // in case the index was found in the second half, use disk2 else return -1; /// otherwise: ERROR if (offset>gfxsize) // this is MYTH: there is only a single image file. { offset=offs1; } // the picture is stored in layers. // the first layer is a huffmann table. // this unpacks the second layer, which contains repitions // and a "stipple" table. from this, the actual pixels are being // calculated. huffsize=gfxbuf[offset+0]; unpackedsize=READ_INT16BE(gfxbuf,offset+huffsize+1); unpackedsize*=4; unpackedsize+=3; unpackedsize&=0xffff; // it was designed for 16 bit machines. pixelcnt=-1; unhufcnt=0; state=0; treeidx=0; mask=0; byteidx=offset+huffsize+2+1; // the beginning of the bitstream starts after the huffmann table and the unpackedsize byte=0; width=0; height=0; memset(xorbuf,0,sizeof(xorbuf)); // initialize the xor buffer with 0 state_cnt=0; max_stipple=last_stipple=0; while (unhufcnt>=1; // MSB first. if (branch&0x80) // leaves have the highest bit set. terminal symbols only have 7 bit. { treeidx=0; branch&=0x7f; // terminal symbols have 7 bit // // // the second layer begins here switch (state) { case 0: // first state: the ID should be "0x77" if (branch!=0x77) return -1; // illegal format state=1; break; case 1: // second byte is the number of "stipples" max_stipple=branch; state=2; break; case 2: // width, stored as 2*6 bit Big Endian width<<=6; // 2*6 bit. big endian; width|=branch&0x3f; state_cnt++; if (state_cnt==2) state=3; break; case 3: // height, stored as 2*6 bit Big Endian height<<=6; // 2*6 bit. big endian; height|=branch&0x3f; state_cnt++; if (state_cnt==4) { if (height<=0 || width<=0) return -2; // error in decoding the height and the width pixelcnt=0; state_cnt=0; state=4; } break; case 4: // rgb values rgbbuf[state_cnt++]=branch; if (state_cnt==16) { state_cnt=0; state=5; } break; case 5: // lookup-table to retrieve the left pixel value from the stipple pl_lut[state_cnt++]=branch; if (state_cnt==max_stipple) { state_cnt=0; state=6; } break; case 6: // lookup-table to retrieve the right pixel value from the stipple pr_lut[state_cnt++]=branch; if (state_cnt==max_stipple) { last_stipple=0; state_cnt=0; state=7; } break; case 7: case 8: // now for the stipple table // this is actually a third layer of encoding. // it contains terminal symbols [0... max_stipple) // // if the symbol is max_stipple, it means that the previous symbol is being repeated. n=0; if (state==8) // this character has been "escaped" { state=7; n=1; last_stipple=branch; } else if (branch=max_stipple. { // this is necessary for the XOR operation. state=8; n=0; } else if (branch>max_stipple) { n=branch-max_stipple; // repeat the previous stipple branch=last_stipple; } } for (i=0;ipixels[pixelcnt++]=pl_lut[x]; pPicture->pixels[pixelcnt++]=pr_lut[x]; } break; } } else { treeidx=branch; // non terminal -> traverse the tree further down } } pPicture->height=height; pPicture->width=width*2; // scale up the RGB values to 10bit per channel for (i=0;i<16;i++) { unsigned char halftonelut[4]={0,2,5,7}; unsigned int red,green,blue; red =(rgbbuf[i]>>4)&0x3; green=(rgbbuf[i]>>2)&0x3; blue =(rgbbuf[i]>>0)&0x3; red =(halftonelut[red]*PICTURE_MAX_RGB_VALUE)/7; green =(halftonelut[green]*PICTURE_MAX_RGB_VALUE)/7; blue =(halftonelut[blue]*PICTURE_MAX_RGB_VALUE)/7; pPicture->palette[i]=(red<<(2*PICTURE_BITS_PER_RGB_CHANNEL))|(green<<(1*PICTURE_BITS_PER_RGB_CHANNEL))|blue; } return retval; } // the gfx4 format is used to handle the pictures from the Magnetic Windows system. // just like the gfx2, it starts with a directory. 6 byte picname, 4 bytes (little endian) offset, 4 bytes (little endian) length. // at the offset, the picture is being comprised of the tree (type 7) and image (type 6) from the Magnetic Windows resource files. // the tree is ALWAYS 609 bytes long. The size of the image varies. // // the huffmann tree uses 9 bits to encode 8 bits: the first 32 bytes are a bitmask (MSB first). Then the branches/symbols follow. // if the bit from the bitmask is set, it is a terminal symbol. // 0x00...0x1f: LEFTMASK (0) // 0x20...0x11f: LEFTBRANCH // 0x120..0x13f: RIGHTMASK (1) // 0x140..0x23f: RIGHTBRANCH // Byte 0x240 escape character (for run level encoding) // Byte 0x241...0x250: EGA palette // Byte 0x251...0x260: EGA palette pairs // // // the image data looks like this: // 0x00..0x03: magic header // 0x04..0x23: 16x2 bytes RGB values (4 bits per channel, little endian, 0x0rgb) // 0x24..0x25: width // 0x26..0x27: height // 0x28..0x29: transparency placeholder // 0x2a..0x2b: size // 0x2c......: bit stream. MSB first. when the bit is set (=1), follow the RIGHT branch. // // the run level encoding is signalled by the escape character from the tree (byte 0x240). // in case the next character is 0xff, it actually an honest escape character. // if any other value follows, it is the repeat num. // the character AFTER that one is being repeated (4+repeat) number of times. // // each symbol is 8 bits large, 4 bits are the pixel. CAREFUL: bits 0..3 are the left, bits 4..7 are the right pixel // in case the width is not divisible by 2, bits 4..7 are being ignored. // // the xor is being performed line by line. // // note that the end of the image comes BEFORE the end of the bitstream. (due to a bug in the encoder) int gfxloader_gfx4(tVM68k_ubyte* gfxbuf,tVM68k_ulong gfxsize,char* picname,tdMagneticPicture* pPicture,int egamode) { #define SIZEOFTREE 609 int directorysize; int retval; int found; int i; int offset,length; int offset_vanilla,length_vanilla; int offset_ega,length_ega; int offset_anim,length_anim; int j; pPicture->width=0; pPicture->height=0; pPicture->pictureType=PICTURE_DEFAULT; // the gfx4 buffer starts with the magic value, and then a directory retval=0; found=0; directorysize=READ_INT16BE(gfxbuf,4); offset_ega=offset_anim=offset_vanilla=-1; length_ega=length_anim=length_vanilla=-1; for (i=0;i=15); stillimage=1; if (READ_INT32LE(gfxbuf,offset+length-4)!=STILLMAGIC) stillimage=0; // if they do not, it is an animation found=0; if (!ega && stillimage) { offset_vanilla=offset; length_vanilla=length; } if (ega) { offset_ega=offset; length_ega=length; } if (!stillimage) { offset_anim=offset; length_anim=length; } } } // in case the image was not found offset=-1; length=-1; if (offset==-1) { offset=offset_vanilla; length=length_vanilla; } if (offset==-1) { offset=offset_anim; length=length_anim; } if (offset==-1 || egamode) { offset=offset_ega; length=length_ega; } if (offset!=-1 && length!=-1) found=1; if (found) { int treestart; int picstart; int size; int treeidx; int repnum; int rlestate; // for the run length encoding. state 0: if not the escape char, output. otherwise -> state 1. in state 1, if the symbol is 0xff, the output is the escapechar. otherwise, the repition number (sans 4) -> state 4. in state 4, repeat the symbol tVM68k_ubyte escapechar; tVM68k_ubyte byte; tVM68k_ubyte mask; treestart=offset+0; picstart=offset+SIZEOFTREE; // byte 0x240 in the tree is the escape symbol for the run level encoding escapechar=gfxbuf[treestart+0x240]; // bytes 0x04..0x23: RGB values for (i=0;i<16;i++) { unsigned short s; unsigned int red,green,blue; s=READ_INT16LE(gfxbuf,picstart+0x4+i*2); red =(s>>8)&0xf; green =(s>>4)&0xf; blue =(s>>0)&0xf; red*=PICTURE_MAX_RGB_VALUE;green*=PICTURE_MAX_RGB_VALUE;blue*=PICTURE_MAX_RGB_VALUE; red/=7;green/=7;blue/=7; pPicture->palette[i]=(red<<(2*PICTURE_BITS_PER_RGB_CHANNEL))|(green<<(1*PICTURE_BITS_PER_RGB_CHANNEL))|blue; } // bytes 0x24,0x25= width // bytes 0x26,0x27= height pPicture->width=READ_INT16LE(gfxbuf,picstart+0x24); pPicture->height=READ_INT16LE(gfxbuf,picstart+0x26); if (pPicture->width>PICTURE_MAX_WIDTH) { retval=-1; return retval; } // bytes 0x2a,0x2b= size of the bitstream (in bytes) size=READ_INT16LE(gfxbuf,picstart+0x2a); j=0; treeidx=0; mask=0;byte=0; i=0; rlestate=0; repnum=1; // i is counting up the bytes in the bitstream. // j is counting up the pixels of the image while (((i<(length-SIZEOFTREE) && iwidth*pPicture->height)) { tVM68k_ubyte term0,term1,term; tVM68k_ubyte branch0,branch1,branch; // the bitmask is denoting (MSB first) if an entry is a terminal symbol (=1) or a branch (=0) term0=gfxbuf[treestart+0x00 +treeidx/8]&(0x80>>(treeidx%8)); term1=gfxbuf[treestart+0x120+treeidx/8]&(0x80>>(treeidx%8)); // the entry in the table could either be a branch or a terminal symbol branch0=gfxbuf[treestart+0x20 +treeidx]; branch1=gfxbuf[treestart+0x140+treeidx]; if (mask==0) { mask=0x80; byte=gfxbuf[picstart+i+0x2c]; i++; } term =(byte&mask)? term1:term0; branch=(byte&mask)?branch1:branch0; mask>>=1; if (term) { if (rlestate==0) { if (branch==escapechar) { rlestate=1; } else { repnum=1; } } else if (rlestate==1) { if (branch==0xff) { branch=escapechar; repnum=1; rlestate=0; } else { repnum=branch+4; // this form of RLE makes sense when the same byte was repeated 4 or more times in the source picture rlestate=2; } } else if (rlestate==2) { rlestate=0; // the current entry is a terminal symbol, which is going to be repeated } if (rlestate==0) { while (repnum && j<(pPicture->width*pPicture->height)) { // the lower 4 bits are the LEFT pixel pPicture->pixels[j]=branch&0xf; j++; // one byte holds two pixels. but when the width is not divisible by 2, drop the remaining nibble. if (j%pPicture->width) // the higher 4 bits are the RIGHT pixel; but only if it is not outside the scope of the image. { pPicture->pixels[j]=(branch>>4)&0xf; // the higher 4 bytes are the RIGHT pixel j++; } repnum--; } } treeidx=0; // go back to the start; } else { // not a terminal symbol. keep following the branches treeidx=branch; } } // the finishing touch: XOR each line with the previous one for (i=pPicture->width;iheight*pPicture->width;i++) { pPicture->pixels[i]^=pPicture->pixels[i-pPicture->width]; } } return retval; } // the Commodore C64 pictures // The c64 pictures consist of two parts: the bitmap and the colour map. essentially, each 8x8 block can be rendered with 4 colours. // 2 of them are fixed, the others are determined by the colourmap. int gfxloader_gfx5(unsigned char* gfxbuf,int gfxsize,int version,int picnum,tdMagneticPicture* pPicture) { #define C64_PICWIDTH 160 #define C64_PICHEIGHT 152 #define C64_PIXELPERBYTE (8/2) #define C64_BYTES_BITMAP (C64_PICWIDTH*C64_PICHEIGHT/C64_PIXELPERBYTE) #define C64_BYTES_BITMAP_PAWN ((C64_PICWIDTH*C64_PICHEIGHT/C64_PIXELPERBYTE)+64) // THE Pawn had a little bit padding #define C64_BYTES_COLOURMAP_V0 380 // #define C64_BYTES_COLOURMAP_V1 (2*C64_BYTES_COLOURMAP_V0) #define C64_BYTES_COLOURMAP_HIGH 760 #define C64_MAXBYTES_PICTURE_V0 (C64_BYTES_BITMAP+C64_BYTES_COLOURMAP_V0+C64_BYTES_COLOURMAP_HIGH) #define C64_MAXBYTES_PICTURE_V1 (C64_BYTES_BITMAP+C64_BYTES_COLOURMAP_V1+C64_BYTES_COLOURMAP_HIGH) #define C64_MAXBYTES_PICTURE C64_MAXBYTES_PICTURE_V1 #define C64_BYTES_IN_BITMAP (C64_PICWIDTH*C64_PICHEIGHT/C64_PIXELPERBYTE) // unsigned char tmpbuf[C64_MAXBYTES_PICTURE]; // maximum size for a picture. plus room for the threebuf unsigned char *tmpbuf=(unsigned char*)&(pPicture->pixels[C64_PICWIDTH*C64_PICHEIGHT]); unsigned char colour[4]={0}; int format; int i; int picoffs; int retval; // approximation of the fixed C64 palette with 10bit RGB values const unsigned int gfx5_rgbvalues[16]={ 0x00000000, // black 0x3fffffff, // white 0x205330e0, // red 0x1d5ceb22, // cyan 0x2393c25d, // violet 0x159ac934, // green 0x0b82c26d, // blue 0x3b6f19c5, // yellow 0x239500a4, // orange 0x15538000, // brown 0x3126c5c5, // light red 0x1284a128, // grey 1 0x1ed7b5ed, // grey 2 0x2a5ffe7d, // light green 0x1c16d7ae, // light blue 0x2cab2aca}; // grey 3 for (i=0;i<16;i++) { pPicture->palette[i]=gfx5_rgbvalues[i]; } retval=0; picoffs=READ_INT32BE(gfxbuf,4+4*picnum); if (picoffs<=0x00000000 || picoffs>gfxsize) { retval=-1; // the picture number was not correct return retval; } pPicture->width=C64_PICWIDTH; pPicture->height=C64_PICHEIGHT; pPicture->pictureType=PICTURE_C64; // only gfx3 offers halftone pictures format=0; ///////////// dehuff ///////////// { int outcnt; int expected; int treeidx; int bitcnt; int byteidx; int threecnt; int rlenum; int rlecnt; unsigned char rlebuf[256]={0}; unsigned char threebuf[3]={0}; unsigned char* ptr; unsigned char byte=0; unsigned char rlechar; treeidx=0; expected=C64_BYTES_BITMAP; outcnt=-1; bitcnt=0; ptr=&gfxbuf[picoffs]; byteidx=1+(ptr[0]+1)*2; // the first byte is the size of the huffmann tree. After the huffmann tree, the bit stream starts. threecnt=0; rlechar=0; rlenum=0; rlecnt=0; while (outcnt>4)&0xf; colour[2]=(tmpbuf[screenram_idx+colidx]>>0)&0xf; if (version!=0) { if (format==0x00) { colour[3]=tmpbuf[colourram_idx+colidx/2]; if ((colidx%2)==0) { colour[3]>>=4; } } else { colour[3]=tmpbuf[colourram_idx+colidx]; } colour[3]&=0xf; } for (i=0;i<8;i++) { int y2; unsigned char mask; y2=y+i; mask=tmpbuf[maskidx+i]; for (j=0;j<4;j++) { int x2; unsigned char col; x2=x+j; col=colour[(mask>>6)&0x3]; pPicture->pixels[x2+y2*(pPicture->width)]=col; mask<<=2; } } x+=4; if (x==C64_PICWIDTH) { x=0; y+=8; } } } retval|=gfxloader_twice_as_wide(pPicture); return retval; } // the Amstrad CPC pictures int gfxloader_gfx6(unsigned char* gfxbuf,int gfxsize,int picnum,tdMagneticPicture* pPicture) { const unsigned char gfx6_codebook[16]={0x00,0x40,0x04,0x44,0x10,0x50,0x14,0x54,0x01,0x41,0x05,0x45,0x11,0x51,0x15,0x55}; // approximation of the fixed CPC palette with 10bit RGB values const unsigned int gfx6_rgbvalues[27]={ 0x00000000,0x00000201,0x000003ff, 0x20100000,0x20100201,0x201003ff, 0x3ff00000,0x3ff00201,0x3ff003ff, 0x00080400,0x00080601,0x000807ff, 0x20180400,0x20180601,0x201807ff, 0x3ff80400,0x3ff80601,0x3ff807ff, 0x000ffc00,0x000ffe01,0x000fffff, 0x201ffc00,0x201ffe01,0x201fffff, 0x3ffffc00,0x3ffffe01,0x3fffffff }; int i; int paletteidx; int outidx; unsigned char byte; unsigned char mask; unsigned char symbol; unsigned char code; int bitidx; int toggle; int picoffs; int treeidx; int retval; retval=0; // find the index within the gfx buffer picoffs=READ_INT32BE(gfxbuf,4+picnum*4); if (picoffs==0x00000000 || picoffs>gfxsize) { retval=-1; // the picture number was not correct return retval; } treeidx=0; pPicture->height=152; // the size is always the same pPicture->width =160; paletteidx=0; outidx=0; byte=0; bitidx=picoffs+1+(gfxbuf[picoffs]+1)*2; mask=0; pPicture->palette[paletteidx++]=gfx6_rgbvalues[ 0]; // black pPicture->palette[paletteidx++]=gfx6_rgbvalues[26]; // bright white symbol=0; code=0; toggle=0; while ((outidx<(pPicture->height*pPicture->width))&& (bitidx>=1; if (branch&0x80) { treeidx=0; branch&=0x7f; if (paletteidx<16) // the first two colours are fixed. and the rest comes from the first 14 terminal symbols { pPicture->palette[paletteidx++]=gfx6_rgbvalues[branch]; // one of them } else { int loopcnt; loopcnt=1; if (branch&0x70) // if bits 6..4 are set, it is a loop. it determines how often the previous code is being repeated { loopcnt=branch-0x10; } else { // otherwise, it is a code code=gfx6_codebook[branch]; } for (i=0;iheight*pPicture->width);i++) { // the symbol is being combined from two codes symbol<<=1; symbol|=code; toggle=1-toggle; // when the symbol is finished if (toggle==0) { unsigned char p0; unsigned char p1; // the images are Amstrad Mode 0 pictures. Which means that the pixel bits are being interleaved in one byte. p0 =((symbol>>7)&0x1)<<0; p0|=((symbol>>3)&0x1)<<1; p0|=((symbol>>5)&0x1)<<2; p0|=((symbol>>1)&0x1)<<3; p1 =((symbol>>6)&0x1)<<0; p1|=((symbol>>2)&0x1)<<1; p1|=((symbol>>4)&0x1)<<2; p1|=((symbol>>0)&0x1)<<3; // at this point, the two pixels have been separated pPicture->pixels[outidx++]=p0; pPicture->pixels[outidx++]=p1; // prepare the next symbol symbol=0; } } } } else { treeidx=branch; } } // descramble the picture over two lines for (i=2*pPicture->width;iwidth*pPicture->height;i++) { pPicture->pixels[i]^=pPicture->pixels[i-2*pPicture->width]; } retval|=gfxloader_twice_as_wide(pPicture); return retval; } // Atari XL int gfxloader_gfx7(unsigned char* gfxbuf,int gfxsize,int picnum,tdMagneticPicture* pPicture) { int retval; int picoffs; int idx; unsigned char mask; unsigned char byte; unsigned char treesize; int treeidx; int treeoffs; int state; int rgbcnt; int rlenum; int rlecnt; unsigned char lc; unsigned char rlebuf[256]; unsigned char threebuf[3]; int threecnt; int pixcnt; int rlerep; int i; int blackcnt; retval=0; picoffs=READ_INT32BE(gfxbuf,4+4*picnum); if (picoffs==0x00000000 || picoffs>gfxsize) { retval=-1; return retval; } treesize=gfxbuf[picoffs]; byte=0; mask=0; treeidx=0; treeoffs=picoffs+1; // idx=idx+treesize*2+3; idx=picoffs+treesize*2+3; threecnt=0; rlenum=0; rlecnt=0; rgbcnt=0; pixcnt=0; state=0; pPicture->width=160; // the original images were stored as 160x152 pixels. However, they look better when being scaled up to 320x152 pPicture->height=152; blackcnt=0; lc=0; while (idxwidth*pPicture->height && state!=3) { unsigned char branch1,branch0,branch; if (mask==0) { byte=gfxbuf[idx++]; mask=0x80; } branch1=gfxbuf[treeoffs+treeidx*2+0]; branch0=gfxbuf[treeoffs+treeidx*2+1]; branch=(byte&mask)?branch1:branch0; mask>>=1; if (branch&0x80) { treeidx=branch&0x7f; } else { treeidx=0; if (threecnt!=3) { threebuf[threecnt++]=branch; } else { int j; for (i=0;i>4)&0xf; brightness=(c>>0)&0xf; red_dark =(gfx7_ataripalette[basecolor][0]>>(2*PICTURE_BITS_PER_RGB_CHANNEL))&0x3ff; green_dark =(gfx7_ataripalette[basecolor][0]>>(1*PICTURE_BITS_PER_RGB_CHANNEL))&0x3ff; blue_dark =(gfx7_ataripalette[basecolor][0]>>(0*PICTURE_BITS_PER_RGB_CHANNEL))&0x3ff; red_bright =(gfx7_ataripalette[basecolor][1]>>(2*PICTURE_BITS_PER_RGB_CHANNEL))&0x3ff; green_bright =(gfx7_ataripalette[basecolor][1]>>(1*PICTURE_BITS_PER_RGB_CHANNEL))&0x3ff; blue_bright =(gfx7_ataripalette[basecolor][1]>>(0*PICTURE_BITS_PER_RGB_CHANNEL))&0x3ff; r=red_dark +((red_bright -red_dark)*brightness)/NUM_BRIGHTNESSLEVELS; g=green_dark +((green_bright -green_dark)*brightness)/NUM_BRIGHTNESSLEVELS; b=blue_dark +((blue_bright -blue_dark)*brightness)/NUM_BRIGHTNESSLEVELS; rgb=(r<<(2*PICTURE_BITS_PER_RGB_CHANNEL))|(g<<(1*PICTURE_BITS_PER_RGB_CHANNEL))|b; } pPicture->palette[rgbcnt++]=rgb; if (c==0) blackcnt++; if (rgbcnt==NUM_BASECOLOURS) { if (treesize==0x3e) state=1; else state=2; if (blackcnt<12) state=3; } break; case 1: // rle lookup table if (rlenum==0) { rlenum=c; rlecnt=0; if (rlenum==128 || rlenum==1) rlenum=0; } else { rlebuf[rlecnt++]=c; } if (rlenum==rlecnt) state=2; break; case 2: // and the pixel information rlerep=0; for (j=0;jpixels[pixcnt++]=(lc>>6)&0x3; pPicture->pixels[pixcnt++]=(lc>>4)&0x3; pPicture->pixels[pixcnt++]=(lc>>2)&0x3; pPicture->pixels[pixcnt++]=(lc>>0)&0x3; } break; } } threecnt=0; } } } if (rlenum!=0) { for (i=pPicture->width*2;ipixels[i]^=pPicture->pixels[i-pPicture->width*2]; } } retval|=gfxloader_twice_as_wide(pPicture); return retval; } // Apple II picture loader int gfxloader_gfx8(unsigned char* gfxbuf,int gfxsize,int picnum,tdMagneticPicture* pPicture) { #define PICTURE_HOTFIX1 0x80000000 #define PICTURE_HOTFIX2 0x40000000 #define PICTURE_HOTFIX3 0x20000000 #define APPLE2_COLOURS 16 #define SIZE_AUX_MEM 8192 #define SIZE_MAIN_MEM 8192 #define APPLE_II_WIDTH 140 #define APPLE_II_HEIGHT (192-32) #define BITS_PER_SYMBOL 7 #define PIXELS_PER_WHATEVER 7 // 4 terminal symbols result in 7 pixels #define SYMBOLS_PER_LINE (APPLE_II_WIDTH/BITS_PER_SYMBOL) // approximation of the fixed Apple II palette with 10 bit RGB values const unsigned int gfx8_apple2_palette[APPLE2_COLOURS]={// 10 bit per channel 0x00000000, // black 0x1814e2f6, // dark blue 0x000a3581, // dark green 0x050cfbf6, // medium blue 0x1817240c, // brown 0x2719c671, // dark grey 0x050f58f0, // light green 0x1c9fff42, // aquamarin 0x38e1e181, // deep red 0x3ff443f6, // purple 0x2719c671, // light grey 0x342c3bff, // light blue 0x3ff6a4f0, // orange 0x3ffa0742, // pink 0x342dda35, // yellow 0x3fffffff // white }; int retval=0; int i=0; unsigned int picoffs=0; unsigned int treeoffs=0; int hotfix=0; int outidx=0; int treeidx=0; unsigned char lastterm=0; unsigned char mask=0; unsigned char byte=0; int bitidx=0; int oidx=0; unsigned int pixreg=0; int colcnt=0; int linecnt=0; retval=0; picoffs=READ_INT32BE(gfxbuf,4*picnum+4); treeoffs=picoffs; if (picoffs==0x0000000) { retval=-1; return retval; } for (i=0;ipalette[i]=gfx8_apple2_palette[i]; } pPicture->height=APPLE_II_HEIGHT; pPicture->width=APPLE_II_WIDTH; for (i=0;iheight*pPicture->width;i++) { pPicture->pixels[i]=0; } hotfix=(treeoffs&0xe0000000); if (hotfix==PICTURE_HOTFIX1) hotfix=-1; if (hotfix==PICTURE_HOTFIX2) hotfix= 1; if (hotfix==PICTURE_HOTFIX3) hotfix= 2; treeoffs&=0x1ffffff; treeoffs+=1; if (treeoffs>gfxsize) { retval=-1; return retval; } // step 1: unhuffing with the RLE outidx=0; treeidx=0; bitidx=treeoffs+gfxbuf[treeoffs-1]+2+hotfix; lastterm=1; mask=0; byte=0; // at the unhuffptr, there is now the content which would have // been written into the Apple II Videoram at $2000. // the first 8192 bytes are the AUX memory bank. // the second 8192 bytes are the MAIN memory bank oidx=0; colcnt=0; linecnt=0; while (outidx<(SIZE_AUX_MEM+SIZE_MAIN_MEM) && bitidx<=gfxsize) { unsigned char branch1,branch0; unsigned char branch=0; if (mask==0) { mask=0x80; byte=gfxbuf[bitidx++]; } branch1=gfxbuf[treeoffs+0+2*treeidx]; branch0=gfxbuf[treeoffs+1+2*treeidx]; branch=(byte&mask)?branch1:branch0;mask>>=1; if (branch&0x80) { unsigned char terminal; int n; terminal=branch&0x7f; if (lastterm==0 && outidx>3) { n=terminal-1; terminal=0; lastterm=1; } else { n=1; lastterm=terminal; } for (i=0;i=SIZE_AUX_MEM) // are we already in the MAIN memory? { // the first 8192 output bytes were meant for the AUX memory pixreg<<=BITS_PER_SYMBOL; // in MAIN memory, the other bits are written } if (colcnt==SYMBOLS_PER_LINE) // at the end of the line { colcnt=0; linecnt++; if ((linecnt&3)==3) // every 4th line does not exist { linecnt++; colcnt-=4; // skip 4 terminal words } // calculate the line address oidx =((linecnt>>0)&0x3)<<6; // bit 0,1 --> bit 6,7 oidx|=((linecnt>>2)&0x7)<<3; // bit 2,3,4 --> bit 3,4,5 oidx|=((linecnt>>5)&0x7)<<0; // bit 5,6,7 --> bit 0,1,2 // line->linear buffer oidx*=pPicture->width; } if (colcnt>=0) { // the pixel information is spread out over AUX and MAIN memory. // 4*7=28 bits are used to store 7 pixels. // the bits are being read LSB first, but only 7 bits of a byte are being used. // // A0..A6 are the bits from the first byte in the AUX memory. Starting at 0x0000 // B0..B6 are the bits from the second byte in the AUX memory. // M0..M6 are the bits from the first byte in the MAIN memory. Starting at 0x2000 // N0..N6 are the bits from the second byte in the MAIN memory. pPicture->pixels[oidx+ 0]|=(pixreg&0xf);pixreg>>=4; // A0 A1 A2 A3 pPicture->pixels[oidx+ 1]|=(pixreg&0xf);pixreg>>=4; // A4 A5 A6 M0 pPicture->pixels[oidx+ 2]|=(pixreg&0xf);pixreg>>=4; // M1 M2 M3 M4 pPicture->pixels[oidx+ 3]|=(pixreg&0xf);pixreg>>=4; // M5 M6 B0 B1 pPicture->pixels[oidx+ 4]|=(pixreg&0xf);pixreg>>=4; // B2 B3 B4 B5 pPicture->pixels[oidx+ 5]|=(pixreg&0xf);pixreg>>=4; // B6 N0 N1 N2 pPicture->pixels[oidx+ 6]|=(pixreg&0xf);pixreg>>=4; // N3 N4 N5 N6 oidx+=PIXELS_PER_WHATEVER; } colcnt++; } outidx++; if (outidx==SIZE_AUX_MEM) { linecnt=0; colcnt=0; oidx=0; } } treeidx=0; } else { treeidx=branch; } } retval|=gfxloader_twice_as_wide(pPicture); return retval; } int gfxloader_unpackpic(tVM68k_ubyte* gfxbuf,tVM68k_ulong gfxsize,tVM68k_ubyte version,int picnum,char* picname,tdMagneticPicture* pPicture,int egamode) { int retval; retval=0; picnum&=0x3f; // there are no more than 30 pictures in each game. except Wonderland. if (gfxbuf==NULL) return -1; if (gfxbuf[0]=='M' && gfxbuf[1]=='a' && gfxbuf[2]=='P' && pPicture!=NULL) { pPicture->width=pPicture->height=0; switch (gfxbuf[3]) // the header in the GFX files sets the format { case 'i': retval=gfxloader_gfx1(gfxbuf,gfxsize,picnum,pPicture);break; // standard .mag/gfx format case '2': retval=gfxloader_gfx2(gfxbuf,gfxsize,picname,pPicture);break; // taken from the magnetic windows .gfx files case '3': retval=gfxloader_gfx3(gfxbuf,gfxsize,picnum,pPicture);break; // ms dos case '4': retval=gfxloader_gfx4(gfxbuf,gfxsize,picname,pPicture,egamode);break; // read from magnetic windows resource files case '5': retval=gfxloader_gfx5(gfxbuf,gfxsize,version,picnum,pPicture);break; // C64 case '6': retval=gfxloader_gfx6(gfxbuf,gfxsize,picnum,pPicture);break; // Amstrad CPC case '7': retval=gfxloader_gfx7(gfxbuf,gfxsize,picnum,pPicture);break; // AtariXL case '8': retval=gfxloader_gfx8(gfxbuf,gfxsize,picnum,pPicture);break; // Apple II default: break; } } return retval; } dmagnetic-0.37/src/libdmagnetic/linea/gfxloader.h0000644000175000017500000000324214430741336020547 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GFXLOADER_H #define GFXLOADER_H #include "vm68k_datatypes.h" #include "dMagnetic.h" // for the pPicture data type // this function is extracting the picture from the .gfx file and converts it into something easier to handle. int gfxloader_unpackpic(tVM68k_ubyte* gfxbuf,tVM68k_ulong gfxsize,tVM68k_ubyte version,int picnum,char* picname,tdMagneticPicture* pPicture,int egamode); #endif dmagnetic-0.37/src/libdmagnetic/include/0000755000175000017500000000000014430741336016755 5ustar useruserdmagnetic-0.37/src/libdmagnetic/include/version.h0000644000175000017500000000261714430741336020621 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef VERSION_H #define VERSION_H #define VERSION_MAJOR 0 #define VERSION_MINOR 3 #define VERSION_REVISION 7 #endif dmagnetic-0.37/src/libdmagnetic/include/linea.h0000644000175000017500000000504314430741336020220 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LINEA_H #define LINEA_H #define LINEA_OK 0 #define LINEA_OK_QUIT 28844 #define LINEA_OK_RESTART 28816 #define LINEA_NOK_UNKNOWN_INSTRUCTION 1 #define LINEA_NOK_INVALID_PTR -1 #define LINEA_NOK_INVALID_PARAM -2 #define LINEA_NOK_NOT_ENOUGH_MEMORY -3 #define LINEA_NOK -23 #include "dMagnetic.h" // configuration functions int lineA_getsize(int* size); int lineA_init(void* hLineA,void* pSharedMem,int sharedmemsize,void* pMag,int magsize,void* pGfx,int gfxsize); int lineA_configrandom(void* hLineA,char random_mode,unsigned int random_seed); int lineA_setEGAMode(void* hLineA,int egamode); int lineA_dumppics(void* hLineA); int lineA_getVersion(void* hLineA,int* version); int lineA_setCBnewOutput(void* hLineA,cbLineANewOutput pCB,void* context); int lineA_setCBinputString(void* hLineA,cbLineAInputString pCB,void* context); int lineA_setCBdrawPicture(void* hLineA,cbLineADrawPicture pCB,void* context,tdMagneticPicture *pPicture); int lineA_setCBsaveGame(void* hLineA,cbLineASaveGame pCB,void* context); int lineA_setCBloadGame(void* hLineA,cbLineALoadGame pCB,void* context); int lineA_showTitleScreen(void* hLineA); // api int lineA_substitute_aliases(void* hLineA,unsigned short* opcode); int lineA_singlestep(void* hLineA,void* hVM68k,unsigned short opcode); #endif dmagnetic-0.37/src/libdmagnetic/include/vm68k.h0000644000175000017500000000403714430741336020105 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef VM68K_H #define VM68K_H // the return value is =0, when everything is okay. #define VM68K_OK 0 #define VM68K_NOK_UNKNOWN_INSTRUCTION 1 #define VM68K_NOK_INVALID_PTR -1 #define VM68K_NOK_INVALID_PARAMETER -2 // this function tells how much memory needs to be mallocated for the handle; int vm68k_getsize(int* size); // as the name suggests, pSharedMem is shared among the cores. it typically holds the game code. int vm68k_init(void* hVM68k,int version); int vm68k_singlestep(void *hVM68k,unsigned short opcode); int vm68k_getNextOpcode(void* hVM68k,unsigned short* opcode); int vm68k_getpSharedMem(void* hVM68k,void** pSharedMem,int* bytes); int vm68k_getState(void* hVM68k,unsigned int* aregs,unsigned int* dregs,unsigned int *pcr,unsigned int* sr); #endif dmagnetic-0.37/src/libdmagnetic/vm68k/0000755000175000017500000000000014430741336016305 5ustar useruserdmagnetic-0.37/src/libdmagnetic/vm68k/vm68k_loadstore.c0000644000175000017500000002574314430741336021513 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "vm68k.h" #include "vm68k_decode.h" #include "vm68k_datatypes.h" #include "vm68k_macros.h" #include "vm68k_loadstore.h" int vm68k_getbytesize(tVM68k_types size) { switch(size) { case VM68K_BYTE: return 1;break; case VM68K_WORD: return 2;break; case VM68K_LONG: return 4;break; default: return 0; } return 0; } // the way addresses are stored here is that memory addresses are >=0. <=0 addresses the registers. int vm68k_resolve_ea(tVM68k* pVM68k,tVM68k_next *pNext,tVM68k_types size, tVM68k_addrmodes addrmode,tVM68k_ubyte reg, tVM68k_uword legal,tVM68k_slong* ea) { tVM68k_sbyte bytesize; int retval; retval=VM68K_NOK_UNKNOWN_INSTRUCTION; bytesize=vm68k_getbytesize(size); if (addrmode==VM68K_AM_EXT) { switch ((tVM68k_addrmode_ext)reg) { case VM68K_AMX_W: if (legal&VM68K_LEGAL_AMX_W) { *ea=(tVM68k_sword)READEXTENSIONWORD(pVM68k,pNext); retval=VM68K_OK; } break; case VM68K_AMX_L: if (legal&VM68K_LEGAL_AMX_L) { *ea=(tVM68k_slong)READEXTENSIONLONG(pVM68k,pNext); retval=VM68K_OK; } break; case VM68K_AMX_data: if (legal&VM68K_LEGAL_AMX_DATA) { *ea=pNext->pcr; retval=VM68K_OK; switch (size) { case VM68K_BYTE: *ea+=1;pNext->pcr+=2;break; case VM68K_WORD: pNext->pcr+=2;break; case VM68K_LONG: pNext->pcr+=4;break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION; } } break; case VM68K_AMX_PC: if (legal&VM68K_LEGAL_AMX_PC) { *ea=READEXTENSIONWORD(pVM68k,pNext); *ea=(tVM68k_sword)(*ea)+pVM68k->pcr; retval=VM68K_OK; } break; case VM68K_AMX_INDEX_PC: if (legal&VM68K_LEGAL_AMX_INDEX_PC) { *ea=READEXTENSIONWORD(pVM68k,pNext); *ea=*ea+pVM68k->pcr; *ea=*ea+pVM68k->a[reg]*bytesize; // TODO: data or addrreg? retval=VM68K_NOK_UNKNOWN_INSTRUCTION; // TODO: lets decide when we stumble upon this mode } break; } } else { switch (addrmode) { case VM68K_AM_DATAREG: if (legal&VM68K_LEGAL_AM_DATAREG) { *ea=DATAREGADDR(reg); retval=VM68K_OK; } break; case VM68K_AM_ADDRREG: if (legal&VM68K_LEGAL_AM_ADDRREG) { *ea=ADDRREGADDR(reg); retval=VM68K_OK; } break; case VM68K_AM_INDIR: if (legal&VM68K_LEGAL_AM_INDIR) { *ea=(pVM68k->a[reg])%pVM68k->memsize; retval=VM68K_OK; } break; case VM68K_AM_POSTINC: if (legal&VM68K_LEGAL_AM_POSTINC) { *ea=pVM68k->a[reg]; pNext->a[reg]+=bytesize; retval=VM68K_OK; } break; case VM68K_AM_PREDEC: if (legal&VM68K_LEGAL_AM_PREDEC) { pNext->a[reg]-=bytesize; *ea=pNext->a[reg]; retval=VM68K_OK; } break; case VM68K_AM_DISP16: if (legal&VM68K_LEGAL_AM_DISP16) { *ea=(tVM68k_sword)READEXTENSIONWORD(pVM68k,pNext); *ea=(*ea)+pVM68k->a[reg]; retval=VM68K_OK; } break; case VM68K_AM_INDEX: if (legal&VM68K_LEGAL_AM_INDEX) { tVM68k_uword extword; // bit 15: =0 data, =1 addr reg // bit 14..12: regnum // bit 11: =0 index register is a signed word // =1 index register is a signed long // bit 10..8: UNKNOWN // bit 7..0: displacement, signed byte tVM68k_ubyte regX; tVM68k_sbyte displacement1; tVM68k_slong displacement2l; tVM68k_sword displacement2w; extword=(tVM68k_uword)READEXTENSIONWORD(pVM68k,pNext); regX=(extword>>12)&0x7; displacement1=(extword&0xff); if ((extword>>15)&1) { displacement2l=pVM68k->a[regX]; displacement2w=(pVM68k->a[regX]&0xffff); } else { displacement2l=pVM68k->d[regX]; displacement2w=(pVM68k->d[regX]&0xffff); } *ea=displacement1+(((extword>>11)&1)?displacement2l:displacement2w); *ea=(*ea)+pVM68k->a[reg]; retval=VM68K_OK; } break; default: retval=VM68K_NOK_INVALID_PTR;break; } } return retval; } // the way addresses are stored here is that memory addresses are >=0. <=0 addresses the registers. int vm68k_fetchoperand(tVM68k* pVM68k,tVM68k_bool extendsign,tVM68k_types size,tVM68k_slong ea,tVM68k_ulong* operand) { int retval; tVM68k_ulong op; op=0; if (ea>=0) // memory address { ea%=pVM68k->memsize; // just to be safe... retval=VM68K_OK; switch (size) { case VM68K_BYTE: op= READ_INT8BE(pVM68k->memory,ea);break; case VM68K_WORD: op=READ_INT16BE(pVM68k->memory,ea);break; case VM68K_LONG: op=READ_INT32BE(pVM68k->memory,ea);break; default: retval=VM68K_NOK_INVALID_PTR;break; } } else { // register address if (ea>=DATAREGADDR(7) && ea<=DATAREGADDR(0)) { op=pVM68k->d[-ea+DATAREGADDR(0)]; retval=VM68K_OK; } else if (ea>=ADDRREGADDR(7) && ea<=ADDRREGADDR(0)) { op=pVM68k->a[-ea+ADDRREGADDR(0)]; retval=VM68K_OK; } else retval=VM68K_NOK_UNKNOWN_INSTRUCTION; } switch (size) { case VM68K_BYTE: op&= 0xff;if (extendsign) op=(tVM68k_slong)((tVM68k_sbyte)op);break; case VM68K_WORD: op&=0xffff;if (extendsign) op=(tVM68k_slong)((tVM68k_sword)op);break; default: break; } *operand=op; return retval; } int vm68k_calculateflags(tVM68k_next* pNext,tVM68k_ubyte flagmask,tVM68k_types size,tVM68k_ulong operand1,tVM68k_ulong operand2,tVM68k_uint64 result) { tVM68k_ubyte msb; tVM68k_ulong mask; tVM68k_sint64 maxval,minval; tVM68k_sint64 res; int retval; retval=VM68K_OK; mask=0; msb=0; maxval=0; minval=0; res=0; switch (size) { case VM68K_BYTE: msb= 8;mask= 0xff;maxval= 0x7fll;minval= -0x80ll;res=((tVM68k_sint64)((tVM68k_sbyte)(result& 0xff)));break; case VM68K_WORD: msb=16;mask= 0xffff;maxval= 0x7fffll;minval= -0x8000ll;res=((tVM68k_sint64)((tVM68k_sbyte)(result& 0xffff)));break; case VM68K_LONG: msb=32;mask=0xffffffff;maxval=0x7fffffffll;minval=-0x80000000ll;res=((tVM68k_sint64)((tVM68k_sbyte)(result&0xffffffff)));break; default: retval=VM68K_NOK_INVALID_PTR;break; } if (flagmask&FLAGC) pNext->cflag=((operand2^result)>>msb)&1; if (flagmask&FLAGZ) pNext->zflag=((result&mask)==0); if (flagmask&FLAGN) pNext->nflag=(result>>(msb-1))&1; // if (flagmask&FLAGV) pNext->vflag=((operand1^operand2^result)>>(msb-1))&1; //if (flagmask&FLAGV) pNext->vflag=((~(operand1^operand2)^result)>>(msb-1))&1; if (flagmask&FLAGV) pNext->vflag=((res>maxval)||(resxflag=pNext->cflag; if (flagmask&FLAGCZCLR) {pNext->cflag=0;pNext->vflag=0;} return retval; } int vm68k_calculateflags2(tVM68k_next* pNext,tVM68k_ubyte flagmask,tVM68k_instruction instruction,tVM68k_types datatype,tVM68k_ulong operand1,tVM68k_ulong operand2,tVM68k_uint64 result) { tVM68k_bool msb1,msb2,msbres; int retval=VM68K_OK; msb1=msb2=msbres=0; switch(datatype) { case VM68K_BYTE: msb1=(operand1>> 7)&1;msb2=(operand2>> 7)&1;msbres=(result>> 7)&1;break; case VM68K_WORD: msb1=(operand1>>15)&1;msb2=(operand2>>15)&1;msbres=(result>>15)&1;break; case VM68K_LONG: msb1=(operand1>>31)&1;msb2=(operand2>>31)&1;msbres=(result>>31)&1;break; default: retval=VM68K_NOK_INVALID_PTR;break; } pNext->zflag=(result==0); pNext->nflag=(msbres); switch (instruction) { case VM68K_INST_ADD: case VM68K_INST_ADDA: case VM68K_INST_ADDI: case VM68K_INST_ADDQ: case VM68K_INST_ADDX: // sr[0] <= (`Sm & `Dm) | (~`Rm & `Dm) | (`Sm & ~`Rm); // sr[1] <= (`Sm & `Dm & ~`Rm) | (~`Sm & ~`Dm & `Rm); pNext->cflag=(msb1&msb2)|((!msbres)&msb2)|(msb1&(!msbres)); pNext->xflag=pNext->cflag; pNext->vflag=(msb1&msb2&(!msbres))|((!msb1)&(!msb2)&msbres); break; case VM68K_INST_SUB: case VM68K_INST_SUBA: case VM68K_INST_SUBI: case VM68K_INST_SUBQ: case VM68K_INST_SUBX: // sr[0] <= (`Sm & ~`Dm) | (`Rm & ~`Dm) | (`Sm & `Rm); // sr[1] <= (~`Sm & `Dm & ~`Rm) | (`Sm & ~`Dm & `Rm); pNext->cflag=(msb1&(!msb2))|(msbres&(!msb2))|(msb1&msbres); pNext->xflag=pNext->cflag; pNext->vflag=((!msb1)&msb2&(!msbres))|(msb1&(!msb2)&msbres); break; case VM68K_INST_CMP: case VM68K_INST_CMPA: case VM68K_INST_CMPI: case VM68K_INST_CMPM: // sr[0] <= (`Sm & ~`Dm) | (`Rm & ~`Dm) | (`Sm & `Rm); // sr[1] <= (~`Sm & `Dm & ~`Rm) | (`Sm & ~`Dm & `Rm); pNext->cflag=(msb1&(!msb2))|(msbres&(!msb2))|(msb1&msbres); pNext->vflag=((!msb1)&msb2&(!msbres))|(msb1&(!msb2)&msbres); break; case VM68K_INST_AND: case VM68K_INST_EOR: case VM68K_INST_OR: case VM68K_INST_ANDI: case VM68K_INST_EORI: case VM68K_INST_ORI: default: pNext->cflag=0; pNext->vflag=0; break; } return retval; } int vm68k_storeresult(tVM68k* pVM68k,tVM68k_next* pNext,tVM68k_types size,tVM68k_slong ea,tVM68k_ulong result) { int retval; tVM68k_ulong uppermask; tVM68k_ulong lowermask; retval=VM68K_NOK_UNKNOWN_INSTRUCTION; switch (size) { case VM68K_BYTE:uppermask=0xffffff00;lowermask=~uppermask;break; case VM68K_WORD:uppermask=0xffff0000;lowermask=~uppermask;break; case VM68K_LONG:uppermask=0x00000000;lowermask=~uppermask;break; default: return 0; } if (ea>=0) // memory address { retval=VM68K_OK; ea%=pVM68k->memsize; // just to be safe... pNext->mem_size=size; pNext->mem_addr[pNext->mem_we]=ea; pNext->mem_value[pNext->mem_we]=result&lowermask; pNext->mem_we++; } else { // register address if (ea>=DATAREGADDR(7) && ea<=DATAREGADDR(0)) { int reg; reg=-ea+DATAREGADDR(0); pNext->d[reg]&=uppermask; pNext->d[reg]|=(result&lowermask); retval=VM68K_OK; } else if (ea>=ADDRREGADDR(7) && ea<=ADDRREGADDR(0)) { int reg; reg=-ea+ADDRREGADDR(0); pNext->a[reg]&=uppermask; pNext->a[reg]|=(result&lowermask); retval=VM68K_OK; } } return retval; } dmagnetic-0.37/src/libdmagnetic/vm68k/vm68k_macros.h0000644000175000017500000001453314430741336021003 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef VM68K_MACROS_H #define VM68K_MACROS_H #include "vm68k_datatypes.h" // the gfx2 format introduced MIXED ENDIAN. #define READ_INT32ME(ptr,idx) (\ (((tVM68k_ulong)((ptr)[((idx)+1)])&0xff)<<24) |\ (((tVM68k_ulong)((ptr)[((idx)+0)])&0xff)<<16) |\ (((tVM68k_ulong)((ptr)[((idx)+3)])&0xff)<< 8) |\ (((tVM68k_ulong)((ptr)[((idx)+2)])&0xff)<< 0) |\ 0) #define READ_INT32BE(ptr,idx) (\ (((tVM68k_ulong)((ptr)[((idx)+0)])&0xff)<<24) |\ (((tVM68k_ulong)((ptr)[((idx)+1)])&0xff)<<16) |\ (((tVM68k_ulong)((ptr)[((idx)+2)])&0xff)<< 8) |\ (((tVM68k_ulong)((ptr)[((idx)+3)])&0xff)<< 0) |\ 0) #define READ_INT16BE(ptr,idx) (\ (((tVM68k_ulong)((ptr)[((idx)+0)])&0xff)<< 8) |\ (((tVM68k_ulong)((ptr)[((idx)+1)])&0xff)<< 0) |\ 0) #define READ_INT8BE(ptr,idx) (\ (((tVM68k_ulong)((ptr)[((idx)+0)])&0xff)<< 0) |\ 0) #define READ_INT32LE(ptr,idx) (\ (((unsigned int)((ptr)[((idx)+3)])&0xff)<<24) |\ (((unsigned int)((ptr)[((idx)+2)])&0xff)<<16) |\ (((unsigned int)((ptr)[((idx)+1)])&0xff)<< 8) |\ (((unsigned int)((ptr)[((idx)+0)])&0xff)<< 0) |\ 0) #define READ_INT16LE(ptr,idx) (\ (((unsigned int)((ptr)[((idx)+1)])&0xff)<< 8) |\ (((unsigned int)((ptr)[((idx)+0)])&0xff)<< 0) |\ 0) #define READ_INT8LE(ptr,idx) (\ (((unsigned int)((ptr)[((idx)+0)])&0xff)<< 0) |\ 0) #define WRITE_INT32BE(ptr,idx,val) {\ (ptr)[(idx)+3]=((tVM68k_ubyte)((val)>> 0)&0xff); \ (ptr)[(idx)+2]=((tVM68k_ubyte)((val)>> 8)&0xff); \ (ptr)[(idx)+1]=((tVM68k_ubyte)((val)>>16)&0xff); \ (ptr)[(idx)+0]=((tVM68k_ubyte)((val)>>24)&0xff); \ } #define WRITE_INT16BE(ptr,idx,val) {\ (ptr)[(idx)+1]=((tVM68k_ubyte)((val)>> 0)&0xff); \ (ptr)[(idx)+0]=((tVM68k_ubyte)((val)>> 8)&0xff); \ } #define WRITE_INT8BE(ptr,idx,val) {\ (ptr)[(idx)+0]=((tVM68k_ubyte)((val)>> 0)&0xff); \ } #define WRITE_INT32LE(ptr,idx,val) {\ (ptr)[(idx)+0]=((unsigned char)((val)>> 0)&0xff); \ (ptr)[(idx)+1]=((unsigned char)((val)>> 8)&0xff); \ (ptr)[(idx)+2]=((unsigned char)((val)>>16)&0xff); \ (ptr)[(idx)+3]=((unsigned char)((val)>>24)&0xff); \ } #define WRITE_INT16LE(ptr,idx,val) {\ (ptr)[(idx)+0]=((unsigned char)((val)>> 0)&0xff); \ (ptr)[(idx)+1]=((unsigned char)((val)>> 8)&0xff); \ } #define WRITE_INT8LE(ptr,idx,val) {\ (ptr)[(idx)+0]=((unsigned char)((val)>> 0)&0xff); \ } #define INITNEXT(pVM68k,next) \ (next).pcr=(pVM68k)->pcr; \ (next).a[0]=(pVM68k)->a[0]; \ (next).a[1]=(pVM68k)->a[1]; \ (next).a[2]=(pVM68k)->a[2]; \ (next).a[3]=(pVM68k)->a[3]; \ (next).a[4]=(pVM68k)->a[4]; \ (next).a[5]=(pVM68k)->a[5]; \ (next).a[6]=(pVM68k)->a[6]; \ (next).a[7]=(pVM68k)->a[7]; \ (next).d[0]=(pVM68k)->d[0]; \ (next).d[1]=(pVM68k)->d[1]; \ (next).d[2]=(pVM68k)->d[2]; \ (next).d[3]=(pVM68k)->d[3]; \ (next).d[4]=(pVM68k)->d[4]; \ (next).d[5]=(pVM68k)->d[5]; \ (next).d[6]=(pVM68k)->d[6]; \ (next).d[7]=(pVM68k)->d[7]; \ (next).override_sr=0; \ (next).sr=((pVM68k)->sr); \ (next).cflag=((pVM68k)->sr>>0)&1; \ (next).vflag=((pVM68k)->sr>>1)&1; \ (next).zflag=((pVM68k)->sr>>2)&1; \ (next).nflag=((pVM68k)->sr>>3)&1; \ (next).xflag=((pVM68k)->sr>>4)&1; \ (next).mem_we=0; \ (next).mem_addr[0]=0; \ (next).mem_size=0; \ (next).mem_value[0]=0; #define WRITEFLAGS(pVM68k,transaction) \ (pVM68k)->sr|=(tVM68k_uword)((transaction).cflag)<<0; \ (pVM68k)->sr|=(tVM68k_uword)((transaction).vflag)<<1; \ (pVM68k)->sr|=(tVM68k_uword)((transaction).zflag)<<2; \ (pVM68k)->sr|=(tVM68k_uword)((transaction).nflag)<<3; \ (pVM68k)->sr|=(tVM68k_uword)((transaction).xflag)<<4; #define READEXTENSIONBYTE(pVM68k,pNext) READ_INT8BE((pVM68k)->memory,(pNext)->pcr+1);(pNext)->pcr+=2; #define READEXTENSIONWORD(pVM68k,pNext) READ_INT16BE((pVM68k)->memory,(pNext)->pcr);(pNext)->pcr+=2; #define READEXTENSIONLONG(pVM68k,pNext) READ_INT32BE((pVM68k)->memory,(pNext)->pcr);(pNext)->pcr+=4; #define READEXTENSION(pVM68k,pNext,datatype,operand) \ switch (datatype) \ { \ case VM68K_BYTE: operand=READEXTENSIONBYTE(pVM68k,pNext);break; \ case VM68K_WORD: operand=READEXTENSIONWORD(pVM68k,pNext);break; \ case VM68K_LONG: operand=READEXTENSIONLONG(pVM68k,pNext);break; \ default: operand=0;break; \ } #define READSIGNEDEXTENSION(pVM68k,pNext,datatype,operand) \ switch (datatype) \ { \ case VM68K_BYTE: operand=READEXTENSIONBYTE(pVM68k,pNext);operand=(tVM68k_slong)((tVM68k_sbyte)((operand)& 0xff));break; \ case VM68K_WORD: operand=READEXTENSIONBYTE(pVM68k,pNext);operand=(tVM68k_slong)((tVM68k_sword)((operand)&0xffff));break; \ case VM68K_LONG: operand=READEXTENSIONLONG(pVM68k,pNext);break; \ } #define PUSHWORDTOSTACK(pVM68k,pNext,x) {(pNext)->a[7]-=2;(pNext)->mem_addr[(pNext)->mem_we]=(pNext)->a[7];(pNext)->mem_size=VM68K_WORD;(pNext)->mem_value[(pNext)->mem_we]=x;(pNext)->mem_we++;} #define PUSHLONGTOSTACK(pVM68k,pNext,x) {(pNext)->a[7]-=4;(pNext)->mem_addr[(pNext)->mem_we]=(pNext)->a[7];(pNext)->mem_size=VM68K_LONG;(pNext)->mem_value[(pNext)->mem_we]=x;(pNext)->mem_we++;} #define POPWORDFROMSTACK(pVM68k,pNext,x) {tVM68k_uword y;y=READ_INT16BE((pVM68k)->memory,(pNext)->a[7]);(pNext)->a[7]+=2;x=((x)&0xffff0000)|(y&0xffff);} #define POPLONGFROMSTACK(pVM68k,pNext,x) {x=READ_INT32BE((pVM68k)->memory,(pNext)->a[7]);(pNext)->a[7]+=4;} #endif dmagnetic-0.37/src/libdmagnetic/vm68k/vm68k_decode.c0000644000175000017500000003035014430741336020730 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "vm68k_datatypes.h" #include "vm68k_decode.h" // the purpose of this function is to perform a pattern matching to the instruction, and return the enumeration value. // the more bits are constant, the higher should be the matche's priority. tVM68k_instruction vm68k_decode(tVM68k_uword opcode) { // instructions with 16 constant bits if ((opcode&0xffff)==0x023c) return VM68K_INST_ANDItoCCR; //ANDItoCCR: 0000 0010 0011 1100 00000000dddddddd if ((opcode&0xffff)==0x027c) return VM68K_INST_ANDItoSR; //ANDItoSR: 0000 0010 0111 1100 dddddddddddddddd if ((opcode&0xffff)==0x0A3C) return VM68K_INST_EORItoCCR; //EORItoCCR: 0000 1010 0011 1100 00000000dddddddd if ((opcode&0xffff)==0x0A7C) return VM68K_INST_EORItoSR; //EORItoSR: 0000 1010 0111 1100 dddddddddddddddd if ((opcode&0xffff)==0x4AFC) return VM68K_INST_ILLEGAL; //ILLEGAL: 0100 1010 1111 1100 if ((opcode&0xffff)==0x4E71) return VM68K_INST_NOP; if ((opcode&0xffff)==0x003C) return VM68K_INST_ORItoCCR; //ORItoCCR: 0000 0000 0011 1100 00000000dddddddd if ((opcode&0xffff)==0x007C) return VM68K_INST_ORItoSR; //ORItoSR: 0000 0000 0111 1100 dddddddddddddddd if ((opcode&0xffff)==0x4E70) return VM68K_INST_RESET; //RESET: 0100 1110 0111 0000 if ((opcode&0xffff)==0x4E73) return VM68K_INST_RTE; //RTE: 0100 1110 0111 0011 if ((opcode&0xffff)==0x4E77) return VM68K_INST_RTR; //RTR: 0100 1110 0111 0111 if ((opcode&0xffff)==0x4E75) return VM68K_INST_RTS; //RTS: 0100 1110 0111 0101 if ((opcode&0xffff)==0x4E72) return VM68K_INST_STOP; //STOP: 0100 1110 0111 0010 iiiiiiiiiiiiiiii if ((opcode&0xffff)==0x4E76) return VM68K_INST_TRAPV; //TRAPV: 0100 1110 0111 0110 // instructions with 13 constant bits if ((opcode&0xfff8)==0x4E50) return VM68K_INST_LINK; //LINK: 0100 1110 0101 0yyy dddddddddddddddd if ((opcode&0xfff8)==0x4840) return VM68K_INST_SWAP; //SWAP: 0100 1000 0100 0yyy if ((opcode&0xfff8)==0x4E58) return VM68K_INST_UNLK; //UNLK: 0100 1110 0101 1yyy // instructions with 12 constant bits if ((opcode&0xfff0)==0x4E40) return VM68K_INST_TRAP; //TRAP: 0100 1110 0100 vvvv if ((opcode&0xfff0)==0x4E60) return VM68K_INST_MOVEUSP; //MOVE USP: 0100 1110 0110 dyyy // instructions with 10 constant bits if ((opcode&0xffc0)==0x0800) return VM68K_INST_BTSTB; //BTST.B: 0000 1000 00mm myyy 0000 0000 bbbb bbbb if ((opcode&0xffc0)==0x0840) return VM68K_INST_BCHGB; //BCHG.B: 0000 1000 01mm myyy if ((opcode&0xffc0)==0x0880) return VM68K_INST_BCLRI; //BCLRI: 0000 1000 10mm myyy if ((opcode&0xffc0)==0x08C0) return VM68K_INST_BSETB; //BSET.B: 0000 1000 11mm myyy if ((opcode&0xffc0)==0x44C0) return VM68K_INST_MOVEtoCCR; //MOVEtoCCR: 0100 0100 11mm myyy if ((opcode&0xffc0)==0x40C0) return VM68K_INST_MOVEfromSR; //MOVEfromSR:0100 0000 11mm myyy if ((opcode&0xffc0)==0x46C0) return VM68K_INST_MOVEtoSR; //MOVEtoSR: 0100 0110 11mm myyy if ((opcode&0xffc0)==0x4840) return VM68K_INST_PEA; //PEA: 0100 1000 01mm myyy if ((opcode&0xffc0)==0x4AC0) return VM68K_INST_TAS; //TAS: 0100 1010 11mm myyy if ((opcode&0xffC0)==0x4EC0) return VM68K_INST_JMP; //JMP: 0100 1110 11mm myyy if ((opcode&0xffC0)==0x4E80) return VM68K_INST_JSR; //JSR: 0100 1110 10mm myyy if ((opcode&0xfe38)==0x4800) return VM68K_INST_EXT; //EXT: 0100 100o oo00 0yyy // instructions with 9 constant bits if ((opcode&0xf0f8)==0x50C8) return VM68K_INST_DBcc; //DBcc: 0101 CCCC 1100 1yyy if ((opcode&0xf1f0)==0xC100) return VM68K_INST_ABCD; //ABCD: 1100 xxx1 0000 myyy if ((opcode&0xf1f0)==0x8100) return VM68K_INST_SBCD; //SBCD: 1000 xxx1 0000 ryyy if ((opcode&0xff80)==0x4880) return VM68K_INST_MOVEMregtomem; //MOVEM: 0100 1000 1smm myyy // reg to mem if ((opcode&0xff80)==0x4C80) return VM68K_INST_MOVEMmemtoreg; //MOVEM: 0100 1100 1smm myyy // mem to reg if ((opcode&0xf1C0)==0x01C0) return VM68K_INST_BSET; //BSET: 0000 xxx1 11mm myyy // instructions with 8 constant bits if ((opcode&0xff00)==0x4200) return VM68K_INST_CLR; //CLR: 0100 0010 ssmm myyy if ((opcode&0xff00)==0x0C00) return VM68K_INST_CMPI; //CMPI: 0000 1100 ssmm myyy if ((opcode&0xff00)==0x0A00) return VM68K_INST_EORI; //EORI: 0000 1010 ssmm myyy if ((opcode&0xff00)==0x0600) return VM68K_INST_ADDI; //ADDI: 0000 0110 ssmm myyy if ((opcode&0xff00)==0x0200) return VM68K_INST_ANDI; //ANDI: 0000 0010 ssmm myyy if ((opcode&0xff00)==0x0000) return VM68K_INST_ORI; //ORI: 0000 0000 ssmm myyy if ((opcode&0xff00)==0x4400) return VM68K_INST_NEG; //NEG: 0100 0100 ssmm myyy if ((opcode&0xff00)==0x4000) return VM68K_INST_NEGX; //NEGX: 0100 0000 ssmm myyy if ((opcode&0xff00)==0x4600) return VM68K_INST_NOT; //NOT: 0100 0110 ssmm myyy if ((opcode&0xff00)==0x0400) return VM68K_INST_SUBI; //SUBI: 0000 0100 ssmm myyy if ((opcode&0xff00)==0x4A00) return VM68K_INST_TST; //TST: 0100 1010 ssmm myyy if ((opcode&0xf0C0)==0xD0C0) return VM68K_INST_ADDA; //ADDA: 1101 rrrs 11mm myyy // IMPORTANT! THIS HAS TO COME BEFORE ADDX! if ((opcode&0xf130)==0xD100) return VM68K_INST_ADDX; //ADDX: 1101 xxx1 ss00 myyy // s=00,01,10=ADDX. 11=ADDA!! if ((opcode&0xf130)==0xC100) return VM68K_INST_EXG; //EXG: 1100 xxx1 oo00 oyyy if ((opcode&0xf0C0)==0x90C0) return VM68K_INST_SUBA; //SUBA: 1001 xxxo 11mm myyy // probably the same problem as ADDA/ADDX. if ((opcode&0xf130)==0x9100) return VM68K_INST_SUBX; //SUBX: 1001 yyy1 ss00 ryyy if ((opcode&0xf0C0)==0xB0C0) return VM68K_INST_CMPA; //CMPA: 1011 xxxo 11mm myyy /// IMPORANT! THIS HAS TO COME BEFORE CMPM! if ((opcode&0xf138)==0xb108) return VM68K_INST_CMPM; //CMPM: 1011 xxx1 ss00 1yyy // instructions with 7 constant bits if ((opcode&0xf1c0)==0x0140) return VM68K_INST_BCHG; //BCHG: 0000 rrr1 01mm myyy if ((opcode&0xf1c0)==0x0180) return VM68K_INST_BCLR; //BCLR: 0000 xxx1 10mm myyy if ((opcode&0xf1C0)==0x0100) return VM68K_INST_BTST; //BTST: 0000 xxx1 00mm myyy if ((opcode&0xf1C0)==0x81C0) return VM68K_INST_DIVS; //DIVS: 1000 xxx1 11mm myyy if ((opcode&0xf1C0)==0x80C0) return VM68K_INST_DIVU; //DIVU: 1000 xxx0 11mm myyy if ((opcode&0xf1C0)==0xC1C0) return VM68K_INST_MULS; //MULS: 1100 xxx1 11mm myyy if ((opcode&0xf1C0)==0xC0C0) return VM68K_INST_MULU; //MULU: 1100 xxx0 11mm myyy if ((opcode&0xf1C0)==0x41C0) return VM68K_INST_LEA; //LEA: 0100 xxx1 11mm myyy if ((opcode&0xf038)==0x0008) return VM68K_INST_MOVEP; //MOVEP: 0000 xxxo oo00 1yyy dddddddddddddddd // instructions with 6 constant bits if ((opcode&0xf018)==0xe018) return VM68K_INST_ROL_ROR; //ROL/ROR: 1110 cccd ssl1 1yyy if ((opcode&0xf018)==0xe010) return VM68K_INST_ROXL_ROXR; //ROXL/ROXR: 1110 cccd ssl1 0yyy if ((opcode&0xf0c0)==0x50C0) return VM68K_INST_SCC; //SCC: 0101 CCCC 11mm myyy if ((opcode&0xf140)==0x4100) return VM68K_INST_CHK; //CHK: 0100 xxx1 s0mm myyy if ((opcode&0xf018)==0xE008) return VM68K_INST_LSL_LSR; //LSL/LSR: 1110 cccd ssl0 1yyy if ((opcode&0xf018)==0xE000) return VM68K_INST_ASL_ASR; //ASL/ASR: 1110 cccd ssl0 0yyy // instructions with 5 constant bits if ((opcode&0xf100)==0x5000) return VM68K_INST_ADDQ; //ADDQ: 0101 ddd0 ssmm myyy if ((opcode&0xf100)==0xb100) return VM68K_INST_EOR; //EOR: 1011 xxx1 oomm myyy if ((opcode&0xf100)==0x7000) return VM68K_INST_MOVEQ; //MOVEQ: 0111 xxx0 dddd dddd if ((opcode&0xf000)==0x9000) return VM68K_INST_SUB; //SUB: 1001 xxx0 oomm myyy if ((opcode&0xf100)==0x5100) return VM68K_INST_SUBQ; //SUBQ: 0101 ddd1 ssmm myyy if ((opcode&0xC1C0)==0x0040) return VM68K_INST_MOVEA; //MOVEA: 00ss xxx0 01mm myyy // instructions with 4 constant bits // if ((opcode&0xf000)==0xD000) return VM68K_INST_ADD; //ADD: 1101 rrro oomm myyy if ((opcode&0xf000)==0xC000) return VM68K_INST_AND; //AND: 1100 xxxo oomm myyy if ((opcode&0xf000)==0x6000) return VM68K_INST_BCC; //BCC: 0110 CCCC dddd dddd if ((opcode&0xf000)==0xB000) return VM68K_INST_CMP; //CMP: 1011 xxx0 oomm myyy if ((opcode&0xf000)==0x8000) return VM68K_INST_OR; //OR: 1000 xxxo oomm myyy // instructions with 2 constant bits if ((opcode&0xc000)==0x0000) return VM68K_INST_MOVE; //MOVE: 00ss xxxm mmMM Myyy return VM68K_INST_UNKNOWN; } #ifdef DEBUG_PRINT void vm68k_get_instructionname(tVM68k_instruction instruction,char* name) { #define INSTFOUND(x) case x: snprintf(name,64,#x); break; switch(instruction) { default: snprintf(name,64,"???"); break; INSTFOUND(VM68K_INST_UNKNOWN) INSTFOUND(VM68K_INST_ABCD) INSTFOUND(VM68K_INST_ADD) INSTFOUND(VM68K_INST_ADDA) INSTFOUND(VM68K_INST_ADDI) INSTFOUND(VM68K_INST_ADDQ) INSTFOUND(VM68K_INST_ADDX) INSTFOUND(VM68K_INST_AND) INSTFOUND(VM68K_INST_ANDI) INSTFOUND(VM68K_INST_ANDItoCCR) INSTFOUND(VM68K_INST_ANDItoSR) INSTFOUND(VM68K_INST_ASL_ASR) INSTFOUND(VM68K_INST_BCC) INSTFOUND(VM68K_INST_BCHG) INSTFOUND(VM68K_INST_BCHGB) INSTFOUND(VM68K_INST_BCLR) INSTFOUND(VM68K_INST_BCLRI) INSTFOUND(VM68K_INST_BRA) INSTFOUND(VM68K_INST_BSET) INSTFOUND(VM68K_INST_BSETB) //INSTFOUND(VM68K_INST_BSR) INSTFOUND(VM68K_INST_BTST) INSTFOUND(VM68K_INST_BTSTB) INSTFOUND(VM68K_INST_CHK) INSTFOUND(VM68K_INST_CLR) INSTFOUND(VM68K_INST_CMP) INSTFOUND(VM68K_INST_CMPA) INSTFOUND(VM68K_INST_CMPI) INSTFOUND(VM68K_INST_CMPM) INSTFOUND(VM68K_INST_DBcc) INSTFOUND(VM68K_INST_DIVS) INSTFOUND(VM68K_INST_DIVU) INSTFOUND(VM68K_INST_EOR) INSTFOUND(VM68K_INST_EORI) INSTFOUND(VM68K_INST_EORItoCCR) INSTFOUND(VM68K_INST_EORItoSR) INSTFOUND(VM68K_INST_EXG) INSTFOUND(VM68K_INST_EXT) INSTFOUND(VM68K_INST_ILLEGAL) INSTFOUND(VM68K_INST_JMP) INSTFOUND(VM68K_INST_JSR) INSTFOUND(VM68K_INST_LEA) INSTFOUND(VM68K_INST_LINK) INSTFOUND(VM68K_INST_LSL_LSR) INSTFOUND(VM68K_INST_MOVE) INSTFOUND(VM68K_INST_MOVEA) INSTFOUND(VM68K_INST_MOVEtoCCR) INSTFOUND(VM68K_INST_MOVEfromSR) INSTFOUND(VM68K_INST_MOVEtoSR) INSTFOUND(VM68K_INST_MOVEUSP) INSTFOUND(VM68K_INST_MOVEMregtomem) INSTFOUND(VM68K_INST_MOVEMmemtoreg) INSTFOUND(VM68K_INST_MOVEP) INSTFOUND(VM68K_INST_MOVEQ) INSTFOUND(VM68K_INST_MULS) INSTFOUND(VM68K_INST_MULU) INSTFOUND(VM68K_INST_NBCD) INSTFOUND(VM68K_INST_NEG) INSTFOUND(VM68K_INST_NEGX) INSTFOUND(VM68K_INST_NOP) INSTFOUND(VM68K_INST_NOT) INSTFOUND(VM68K_INST_OR) INSTFOUND(VM68K_INST_ORI) INSTFOUND(VM68K_INST_ORItoCCR) INSTFOUND(VM68K_INST_ORItoSR) INSTFOUND(VM68K_INST_PEA) INSTFOUND(VM68K_INST_RESET) INSTFOUND(VM68K_INST_ROL_ROR) INSTFOUND(VM68K_INST_ROXL_ROXR) INSTFOUND(VM68K_INST_RTE) INSTFOUND(VM68K_INST_RTR) INSTFOUND(VM68K_INST_RTS) INSTFOUND(VM68K_INST_SBCD) INSTFOUND(VM68K_INST_SCC) INSTFOUND(VM68K_INST_STOP) INSTFOUND(VM68K_INST_SUB) INSTFOUND(VM68K_INST_SUBA) INSTFOUND(VM68K_INST_SUBI) INSTFOUND(VM68K_INST_SUBQ) INSTFOUND(VM68K_INST_SUBX) INSTFOUND(VM68K_INST_SWAP) INSTFOUND(VM68K_INST_TAS) INSTFOUND(VM68K_INST_TRAP) INSTFOUND(VM68K_INST_TRAPV) INSTFOUND(VM68K_INST_TST) INSTFOUND(VM68K_INST_UNLK) } } #endif dmagnetic-0.37/src/libdmagnetic/vm68k/vm68k_decode.h0000644000175000017500000001255214430741336020741 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef VM68K_DECODE_H #define VM68K_DECODE_H //#define DEBUG_PRINT #include "vm68k_datatypes.h" // this data structure makes the decoding process easier to read. typedef enum _tVM68k_instruction { VM68K_INST_UNKNOWN=0, VM68K_INST_ABCD, //1100xxx10000myyy VM68K_INST_ADD, //1101 rrro oomm myyy VM68K_INST_ADDA, //1101 rrro oomm myyy VM68K_INST_ADDI, //0000 0110 ssmm myyy VM68K_INST_ADDQ, //0101 ddd0 ssmm myyy VM68K_INST_ADDX, //1101 xxx1 ss00 myyy VM68K_INST_AND, //1100 xxxo oomm myyy VM68K_INST_ANDI, //0000 0010 ssmm myyy VM68K_INST_ANDItoCCR, //0000 0010 0011 1100 00000000dddddddd VM68K_INST_ANDItoSR, //0000 0010 0111 1100 dddddddddddddddd VM68K_INST_ASL_ASR, //1110 cccd ssl0 0yyy VM68K_INST_BCC, //0110 CCCC dddd dddd VM68K_INST_BCHG, //0000 xxx1 01mm myyy VM68K_INST_BCHGB, //0000 1000 10mm myyy VM68K_INST_BCLR, //0000 xxx1 10mm myyy VM68K_INST_BCLRI, //0000xxx110mmmyyy VM68K_INST_BRA, //0110 0000 dddd dddd VM68K_INST_BSET, //0000 xxx1 11mm myyy VM68K_INST_BSETB, //0000 1000 11mm myyy // VM68K_INST_BSR, //01100001dddddddd VM68K_INST_BTST, //0000 xxx1 00mm myyy VM68K_INST_BTSTB, //0000 1000 00mm myyy VM68K_INST_CHK, //0100xxxss0mmmyyy VM68K_INST_CLR, //0100 0010 ssmm myyy VM68K_INST_CMP, //1011 xxxo oomm myyy VM68K_INST_CMPA, //1011 xxxo oomm myyy VM68K_INST_CMPI, //0000 1100 ssmm myyy VM68K_INST_CMPM, //1011 xxx1 ss00 1yyy VM68K_INST_DBcc, //0101 CCCC 1100 1yyy VM68K_INST_DIVS, //1000xxx111mmmyyy VM68K_INST_DIVU, //1000xxx011mmmyyy VM68K_INST_EOR, //1011 xxxo oomm myyy VM68K_INST_EORI, //0000 1010 ssmm myyy VM68K_INST_EORItoCCR, //0000 1010 0011 1100 00000000dddddddd VM68K_INST_EORItoSR, //0000 1010 0111 1100 dddddddddddddddd VM68K_INST_EXG, //1100 xxx1 oooo oyyy VM68K_INST_EXT, //0100 100o oo00 0yyy VM68K_INST_ILLEGAL, //0100101011111100 VM68K_INST_JMP, //0100 1110 11mm myyy VM68K_INST_JSR, //0100 1110 10mm myyy VM68K_INST_LEA, //0100 xxx1 11mm myyy VM68K_INST_LINK, //0100111001010yyydddddddddddddddd VM68K_INST_LSL_LSR, //1110 cccd ssl0 1yyy VM68K_INST_MOVE, //00ss xxxm mmMM Myyy VM68K_INST_MOVEA, //00ss xxx0 01mm myyy VM68K_INST_MOVEtoCCR, //0100010011mmmyyy VM68K_INST_MOVEfromSR, //0100000011mmmyyy VM68K_INST_MOVEtoSR, //0100011011mmmyyy VM68K_INST_MOVEUSP, //010011100110dyyy VM68K_INST_MOVEMregtomem, //0100 1d00 1smm myyy VM68K_INST_MOVEMmemtoreg, //0100 1d00 1smm myyy VM68K_INST_MOVEP, //0000xxxooo001yyydddddddddddddddd VM68K_INST_MOVEQ, //0111xxx0dddddddd VM68K_INST_MULS, //1100xxx111mmmyyy VM68K_INST_MULU, //1100xxx011mmmyyy!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VM68K_INST_NBCD, //1100xxx011mmmyyy!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VM68K_INST_NEG, //0100 0100 ssmm myyy VM68K_INST_NEGX, //0100 0000 ssmm myyy VM68K_INST_NOP, //0100 1110 0111 0001 VM68K_INST_NOT, //0100 0110 ssmm myyy VM68K_INST_OR, //1000 xxxo oomm myyy VM68K_INST_ORI, //0000 0000 ssmm myyy VM68K_INST_ORItoCCR, //0000 0000 0011 1100 00000000dddddddd VM68K_INST_ORItoSR, //0000 0000 0111 1100 dddddddddddddddd VM68K_INST_PEA, //0100 1000 01mm myyy VM68K_INST_RESET, //0100111001110000 VM68K_INST_ROL_ROR, //1110 cccd ssl1 1yyy VM68K_INST_ROXL_ROXR, //1110 cccd ssl1 0yyy VM68K_INST_RTE, //0100 1110 0111 0011 VM68K_INST_RTR, //0100 1110 0111 0111 VM68K_INST_RTS, //0100 1110 0111 0101 VM68K_INST_SBCD, //1000xxx10000ryyy VM68K_INST_SCC, //0101 CCCC 11mm myyy VM68K_INST_STOP, //0100111001110010iiiiiiiiiiiiiiii VM68K_INST_SUB, //1001 xxxo oomm myyy VM68K_INST_SUBA, //1001 xxxo oomm myyy VM68K_INST_SUBI, //0000 0100 ssmm myyy VM68K_INST_SUBQ, //0101 ddd1 ssmm myyy VM68K_INST_SUBX, //1001 yyy1 ss00 ryyy VM68K_INST_SWAP, //0100100001000yyy VM68K_INST_TAS, //0100101011mmmyyy VM68K_INST_TRAP, //010011100100vvvv VM68K_INST_TRAPV, //0100111001110110 VM68K_INST_TST, //0100 1010 ssmm myyy VM68K_INST_UNLK, //0100111001011yyy } tVM68k_instruction; // opcodes are 16 bit values, this function translates them into an easier-to-handle enumeration. tVM68k_instruction vm68k_decode(tVM68k_uword opcode); #ifdef DEBUG_PRINT // this function is for translating the enumeration into something human-readable. void vm68k_get_instructionname(tVM68k_instruction instruction,char* name); #endif #endif dmagnetic-0.37/src/libdmagnetic/vm68k/vm68k.c0000644000175000017500000010016214430741336017424 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "vm68k_datatypes.h" #include "vm68k_decode.h" #include "vm68k_loadstore.h" #include "vm68k_macros.h" #include "vm68k.h" #define MAGICVALUE 0x564A3638 // "VM68" tVM68k_bool vm68k_checkcondition(tVM68k* pVM68k,tVM68k_ubyte condition) { #define CFLAG(pVM68k) ((((pVM68k)->sr)>>0)&1) #define VFLAG(pVM68k) ((((pVM68k)->sr)>>1)&1) #define ZFLAG(pVM68k) ((((pVM68k)->sr)>>2)&1) #define NFLAG(pVM68k) ((((pVM68k)->sr)>>3)&1) #define XFLAG(pVM68k) ((((pVM68k)->sr)>>4)&1) tVM68k_bool condtrue; switch(condition) { case 0: condtrue=1;break; case 1: condtrue=0;break; case 2: //BHI high 0010 /C | /Z condtrue=!(CFLAG(pVM68k)|ZFLAG(pVM68k)); break; case 3: //LS low or same 0011 C & Z condtrue= (CFLAG(pVM68k)|ZFLAG(pVM68k)); break; case 4://BCC carry clear 0100 /C condtrue=!(CFLAG(pVM68k)); break; case 5://BCS carry set 0101 C condtrue= (CFLAG(pVM68k)); break; case 6://BNE not equal 0110 /Z condtrue=!(ZFLAG(pVM68k)); break; case 7://BEQ equal 0111 Z condtrue= (ZFLAG(pVM68k)); break; case 8://BVC overflow clear 1000 /V condtrue=!(VFLAG(pVM68k)); break; case 9://BVS overflow set 1001 V condtrue= (VFLAG(pVM68k)); break; case 10://BPL plus 1010 /N condtrue=!(NFLAG(pVM68k)); break; case 11://BMI minus 1011 N condtrue= (NFLAG(pVM68k)); break; case 12://BGE greater or equal 1100 (N & V) | (/N & /V), N and V are both set or both clear condtrue=!((NFLAG(pVM68k)^VFLAG(pVM68k))); break; case 13://BLT less than 1101 (N & /V) | (/N & V), N and V are either set or clear condtrue= ((NFLAG(pVM68k)^VFLAG(pVM68k))); break; case 14://BGT greater than 1110 (N & V & /Z) | (/N & /V & /Z) condtrue=!ZFLAG(pVM68k)&!((NFLAG(pVM68k)^VFLAG(pVM68k))); break; case 15://BLE less or equal 1111 condtrue=ZFLAG(pVM68k)|((NFLAG(pVM68k)^VFLAG(pVM68k))); break; default: condtrue=0; break; } return condtrue; } int vm68k_getsize(int* size) { if (size==NULL) return VM68K_NOK_INVALID_PTR; *size=sizeof(tVM68k); return VM68K_OK; } int vm68k_init(void* hVM68k,int version) { tVM68k* pVM68k=(tVM68k*)hVM68k; if (hVM68k==NULL) return VM68K_NOK_INVALID_PTR; pVM68k->magic=MAGICVALUE; pVM68k->pcr=0; pVM68k->memsize=sizeof(pVM68k->memory); pVM68k->a[7]=pVM68k->memsize-4; // The stack pointer goes to the end of the memory pVM68k->version=version; return VM68K_OK; } int vm68k_singlestep(void *hVM68k,unsigned short opcode) { tVM68k* pVM68k=(tVM68k*)hVM68k; tVM68k_instruction instruction; tVM68k_ubyte addrmode; tVM68k_ubyte reg1,reg2; tVM68k_types datatype; tVM68k_next next; tVM68k_slong ea; tVM68k_ulong operand1,operand2; tVM68k_uint64 result; tVM68k_ubyte condition; tVM68k_sword displacement; tVM68k_bool direction; int retval; int i; if (hVM68k==NULL) return VM68K_NOK_INVALID_PTR; if (pVM68k->magic!=MAGICVALUE) return VM68K_NOK_INVALID_PARAMETER; retval=VM68K_NOK_UNKNOWN_INSTRUCTION; instruction=vm68k_decode(opcode); // decode the opcode reg1=(opcode>>9)&0x7; addrmode=(opcode>>3)&0x7; reg2=(opcode>>0)&0x7; datatype=(tVM68k_types)(opcode>>6)&0x3; // branches condition=(opcode>>8)&0xf; displacement=(tVM68k_sword)((tVM68k_sbyte)(opcode&0xff)); // alu operations direction=(opcode>>8)&0x1; INITNEXT(pVM68k,next); result=0; operand1=operand2=0; switch(instruction) { case VM68K_INST_TRAP: printf("\x1b[1;37;42mtrap #%d\n",opcode&0xf); for (i=0;i<16;i++) { printf(" ** trap %d stack %2d %08X \n",opcode&0xf,i,READ_INT32BE(pVM68k->memory,pVM68k->a[7]-i*4)); } printf("\x1b[0m\n"); retval=VM68K_OK; break; case VM68K_INST_MULU: retval=vm68k_resolve_ea(pVM68k,&next,VM68K_WORD,addrmode,reg2,VM68K_LEGAL_ALL,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,VM68K_WORD,ea,&operand1); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,VM68K_WORD,DATAREGADDR(reg1),&operand2); if (retval==VM68K_OK) result=((unsigned int)operand1&0xffff)*((unsigned short)operand2&0xffff); if (retval==VM68K_OK) retval=vm68k_calculateflags2(&next,FLAGS_ALL,instruction,VM68K_LONG,operand1,operand2,result); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,VM68K_LONG,DATAREGADDR(reg1),result); break; case VM68K_INST_DIVU: // FIXME: division by 0? retval=vm68k_resolve_ea(pVM68k,&next,VM68K_WORD,addrmode,reg2,VM68K_LEGAL_ALL,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,VM68K_WORD,ea,&operand1); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,VM68K_WORD,DATAREGADDR(reg1),&operand2); // upper 16 bits are the remainder if (retval==VM68K_OK) result=(((unsigned int)operand1&0xffff)%((unsigned short)operand2&0xffff))<<16; // lower 16 bits are the quotient if (retval==VM68K_OK) result|=(((unsigned int)operand1&0xffff)/((unsigned short)operand2&0xffff))&0xffff; if (retval==VM68K_OK) retval=vm68k_calculateflags2(&next,FLAGS_ALL,instruction,VM68K_LONG,operand1,operand2,result); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,VM68K_LONG,DATAREGADDR(reg1),result); break; case VM68K_INST_ADD: case VM68K_INST_CMP: case VM68K_INST_SUB: if (instruction==VM68K_INST_CMP) { retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,VM68K_LEGAL_ALL,&ea); } else { retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,direction?VM68K_LEGAL_MEMORYALTERATE:VM68K_LEGAL_ALL,&ea); } if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,1,datatype,ea,&operand1); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,1,datatype,DATAREGADDR(reg1),&operand2); if (instruction==VM68K_INST_SUB || instruction==VM68K_INST_CMP) { if (direction) { result=operand1-operand2; } else { result=operand2-operand1; } } else { result=operand2+operand1; } if (retval==VM68K_OK) retval=vm68k_calculateflags2(&next,FLAGS_ALL,instruction,datatype,operand1,operand2,result); if (retval==VM68K_OK && instruction!=VM68K_INST_CMP) retval=vm68k_storeresult(pVM68k,&next,datatype,(direction)?ea:DATAREGADDR(reg1),result); break; case VM68K_INST_ADDA: case VM68K_INST_CMPA: case VM68K_INST_SUBA: if (datatype==VM68K_UNKNOWN) { tVM68k_types datatype2; tVM68k_types datatype3; datatype2=((opcode>>8)&1)?VM68K_LONG:VM68K_WORD; if (pVM68k->version==4) { datatype3=VM68K_LONG; } else { datatype3=datatype2; } retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_ALL,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,1,datatype2,ea,&operand1); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,1,datatype3,ADDRREGADDR(reg1),&operand2); if (instruction==VM68K_INST_SUBA || instruction==VM68K_INST_CMPA) { result=operand2-operand1; } else { result=operand2+operand1; } if (retval==VM68K_OK && instruction==VM68K_INST_CMPA) retval=vm68k_calculateflags2(&next,FLAGS_ALL,instruction,datatype2,operand2,operand1,result); if (retval==VM68K_OK && instruction!=VM68K_INST_CMPA) retval=vm68k_storeresult(pVM68k,&next,datatype3,ADDRREGADDR(reg1),result); } break; case VM68K_INST_ADDI: case VM68K_INST_CMPI: case VM68K_INST_SUBI: READEXTENSION(pVM68k,&next,datatype,operand1); retval=VM68K_OK; switch(datatype) { case VM68K_BYTE: operand1=(tVM68k_slong)((tVM68k_sbyte)(operand1& 0xff));break; case VM68K_WORD: operand1=(tVM68k_slong)((tVM68k_sword)(operand1& 0xffff));break; case VM68K_LONG: operand1=(tVM68k_slong)((tVM68k_slong)(operand1&0xffffffff));break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION;break; } if (retval==VM68K_OK) retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,VM68K_LEGAL_DATAALTERATE,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,1,datatype,ea,&operand2); if (instruction==VM68K_INST_SUBI || instruction==VM68K_INST_CMPI) { result=operand2-operand1; // Checked 0c01 } else { result=operand2+operand1; } if (retval==VM68K_OK) retval=vm68k_calculateflags2(&next,FLAGS_ALL,instruction,datatype,operand1,operand2,result); if (retval==VM68K_OK && instruction!=VM68K_INST_CMPI) retval=vm68k_storeresult(pVM68k,&next,datatype,ea,result); break; case VM68K_INST_ADDQ: case VM68K_INST_SUBQ: { tVM68k_sbyte quick; tVM68k_bool version3_workaround; retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,VM68K_LEGAL_ALTERABLEADRESSING,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,1,datatype,ea,&operand2); quick=reg1; operand1=quick; if (operand1==0) operand1=8; if (instruction==VM68K_INST_SUBQ) { result=operand2-operand1; } else { result=operand2+operand1; } version3_workaround=next.zflag; // starting with version 3, the z-flag needed to be preserved. this was an inconsistency in the original engine, that just stuck. if (retval==VM68K_OK) retval=vm68k_calculateflags2(&next,FLAGS_ALL,instruction,datatype,operand1,operand2,result); if ((pVM68k->version>=3) && (instruction==VM68K_INST_ADDQ)) next.zflag=version3_workaround; if (retval==VM68K_OK && instruction!=VM68K_INST_CMPI) retval=vm68k_storeresult(pVM68k,&next,datatype,ea,result); } break; case VM68K_INST_EXG: { tVM68k_sbyte opmode; opmode=(opcode>>3)&0x1f; switch(opmode) { case 0x08: next.d[reg1]=pVM68k->d[reg2]; next.d[reg2]=pVM68k->d[reg1];break; // 01000= data registers. case 0x09: next.a[reg1]=pVM68k->a[reg2]; next.a[reg2]=pVM68k->a[reg1];break; // 01001= addr registers. case 0x11: next.d[reg1]=pVM68k->a[reg2]; next.a[reg2]=pVM68k->d[reg1];break; // 10001= data +addr registers. } retval=VM68K_OK; } break; case VM68K_INST_MOVEQ: { tVM68k_types datatype2; tVM68k_sbyte data; datatype2=VM68K_LONG; data=opcode&0xff; result=(tVM68k_slong)data; retval=vm68k_calculateflags(&next,FLAGS_LOGIC,datatype2,0,0,result); next.cflag=next.vflag=0; if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype2,DATAREGADDR(reg1),result); } break; case VM68K_INST_AND: case VM68K_INST_EOR: case VM68K_INST_OR: // direction=1: -Dn -> // direction=0: Dn- -> Dn if (instruction==VM68K_INST_EOR) // TODO: is this really neessary? { retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,VM68K_LEGAL_ALL,&ea); } else { retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,direction?VM68K_LEGAL_MEMORYALTERATE:VM68K_LEGAL_ALL,&ea); } if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype,ea,&operand2); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype,DATAREGADDR(reg1),&operand1); switch (instruction) { case VM68K_INST_AND: result=operand1&operand2;break; case VM68K_INST_EOR: result=operand1^operand2;break; case VM68K_INST_OR: result=operand1|operand2;break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION;break; } if (retval==VM68K_OK) retval=vm68k_calculateflags(&next,FLAGS_LOGIC,datatype,operand1,operand2,result); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype,(direction)?ea:DATAREGADDR(reg1),result); break; case VM68K_INST_ANDI: case VM68K_INST_EORI: case VM68K_INST_ORI: READEXTENSION(pVM68k,&next,datatype,operand1); retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,VM68K_LEGAL_DATAALTERATE,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype,ea,&operand2); switch (instruction) { case VM68K_INST_ANDI:result=operand1&operand2;break; case VM68K_INST_EORI:result=operand1^operand2;break; case VM68K_INST_ORI: result=operand1|operand2;break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION;break; } if (retval==VM68K_OK) retval=vm68k_calculateflags(&next,FLAGS_LOGIC,datatype,operand1,operand2,result); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype,ea,result); break; case VM68K_INST_BCC: if (displacement==0) { displacement=READEXTENSIONWORD(pVM68k,&next); } if (condition==1) // BSR { PUSHLONGTOSTACK(pVM68k,&next,(next.pcr)); } if (vm68k_checkcondition(pVM68k,condition) || condition==1) { next.pcr=pVM68k->pcr+displacement; } retval=VM68K_OK; break; case VM68K_INST_MOVE: case VM68K_INST_MOVEA: { tVM68k_types datatype2; tVM68k_ubyte addrmode_dest; tVM68k_slong ea_dest; datatype2=VM68K_UNKNOWN; retval=VM68K_OK; switch ((opcode>>12)&0x3) { case 1: datatype2=VM68K_BYTE;break; case 3: datatype2=VM68K_WORD;break; case 2: datatype2=VM68K_LONG;break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION; } addrmode_dest=(opcode>>6)&0x7; if (retval==VM68K_OK) retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_ALL,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype2,ea,&operand2); // TODO: I had a problem here, when the addrmode was 7/4 and the size was BYTE. lets see what happens. if (retval==VM68K_OK) retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode_dest,reg1,(instruction==VM68K_INST_MOVE)?VM68K_LEGAL_DATAALTERATE:VM68K_LEGAL_ALL,&ea_dest); if (retval==VM68K_OK) result=operand2; if (retval==VM68K_OK && instruction!=VM68K_INST_MOVEA) retval=vm68k_calculateflags(&next,FLAGS_LOGIC,datatype2,0,operand2,result); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype2,ea_dest,result); } break; case VM68K_INST_NEG: case VM68K_INST_NEGX: case VM68K_INST_NOT: { retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,VM68K_LEGAL_DATAALTERATE,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,1,datatype,ea,&operand2); result=(instruction==VM68K_INST_NOT)?(~operand2):(0-operand2); result=result-((instruction==VM68K_INST_NEGX)&next.xflag); operand1=0; if (retval==VM68K_OK) retval=vm68k_calculateflags(&next,FLAGS_LOGIC,datatype,operand1,operand2,result); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype,ea,result); } break; case VM68K_INST_JMP: case VM68K_INST_JSR: { tVM68k_types datatype2; datatype2=VM68K_LONG; retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_CONTROLADDRESSING,&ea); operand2=ea; if (instruction==VM68K_INST_JSR && retval==VM68K_OK) { PUSHLONGTOSTACK(pVM68k,&next,(next.pcr)); } // TODO: why this way? switch (addrmode) { case VM68K_AM_INDIR: next.pcr=operand2%pVM68k->memsize; break; default: next.pcr=operand2; // wonderland break; } } break; case VM68K_INST_RTS: retval=VM68K_OK; POPLONGFROMSTACK(pVM68k,&next,next.pcr); break; case VM68K_INST_ANDItoSR: case VM68K_INST_EORItoSR: case VM68K_INST_ORItoSR: case VM68K_INST_ANDItoCCR: case VM68K_INST_EORItoCCR: case VM68K_INST_ORItoCCR: retval=VM68K_OK; operand2=READEXTENSIONWORD(pVM68k,&next); operand1=0xffff; switch (instruction) { case VM68K_INST_ANDItoCCR: operand1&=0x1f; case VM68K_INST_ANDItoSR: operand2&=next.sr;break; case VM68K_INST_EORItoCCR: operand1&=0x1f; case VM68K_INST_EORItoSR: operand2^=next.sr;break; case VM68K_INST_ORItoCCR: operand1&=0x1f; case VM68K_INST_ORItoSR: operand2|=next.sr;break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION;break; } next.override_sr=1; next.sr&=~operand1; next.sr|=operand2; break; case VM68K_INST_MOVEfromSR: { tVM68k_types datatype2; datatype2=VM68K_WORD; retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_DATAALTERATE,&ea); result=next.sr&0xffff; if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype2,ea,result); } break; case VM68K_INST_MOVEtoCCR: case VM68K_INST_MOVEtoSR: { tVM68k_types datatype2; datatype2=VM68K_WORD; retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_DATAADDRESSING,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype2,ea,&operand2); if (retval==VM68K_OK) { next.override_sr=1; next.sr=(instruction==VM68K_INST_MOVEtoCCR)?((next.sr&0xffe0)|(operand2&0x1f)):operand2; } } break; case VM68K_INST_MOVEMregtomem: { tVM68k_types datatype2; tVM68k_uword bitmask=0; datatype2=((opcode>>6)&1)?VM68K_LONG:VM68K_WORD; retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_CONTROLALTERATEADDRESSING|VM68K_LEGAL_AM_PREDEC,&ea); // special case: the memory decrement should only be performed when the bitmask says so { for (i=0;i<8;i++) next.a[i]=pVM68k->a[i]; } if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype2,ea,&operand2); if (retval==VM68K_OK) {bitmask=READEXTENSIONWORD(pVM68k,&next);} if (retval==VM68K_OK) { for (i=0;i<8;i++) { if (bitmask&1) { // FIXME: technically not the stack. if (datatype2==VM68K_WORD) PUSHWORDTOSTACK(pVM68k,&next,pVM68k->a[7-i]); if (datatype2==VM68K_LONG) PUSHLONGTOSTACK(pVM68k,&next,pVM68k->a[7-i]); } bitmask>>=1; } for (i=0;i<8;i++) { if (bitmask&1) { // FIXME: technically not the stack. if (datatype2==VM68K_WORD) PUSHWORDTOSTACK(pVM68k,&next,pVM68k->d[7-i]); if (datatype2==VM68K_LONG) PUSHLONGTOSTACK(pVM68k,&next,pVM68k->d[7-i]); } bitmask>>=1; } } } break; case VM68K_INST_MOVEMmemtoreg: { tVM68k_types datatype2; tVM68k_uword bitmask=0; datatype2=((opcode>>6)&1)?VM68K_LONG:VM68K_WORD; retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_CONTROLADDRESSING|VM68K_LEGAL_AM_POSTINC,&ea); // special case: the memory increment should only be performed when the bitmask says so { for (i=0;i<8;i++) next.a[i]=pVM68k->a[i]; } if (retval==VM68K_OK) {bitmask=READEXTENSIONWORD(pVM68k,&next);} if (retval==VM68K_OK) { for (i=0;i<8;i++) { if (bitmask&1) { // FIXME: not really the stack. if (datatype2==VM68K_WORD) { POPWORDFROMSTACK(pVM68k,&next,next.d[i]); } if (datatype2==VM68K_LONG) { POPLONGFROMSTACK(pVM68k,&next,next.d[i]); } } bitmask>>=1; } for (i=0;i<8;i++) { if (bitmask&1) { // FIXME: not really the stack. if (datatype2==VM68K_WORD) { POPWORDFROMSTACK(pVM68k,&next,next.a[i]); next.a[i]&=0xffff; } if (datatype2==VM68K_LONG) { POPLONGFROMSTACK(pVM68k,&next,next.a[i]); } } bitmask>>=1; } } } break; case VM68K_INST_EXT: { tVM68k_types datatype2=VM68K_UNKNOWN; switch ((opcode>>6)&0x3) { case 2: datatype2=VM68K_WORD;break; case 3: datatype2=VM68K_LONG;break; } switch (datatype2) { case VM68K_WORD: result=(tVM68k_sword)((tVM68k_sbyte)(pVM68k->d[reg2]& 0xff));retval=VM68K_OK; result=((pVM68k->d[reg2])&0xffff0000)|(((tVM68k_ulong)result)&0xffff); break; case VM68K_LONG: result=(tVM68k_slong)((tVM68k_sword)(pVM68k->d[reg2]&0xffff));retval=VM68K_OK;break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION;break; } if (retval==VM68K_OK) retval=vm68k_calculateflags(&next,FLAGS_LOGIC,datatype2,0,0,result); if (retval==VM68K_OK) next.d[reg2]=result; } break; case VM68K_INST_PEA: retval=vm68k_resolve_ea(pVM68k,&next,VM68K_LONG,addrmode,reg2,VM68K_LEGAL_CONTROLADDRESSING,&ea); result=ea; if (retval==VM68K_OK) PUSHLONGTOSTACK(pVM68k,&next,result); break; case VM68K_INST_LEA: retval=vm68k_resolve_ea(pVM68k,&next,VM68K_LONG,addrmode,reg2,VM68K_LEGAL_CONTROLADDRESSING,&ea); result=ea%(pVM68k->memsize); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,VM68K_LONG,ADDRREGADDR(reg1),result); break; case VM68K_INST_NOP: retval=VM68K_OK; break; case VM68K_INST_ASL_ASR: case VM68K_INST_LSL_LSR: case VM68K_INST_ROL_ROR: case VM68K_INST_ROXL_ROXR: { tVM68k_types datatype2; tVM68k_ubyte count; tVM68k_bool direction; tVM68k_bool msb; tVM68k_bool lsb; tVM68k_ubyte bitnum; direction=(opcode>>8)&1; // 0=right. 1=left. bitnum=8; if (datatype==VM68K_UNKNOWN) // memory shift { datatype2=VM68K_WORD; count=1; retval=vm68k_resolve_ea(pVM68k,&next,VM68K_LONG,addrmode,reg2,VM68K_LEGAL_MEMORYALTERATE,&ea); } else { datatype2=datatype; if ((opcode>>5)&1) { count=pVM68k->d[reg1]%64; } else // i/r=1 -> register. i/r=0 -> immedate { count=reg1; if (count==0) count=8; } retval=VM68K_OK; ea=DATAREGADDR(reg2); } if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype,ea,&operand2); switch (datatype2) { case VM68K_BYTE: bitnum= 8;break; case VM68K_WORD: bitnum=16;break; case VM68K_LONG: bitnum=32;break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION;break; } next.vflag=0; for (i=0;i>(bitnum-1))&1; lsb=operand2&1; if (direction) // left shift { operand2<<=1; switch (instruction) { case VM68K_INST_ASL_ASR: case VM68K_INST_LSL_LSR: lsb=0;break; case VM68K_INST_ROL_ROR: lsb=msb;break; case VM68K_INST_ROXL_ROXR: lsb=next.xflag;break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION;break; } operand2|=lsb; next.cflag=next.xflag=msb; // FIXME: reallY??? if (instruction!=VM68K_INST_ASL_ASR) next.vflag|=(prevmsb^(operand2>>(bitnum-1)))&1; // set overflow flag if the msb is changed at any time. } else { /// right shift operand2>>=1; switch (instruction) { case VM68K_INST_ASL_ASR: msb=msb&1;break; case VM68K_INST_LSL_LSR: msb=0;break; case VM68K_INST_ROL_ROR: msb=lsb;break; case VM68K_INST_ROXL_ROXR: msb=next.xflag;break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION;break; } operand2|=(msb<<(bitnum-1)); next.cflag=next.xflag=lsb; } } result=operand2; if (retval==VM68K_OK) retval=vm68k_calculateflags(&next,FLAGN|FLAGZ,datatype2,0,operand2,result); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype2,ea,result); } break; case VM68K_INST_BCLR: case VM68K_INST_BCHG: case VM68K_INST_BSET: case VM68K_INST_BTST: { tVM68k_ubyte bitnum; tVM68k_types datatype2; if (addrmode==VM68K_AM_DATAREG) { datatype2=VM68K_LONG; bitnum=32; } else { datatype2=VM68K_BYTE; bitnum=8; } retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_DATAALTERATE,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype2,ea,&operand2); if (retval==VM68K_OK) { operand1=(1<<(next.d[reg1]%bitnum)); next.zflag=((operand2&operand1)==0); switch(instruction) { case VM68K_INST_BCLR: result=operand2&~operand1;break; case VM68K_INST_BCHG: result=operand2^ operand1;break; case VM68K_INST_BSET: result=operand2| operand1;break; default: result=0; break; } } if (retval==VM68K_OK && instruction!=VM68K_INST_BTST) retval=vm68k_storeresult(pVM68k,&next,datatype2,ea,result); } break; case VM68K_INST_BCLRI: case VM68K_INST_BCHGB: case VM68K_INST_BSETB: case VM68K_INST_BTSTB: { tVM68k_uword bitnum; tVM68k_types datatype2; bitnum=READEXTENSIONWORD(pVM68k,&next); datatype2=((addrmode==VM68K_AM_DATAREG)||(addrmode==VM68K_AM_ADDRREG))?VM68K_LONG:VM68K_BYTE; retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_DATAALTERATE,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype2,ea,&operand2); if (retval==VM68K_OK) { operand1=(1<<(bitnum%32)); next.zflag=((operand2&operand1)==0); switch (instruction) { case VM68K_INST_BCLRI: result=operand2&~operand1;break; case VM68K_INST_BCHGB: result=operand2^ operand1;break; case VM68K_INST_BSETB: result=operand2| operand1;break; default: result=0; break; } } if (retval==VM68K_OK && instruction!=VM68K_INST_BTSTB) retval=vm68k_storeresult(pVM68k,&next,datatype2,ea,result); } break; case VM68K_INST_ADDX: case VM68K_INST_SUBX: { tVM68k_slong ea_dest; tVM68k_bool rm; rm=(opcode>>3)&1; retval=vm68k_resolve_ea(pVM68k,&next,datatype,(rm?VM68K_AM_PREDEC:VM68K_AM_DATAREG),reg2,VM68K_LEGAL_AM_PREDEC|VM68K_LEGAL_AM_DATAREG,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype,ea,&operand2); if (retval==VM68K_OK) retval=vm68k_resolve_ea(pVM68k,&next,datatype,(rm?VM68K_AM_PREDEC:VM68K_AM_DATAREG),reg1,VM68K_LEGAL_AM_PREDEC|VM68K_LEGAL_AM_DATAREG,&ea_dest); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype,ea,&operand1); if (retval==VM68K_OK) operand1+=next.xflag; if (retval==VM68K_OK) if (instruction==VM68K_INST_SUBX) operand1=-operand1; if (retval==VM68K_OK) result=operand1+operand2; if (retval==VM68K_OK) if (result!=0) next.zflag=0; // special case if (retval==VM68K_OK) retval=vm68k_calculateflags(&next,FLAGS_ALL^FLAGZ,datatype,operand1,operand2,result); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype,ea,result); } break; case VM68K_INST_CMPM: { tVM68k_slong ea_dest; retval=vm68k_resolve_ea(pVM68k,&next,datatype,VM68K_AM_POSTINC,reg2,VM68K_LEGAL_AM_POSTINC,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,1,datatype,ea,&operand2); if (retval==VM68K_OK) retval=vm68k_resolve_ea(pVM68k,&next,datatype,VM68K_AM_POSTINC,reg1,VM68K_LEGAL_AM_POSTINC,&ea_dest); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,1,datatype,ea_dest,&operand1); if (retval==VM68K_OK) result=operand2-operand1; if (retval==VM68K_OK) retval=vm68k_calculateflags2(&next,FLAGS_ALL,instruction,datatype,operand1,operand2,result); } break; case VM68K_INST_CLR: { retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,VM68K_LEGAL_DATAALTERATE,&ea); result=0; if (retval==VM68K_OK) retval=vm68k_calculateflags(&next,FLAGS_LOGIC,datatype,0,0,result); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype,ea,result); } break; case VM68K_INST_DBcc: { retval=VM68K_OK; displacement=READEXTENSIONWORD(pVM68k,&next); if (!vm68k_checkcondition(pVM68k,condition)) { next.d[reg2]&=0xffff0000; next.d[reg2]|=(pVM68k->d[reg2]-1)&0xffff; if ((tVM68k_sword)next.d[reg2]>=0) next.pcr=pVM68k->pcr+displacement; } } break; case VM68K_INST_SWAP: { tVM68k_types datatype2; datatype2=VM68K_LONG; retval=vm68k_resolve_ea(pVM68k,&next,datatype2,VM68K_AM_DATAREG,reg2,VM68K_LEGAL_AM_DATAREG,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype2,ea,&operand2); if (retval==VM68K_OK) result=((operand2>>16)&0xffff)|((operand2&0xffff)<<16); if (retval==VM68K_OK) next.nflag=(result>>31)&1; if (retval==VM68K_OK) next.zflag=(result==0); next.cflag=0; next.vflag=0; if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype2,ea,result); } break; case VM68K_INST_SCC: { tVM68k_types datatype2; datatype2=VM68K_BYTE; result=(vm68k_checkcondition(pVM68k,condition))?0xff:0x00; retval=vm68k_resolve_ea(pVM68k,&next,datatype2,addrmode,reg2,VM68K_LEGAL_DATAALTERATE,&ea); if (retval==VM68K_OK) retval=vm68k_storeresult(pVM68k,&next,datatype2,ea,result); } break; case VM68K_INST_TST: { retval=vm68k_resolve_ea(pVM68k,&next,datatype,addrmode,reg2,VM68K_LEGAL_DATAALTERATE,&ea); if (retval==VM68K_OK) retval=vm68k_fetchoperand(pVM68k,0,datatype,ea,&operand2); if (retval==VM68K_OK) result=operand2; if (retval==VM68K_OK) retval=vm68k_calculateflags(&next,FLAGS_LOGIC,datatype,0,0,result); } break; case VM68K_INST_UNKNOWN: retval=VM68K_NOK_UNKNOWN_INSTRUCTION; break; default: { #ifdef DEBUG_PRINT char tmp[64]; vm68k_get_instructionname(instruction,tmp); printf("UNIMPLEMENTED opcode %04X = %s\n",opcode,tmp); #else printf("UNIMPLEMENTED opcode %04X\n",opcode); #endif retval=VM68K_NOK_UNKNOWN_INSTRUCTION; } break; } if (retval==VM68K_OK) { pVM68k->pcr=next.pcr; pVM68k->sr&=0xffe0; if (next.override_sr==1) { pVM68k->sr=next.sr; } else { pVM68k->sr|=(next.cflag)<<0; pVM68k->sr|=(next.vflag)<<1; pVM68k->sr|=(next.zflag)<<2; pVM68k->sr|=(next.nflag)<<3; pVM68k->sr|=(next.xflag)<<4; } for (i=0;i<8;i++) { pVM68k->a[i]=next.a[i]; pVM68k->d[i]=next.d[i]; } if (next.mem_we) { for (i=0;imemory, next.mem_addr[i],next.mem_value[i]& 0xff);break; case 1: WRITE_INT16BE(pVM68k->memory,next.mem_addr[i],next.mem_value[i]& 0xffff);break; case 2: WRITE_INT32BE(pVM68k->memory,next.mem_addr[i],next.mem_value[i]&0xffffffff);break; default: retval=VM68K_NOK_UNKNOWN_INSTRUCTION;break; } } } } return retval; } int vm68k_getNextOpcode(void* hVM68k,unsigned short* opcode) { tVM68k* pVM68k=(tVM68k*)hVM68k; if (hVM68k==NULL) return VM68K_NOK_INVALID_PTR; if (opcode==NULL) return VM68K_NOK_INVALID_PTR; if (pVM68k->magic!=MAGICVALUE) return VM68K_NOK_INVALID_PARAMETER; *opcode=READ_INT16BE(pVM68k->memory,pVM68k->pcr); pVM68k->pcr+=2; #ifdef DEBUG_PRINT { //static int pcrcnt[65536]={0}; int i; char tmp[64]; tVM68k_instruction inst; printf("\n\n\n"); //printf("%d ",pcrcnt[pVM68k->pcr]++); printf("pcr:%06x ",pVM68k->pcr); printf("INST:%04X ",*opcode); printf("CVZN:%d%d%d%d ", (pVM68k->sr>>0)&1,(pVM68k->sr>>1)&1,(pVM68k->sr>>2)&1,(pVM68k->sr>>3)&1); printf("D:"); for (i=0;i<8;i++) { printf("%08X:",pVM68k->d[i]); } printf(" A:"); for (i=0;i<8;i++) { printf("%08X:",pVM68k->a[i]); } { unsigned long long sum; sum=0; for (i=0;imemsize;i++) sum+=READ_INT32BE(pVM68k->memory,i); printf("MEMSUM:%llX ",sum); } inst=vm68k_decode(*opcode); vm68k_get_instructionname(inst,tmp); printf(" --> %s\n",tmp); if (pVM68k->pcr<0x1c238) printf("\x1b[0m\n"); fflush(stdout); } #endif return VM68K_OK; } int vm68k_getpSharedMem(void* hVM68k,void** pSharedMem,int* bytes) { tVM68k* pVM68k=(tVM68k*)hVM68k; *pSharedMem=(void*)&(pVM68k->memory[0]); *bytes=sizeof(pVM68k->memory); return VM68K_OK; } int vm68k_getState(void* hVM68k,unsigned int* aregs,unsigned int* dregs,unsigned int *pcr,unsigned int* sr) { int i; tVM68k* pVM68k=(tVM68k*)hVM68k; for (i=0;i<8;i++) { aregs[i]=pVM68k->a[i]; dregs[i]=pVM68k->d[i]; } *pcr=pVM68k->pcr; *sr=pVM68k->sr; return VM68K_OK; } dmagnetic-0.37/src/libdmagnetic/vm68k/vm68k_loadstore.h0000644000175000017500000001142314430741336021506 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef VM68K_LOADSTORE_H #define VM68K_LOADSTORE_H #include "vm68k_datatypes.h" // some helper defines #define DATAREGADDR(addr) (-((addr)+ 1)) #define ADDRREGADDR(addr) (-((addr)+10)) #define VM68K_LEGAL_AM_DATAREG (1<< 1) #define VM68K_LEGAL_AM_ADDRREG (1<< 2) #define VM68K_LEGAL_AM_INDIR (1<< 3) #define VM68K_LEGAL_AM_POSTINC (1<< 4) #define VM68K_LEGAL_AM_PREDEC (1<< 5) #define VM68K_LEGAL_AM_DISP16 (1<< 6) #define VM68K_LEGAL_AM_INDEX (1<< 7) #define VM68K_LEGAL_AMX_W (1<< 8) #define VM68K_LEGAL_AMX_L (1<< 9) #define VM68K_LEGAL_AMX_DATA (1<<10) #define VM68K_LEGAL_AMX_PC (1<<11) #define VM68K_LEGAL_AMX_INDEX_PC (1<<12) #define VM68K_LEGAL_CONTROLALTERATEADDRESSING (VM68K_LEGAL_AM_INDIR|VM68K_LEGAL_AM_DISP16|VM68K_LEGAL_AM_INDEX|VM68K_LEGAL_AMX_W|VM68K_LEGAL_AMX_L|VM68K_LEGAL_AMX_PC|VM68K_LEGAL_AMX_INDEX_PC) #define VM68K_LEGAL_CONTROLADDRESSING (VM68K_LEGAL_AM_INDIR|VM68K_LEGAL_AM_DISP16|VM68K_LEGAL_AM_INDEX|VM68K_LEGAL_AMX_W|VM68K_LEGAL_AMX_L|VM68K_LEGAL_AMX_PC|VM68K_LEGAL_AMX_INDEX_PC) #define VM68K_LEGAL_DATAADDRESSING ( VM68K_LEGAL_AM_DATAREG| VM68K_LEGAL_AM_INDIR| VM68K_LEGAL_AM_POSTINC| VM68K_LEGAL_AM_PREDEC| VM68K_LEGAL_AM_DISP16| VM68K_LEGAL_AM_INDEX| VM68K_LEGAL_AMX_W| VM68K_LEGAL_AMX_L| VM68K_LEGAL_AMX_DATA| VM68K_LEGAL_AMX_PC| VM68K_LEGAL_AMX_INDEX_PC ) #define VM68K_LEGAL_ALL ( VM68K_LEGAL_AM_DATAREG| VM68K_LEGAL_AM_ADDRREG| VM68K_LEGAL_AM_INDIR| VM68K_LEGAL_AM_POSTINC| VM68K_LEGAL_AM_PREDEC| VM68K_LEGAL_AM_DISP16| VM68K_LEGAL_AM_INDEX| VM68K_LEGAL_AMX_W| VM68K_LEGAL_AMX_L| VM68K_LEGAL_AMX_DATA| VM68K_LEGAL_AMX_PC| VM68K_LEGAL_AMX_INDEX_PC ) #define VM68K_LEGAL_DATAALTERATE ( VM68K_LEGAL_AM_DATAREG|VM68K_LEGAL_AM_INDIR|VM68K_LEGAL_AM_POSTINC| VM68K_LEGAL_AM_PREDEC| VM68K_LEGAL_AM_DISP16| VM68K_LEGAL_AM_INDEX| VM68K_LEGAL_AMX_W| VM68K_LEGAL_AMX_L) #define VM68K_LEGAL_MEMORYALTERATE ( VM68K_LEGAL_AM_INDIR|VM68K_LEGAL_AM_POSTINC| VM68K_LEGAL_AM_PREDEC| VM68K_LEGAL_AM_DISP16| VM68K_LEGAL_AM_INDEX| VM68K_LEGAL_AMX_W| VM68K_LEGAL_AMX_L) #define VM68K_LEGAL_ALTERABLEADRESSING (VM68K_LEGAL_AM_DATAREG|VM68K_LEGAL_AM_ADDRREG|VM68K_LEGAL_AM_INDIR|VM68K_LEGAL_AM_POSTINC|VM68K_LEGAL_AM_PREDEC|VM68K_LEGAL_AM_DISP16|VM68K_LEGAL_AM_INDEX| VM68K_LEGAL_AMX_W|VM68K_LEGAL_AMX_L) #define FLAGC (1<<0) #define FLAGV (1<<1) #define FLAGZ (1<<2) #define FLAGN (1<<3) #define FLAGX (1<<4) #define FLAGCZCLR (1<<5) // when set, the c and z flag are always cleared. #define FLAGS_ALL (FLAGC|FLAGV|FLAGZ|FLAGN|FLAGX) #define FLAGS_LOGIC (FLAGZ|FLAGN|FLAGCZCLR) // this function is calculating the number of bytes for a datatype int vm68k_getbytesize(tVM68k_types size); // this function is translating the addressmode/registerpair into a memory address int vm68k_resolve_ea(tVM68k* pVM68k,tVM68k_next *pNext,tVM68k_types size, tVM68k_addrmodes addrmode,tVM68k_ubyte reg, tVM68k_uword legal,tVM68k_slong* ea); // this function is loading the operand int vm68k_fetchoperand(tVM68k* pVM68k,tVM68k_bool extendsign,tVM68k_types size,tVM68k_slong ea,tVM68k_ulong* operand); // this function is calculting the status flags int vm68k_calculateflags(tVM68k_next* pNext,tVM68k_ubyte flagmask,tVM68k_types size,tVM68k_ulong operand1,tVM68k_ulong operand2,tVM68k_uint64 result); int vm68k_calculateflags2(tVM68k_next* pNext,tVM68k_ubyte flagmask,tVM68k_instruction instruction,tVM68k_types datatype,tVM68k_ulong operand1,tVM68k_ulong operand2,tVM68k_uint64 result); // this function is storing the result of the operation. int vm68k_storeresult(tVM68k* pVM68k,tVM68k_next* pNext,tVM68k_types size,tVM68k_slong ea,tVM68k_ulong result); #endif dmagnetic-0.37/src/libdmagnetic/vm68k/vm68k_datatypes.h0000644000175000017500000001002714430741336021507 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef VM68K_DATATYPES_H #define VM68K_DATATYPES_H // the purpose of this file is to provide the shared datatypes needed for the virtual machine #ifdef __sgi__ typedef unsigned char tVM68k_bool; typedef unsigned char tVM68k_ubyte; typedef unsigned short tVM68k_uword; typedef unsigned int tVM68k_ulong; typedef unsigned long long tVM68k_uint64; typedef signed char tVM68k_sbyte; typedef signed short tVM68k_sword; typedef signed int tVM68k_slong; typedef signed long long tVM68k_sint64; #else #include // first of all: the standard data types. typedef uint_least8_t tVM68k_bool; typedef uint_least8_t tVM68k_ubyte; typedef uint_least16_t tVM68k_uword; typedef uint_least32_t tVM68k_ulong; typedef uint_least64_t tVM68k_uint64; typedef int_least8_t tVM68k_sbyte; typedef int_least16_t tVM68k_sword; typedef int_least32_t tVM68k_slong; typedef int_least64_t tVM68k_sint64; #endif // then a couple of enumerations. to make the sourcecode a little bit easier to read. typedef enum _tVM68k_types {VM68K_BYTE=0,VM68K_WORD=1,VM68K_LONG=2,VM68K_UNKNOWN=3} tVM68k_types; typedef enum _tVM68k_addrmodes { VM68K_AM_DATAREG=0, // Dn VM68K_AM_ADDRREG=1, // An VM68K_AM_INDIR=2, // (An) VM68K_AM_POSTINC=3, // (An)+ VM68K_AM_PREDEC=4, // -(An) VM68K_AM_DISP16=5, // (d16,An) VM68K_AM_INDEX=6, // (d8,An,Xn) VM68K_AM_EXT=7} tVM68k_addrmodes; typedef enum _tVM68k_addrmode_ext { VM68K_AMX_W=0, // (xxx),W VM68K_AMX_L=1, // (xxx),L VM68K_AMX_data=4, // # VM68K_AMX_PC=2, // (d16,PC) VM68K_AMX_INDEX_PC=3} // (d8,PC,Xn) tVM68k_addrmode_ext; // the internal structures ////// this structure holds the state after the instruction has been decoded. typedef struct _tVM68k_next { tVM68k_ulong pcr; // program counter tVM68k_bool override_sr; tVM68k_uword sr; tVM68k_bool cflag; tVM68k_bool vflag; tVM68k_bool zflag; tVM68k_bool nflag; tVM68k_bool xflag; // bit 0..4: CVZNX tVM68k_ulong a[8]; // address register tVM68k_ulong d[8]; // data register ////// memory queue tVM68k_types mem_size; tVM68k_ulong mem_addr[16]; tVM68k_ulong mem_value[16]; tVM68k_ubyte mem_we; } tVM68k_next; // virtual machine state. This is being saved as "advanced" savegame, so make sure it does not contain any pointers. typedef struct _tVM68k { tVM68k_ulong magic; // just so that the functions can identify a handle as this particular data structure tVM68k_ulong pcr; // program counter tVM68k_uword sr; // status register. // bit 0..4: CVZNX tVM68k_ulong a[8]; // address register tVM68k_ulong d[8]; // data register tVM68k_ubyte memory[98304]; tVM68k_ulong memsize; // TODO: check for violations. /////// VERSION PATCH tVM68k_ubyte version; // game version. not the interpreter version } tVM68k; #endif dmagnetic-0.37/src/libdmagnetic/dMagnetic.h0000644000175000017500000001031414430741336017375 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef DMAGNETIC_H #define DMAGNETIC_H // return values of the functions #define DMAGNETIC_OK 0 #define DMAGNETIC_OK_QUIT 28844 #define DMAGNETIC_OK_RESTART 28816 #define DMAGNETIC_NOK_UNKNOWN_INSTRUCTION 1 #define DMAGNETIC_NOK_INVALID_PTR -1 #define DMAGNETIC_NOK_INVALID_PARAM -2 #define DMAGNETIC_NOK -23 // defines and data structures for the pictures #define PICTURE_BITS_PER_RGB_CHANNEL 10 #define PICTURE_MAX_RGB_VALUE ((1<>(2*PICTURE_BITS_PER_RGB_CHANNEL))&PICTURE_MAX_RGB_VALUE) #define PICTURE_GET_GREEN(p) (((p)>>(1*PICTURE_BITS_PER_RGB_CHANNEL))&PICTURE_MAX_RGB_VALUE) #define PICTURE_GET_BLUE(p) (((p)>>(0*PICTURE_BITS_PER_RGB_CHANNEL))&PICTURE_MAX_RGB_VALUE) #define PICTURE_MAX_WIDTH 640 // title screen of "Fish!" #define PICTURE_MAX_HEIGHT 350 // title screen of "Fish!" #define PICTURE_MAX_PIXELS (PICTURE_MAX_WIDTH*PICTURE_MAX_HEIGHT) typedef enum _eDmagneticPictureType { PICTURE_DEFAULT, PICTURE_HALFTONE, PICTURE_C64 } eDmagneticPictureType; typedef struct _tdMagneticPicture { unsigned int palette[16]; int height; int width; char pixels[PICTURE_MAX_PIXELS]; eDmagneticPictureType pictureType; } tdMagneticPicture; // callback pointers. typedef int (*cbLineANewOutput)(void* context,char* headline,char* text,char* picname); typedef int (*cbLineAInputString)(void* context,int* len,char* string); typedef int (*cbLineADrawPicture)(void* context,tdMagneticPicture* picture,char* picname,int mode); typedef int (*cbLineASaveGame)(void* context,char* filename,void* ptr,int len); typedef int (*cbLineALoadGame)(void* context,char* filename,void* ptr,int len); // interface functions/ int dMagnetic_getsize(int* bytes,int alignment); int dMagnetic_init(void* pHandle,int alignment,void* pMagBuf,int magsize,void* pGfxBuf,int gfxsize); int dMagnetic_getGameVersion(void* pHandle,int* version); int dMagnetic_run(void* pHandle); // config calls int dMagnetic_configrandom(void* pHandle,char random_mode,unsigned int random_seed); int dMagnetic_setEGAMode(void* pHandle,int egamode); int dMagnetic_setCBnewOutput(void* pHandle,cbLineANewOutput pCB,void* context); int dMagnetic_setCBinputString(void* pHandle,cbLineAInputString pCB,void* context); int dMagnetic_setCBdrawPicture(void* pHandle,cbLineADrawPicture pCB,void* context,tdMagneticPicture *pPicture); int dMagnetic_setCBsaveGame(void* pHandle,cbLineASaveGame pCB,void* context); int dMagnetic_setCBloadGame(void* pHandle,cbLineALoadGame pCB,void* context); // spoiler alert... int dMagnetic_dumppics(void* pHandle); // for debugging purposes typedef struct _tdMagneticVMstate { unsigned int pcr; unsigned int sr; unsigned int aregs[8]; unsigned int dregs[8]; unsigned short lastOpcode; void* pMem; int memsize; } tdMagneticVMstate; int dMagnetic_singleStep(void* pHandle); int dMagnetic_getVM(void* pHandle,void** pVM,int* vmsize); int dMagnetic_getVMstate(void* pHandle,tdMagneticVMstate *pVMstate); #endif dmagnetic-0.37/src/libdmagnetic/dMagnetic.c0000644000175000017500000002126014430741336017372 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "dMagnetic.h" #include "linea.h" #include "vm68k.h" #define MAGIC 0x314159 typedef struct _tHandle { unsigned int magic; void *hVM68k; int bytes_vm68k; void *hLineA; void *pMagBuf; int magbufsize; void *pGfxBuf; int gfxbufsize; int alignment; int version; unsigned short opcode; } tHandle; int dMagnetic_getsize(int* bytes,int alignment) { int bytes_handle; int bytes_vm68k; int bytes_lineA; int retval; if (bytes==NULL) { fprintf(stderr,"ERROR: NULL pointer in dMagnetic_getSize\n"); return DMAGNETIC_NOK_INVALID_PTR; } bytes_handle=sizeof(tHandle); retval=vm68k_getsize(&bytes_vm68k); if (retval) { fprintf(stderr,"ERROR: vm68k_getsize returned %d\n",retval); return retval; } retval=lineA_getsize(&bytes_lineA); if (retval) { fprintf(stderr,"ERROR: lineA_getsize returned %d\n",retval); return retval; } *bytes =bytes_handle+(alignment-(bytes_handle%alignment)); *bytes+=bytes_vm68k+ (alignment-(bytes_vm68k %alignment)); *bytes+=bytes_lineA+ (alignment-(bytes_lineA %alignment)); return retval; } int dMagnetic_init(void* pHandle,int alignment,void* pMagBuf,int magsize,void* pGfxBuf,int gfxsize) { tHandle *pThis=(tHandle*)pHandle; int retval; int bytes_handle; int bytes_vm68k; int bytes_lineA; int totalmem; int idx; void *pSharedMem; int bytes_shared; retval=DMAGNETIC_OK; // step 1: recalculate the actual size of the handle and the sections. bytes_handle=sizeof(tHandle); retval|=vm68k_getsize(&bytes_vm68k); retval|=lineA_getsize(&bytes_lineA); if (retval) { fprintf(stderr,"ERROR: getsize returned %d\n",retval); return retval; } totalmem=0; totalmem+=bytes_handle;totalmem+=(alignment-(totalmem%alignment)); totalmem+=bytes_vm68k; totalmem+=(alignment-(totalmem%alignment)); totalmem+=bytes_lineA; totalmem+=(alignment-(totalmem%alignment)); // step 2: let the init begin memset(pThis,0,totalmem); // tabula rasa pThis->magic=MAGIC; idx=0; idx+=bytes_handle;idx+=(alignment-(idx%alignment)); pThis->hVM68k=(void*)(&((unsigned char*)pHandle)[idx]); idx+=bytes_vm68k; idx+=(alignment-(idx%alignment)); pThis->hLineA=(void*)(&((unsigned char*)pHandle)[idx]); idx+=bytes_lineA; idx+=(alignment-(idx%alignment)); pThis->pMagBuf=pMagBuf; pThis->magbufsize=magsize; pThis->pGfxBuf=pGfxBuf; pThis->gfxbufsize=gfxsize; pThis->alignment=alignment; pThis->version=-1; // nothing loaded yet if (idx>totalmem) { fprintf(stderr,"ERROR: Initializing memory\n"); return DMAGNETIC_NOK; } retval|=vm68k_getpSharedMem(pThis->hVM68k,&pSharedMem,&bytes_shared); retval|=lineA_init(pThis->hLineA,pSharedMem,bytes_shared,pMagBuf,magsize,pGfxBuf,gfxsize); if (retval) { fprintf(stderr,"ERROR: lineA_init returned %d\n",retval); return retval; } retval=lineA_getVersion(pThis->hLineA,&pThis->version); if (retval) { fprintf(stderr,"ERROR: lineA_getversion returned %d\n",retval); return retval; } retval=vm68k_init(pThis->hVM68k,pThis->version); if (retval) { fprintf(stderr,"ERROR: vm68k_init returned %d\n",retval); return retval; } pThis->bytes_vm68k=bytes_vm68k; return retval; } int dMagnetic_getGameVersion(void* pHandle,int* version) { tHandle* pThis=(tHandle*)pHandle; int retval; if (pThis->magic!=MAGIC) { return DMAGNETIC_NOK_INVALID_PARAM; } retval=lineA_getVersion(pThis->hLineA,version); return retval; } int dMagnetic_singleStep(void* pHandle) { int retval; tHandle* pThis=(tHandle*)pHandle; pThis->opcode=0; retval=vm68k_getNextOpcode(pThis->hVM68k,&pThis->opcode); if (retval==VM68K_OK) retval=lineA_substitute_aliases(pThis->hLineA,&pThis->opcode); if (retval==LINEA_OK) retval=vm68k_singlestep(pThis->hVM68k,pThis->opcode); if (retval!=VM68K_OK) retval=lineA_singlestep(pThis->hLineA,pThis->hVM68k,pThis->opcode); if (retval!=LINEA_OK && retval!=LINEA_OK_QUIT && retval!=LINEA_OK_RESTART) { retval=DMAGNETIC_NOK_UNKNOWN_INSTRUCTION; } else if (retval==LINEA_OK_RESTART) { retval=DMAGNETIC_OK_RESTART; } else if (retval==LINEA_OK_QUIT) { retval=DMAGNETIC_OK_QUIT; } return retval; } int dMagnetic_run(void* pHandle) { tHandle* pThis=(tHandle*)pHandle; int retval; if (pThis->magic!=MAGIC) { return DMAGNETIC_NOK_INVALID_PARAM; } lineA_showTitleScreen(pThis->hLineA); // some versions of the game have a title screen do { retval=dMagnetic_singleStep(pHandle); } while (retval==DMAGNETIC_OK); return retval; } int dMagnetic_getVM(void* pHandle,void** pVM,int* vmsize) { tHandle* pThis=(tHandle*)pHandle; int retval; retval=DMAGNETIC_OK; if (pThis->magic!=MAGIC) { return DMAGNETIC_NOK_INVALID_PARAM; } *pVM=pThis->hVM68k; *vmsize=pThis->bytes_vm68k; return retval; } int dMagnetic_getVMstate(void* pHandle,tdMagneticVMstate *pVMstate) { tHandle* pThis=(tHandle*)pHandle; int retval; retval=DMAGNETIC_OK; if (pThis->magic!=MAGIC) { return DMAGNETIC_NOK_INVALID_PARAM; } pVMstate->lastOpcode=pThis->opcode; retval|=vm68k_getState(pThis->hVM68k,pVMstate->aregs,pVMstate->dregs,&(pVMstate->pcr),&(pVMstate->sr)); retval|=vm68k_getpSharedMem(pThis->hVM68k,&(pVMstate->pMem),&(pVMstate->memsize)); return retval; } //////////// int dMagnetic_configrandom(void* pHandle,char random_mode,unsigned int random_seed) { tHandle* pThis=(tHandle*)pHandle; int retval; retval=DMAGNETIC_OK; if (pThis->magic!=MAGIC) { return DMAGNETIC_NOK_INVALID_PARAM; } retval|=lineA_configrandom(pThis->hLineA,random_mode,random_seed); return retval; } int dMagnetic_setEGAMode(void* pHandle,int egamode) { tHandle* pThis=(tHandle*)pHandle; int retval; retval=DMAGNETIC_OK; if (pThis->magic!=MAGIC) { return DMAGNETIC_NOK_INVALID_PARAM; } retval|=lineA_setEGAMode(pThis->hLineA,egamode); return retval; } int dMagnetic_dumppics(void* pHandle) { tHandle* pThis=(tHandle*)pHandle; int retval; retval=DMAGNETIC_OK; if (pThis->magic!=MAGIC) { return DMAGNETIC_NOK_INVALID_PARAM; } retval|=lineA_dumppics(pThis->hLineA); return retval; } int dMagnetic_setCBnewOutput(void* pHandle,cbLineANewOutput pCB,void* context) { tHandle* pThis=(tHandle*)pHandle; int retval; retval=DMAGNETIC_OK; if (pThis->magic!=MAGIC) { return DMAGNETIC_NOK_INVALID_PARAM; } retval|=lineA_setCBnewOutput(pThis->hLineA,pCB,context); return retval; } int dMagnetic_setCBinputString(void* pHandle,cbLineAInputString pCB,void* context) { tHandle* pThis=(tHandle*)pHandle; int retval; retval=DMAGNETIC_OK; if (pThis->magic!=MAGIC) { return DMAGNETIC_NOK_INVALID_PARAM; } retval|=lineA_setCBinputString(pThis->hLineA,pCB,context); return retval; } int dMagnetic_setCBdrawPicture(void* pHandle,cbLineADrawPicture pCB,void* context,tdMagneticPicture *pPicture) { tHandle* pThis=(tHandle*)pHandle; int retval; retval=DMAGNETIC_OK; if (pThis->magic!=MAGIC) { return DMAGNETIC_NOK_INVALID_PARAM; } retval|=lineA_setCBdrawPicture(pThis->hLineA,pCB,context,pPicture); return retval; } int dMagnetic_setCBsaveGame(void* pHandle,cbLineASaveGame pCB,void* context) { tHandle* pThis=(tHandle*)pHandle; int retval; retval=DMAGNETIC_OK; if (pThis->magic!=MAGIC) { return DMAGNETIC_NOK_INVALID_PARAM; } retval|=lineA_setCBsaveGame(pThis->hLineA,pCB,context); return retval; } int dMagnetic_setCBloadGame(void* pHandle,cbLineALoadGame pCB,void* context) { tHandle* pThis=(tHandle*)pHandle; int retval; retval=DMAGNETIC_OK; if (pThis->magic!=MAGIC) { return DMAGNETIC_NOK_INVALID_PARAM; } retval|=lineA_setCBloadGame(pThis->hLineA,pCB,context); return retval; } dmagnetic-0.37/src/frontends/0000755000175000017500000000000014430741336014712 5ustar useruserdmagnetic-0.37/src/frontends/helloworld/0000755000175000017500000000000014430741336017065 5ustar useruserdmagnetic-0.37/src/frontends/helloworld/README.txt0000644000175000017500000000047214430741336020566 0ustar useruserThis directory contains an example for interested developers. It can be build with sh build.sh and be run with ./helloworld GAME.mag GAME.gfx Even though the output will not be pretty, it illustrates what is needed to write ones own frontend to dMagnetic. The source code is what is more interesting here. dmagnetic-0.37/src/frontends/helloworld/helloworld.c0000644000175000017500000001051214430741336021403 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "dMagnetic.h" #define MAXMAGSIZE 184000 // the largest .mag file has 183915 bytes (wonder.mag) #define MAXGFXSIZE 2540000 // the largest .gfx file has 2534110 bytes (wonder.gfx) #define ALIGNMENT 64 // influences how memory is allocated internally int cb_output(void* context,char* headline,char* text,char* picname) { printf("\x1b[1;37;41m headline: [%s]\x1b[0m\n",headline); printf("\x1b[1;37;44m picture: %s\x1b[0m\n",picname); printf("text: [%s]\n",text); return 0; } int cb_input(void* context,int* len,char* string) { printf("\x1b[0;30;46m\n"); if (feof(stdin)) exit(0); if (fgets(string,256,stdin)==NULL) exit(0); *len=strlen(string); printf("\x1b[0m\n"); return 0; } int cb_picture(void* context,tdMagneticPicture* picture,char* picname,int mode) { int i; printf("\x1b[1;37;42m picture %s >> width:%d height:%d\x1b[0m\n",picname,picture->width,picture->height); #if 0 for (i=0;iheight*picture->width;i++) { if (i%(picture->width)==0) printf("\n"); printf("%x",picture->pixels[i]); } printf("\n"); #endif return 0; } int main(int argc,char** argv) { int magsize; int gfxsize; void* handle; unsigned char* magbuf; unsigned char* gfxbuf; int bytes; int retval; FILE *f; tdMagneticPicture* picture; if (argc!=3) { printf("please run with %s INPUT.mag INPUT.gfx\n",argv[0]); return 1; } printf("allocating %d bytes for the mag buffer\n",MAXMAGSIZE); magbuf=malloc(MAXMAGSIZE); printf("allocating %d bytes for the gfx buffer\n",MAXGFXSIZE); gfxbuf=malloc(MAXGFXSIZE); dMagnetic_getsize(&bytes,ALIGNMENT); printf("allocating %d bytes for the engine\n",bytes); handle=malloc(bytes); printf("allocating %d bytes for the pictures\n",sizeof(tdMagneticPicture)); picture=malloc(sizeof(tdMagneticPicture)); do { f=fopen(argv[1],"rb"); magsize=fread(magbuf,sizeof(char),MAXMAGSIZE,f); fclose(f); f=fopen(argv[2],"rb"); gfxsize=fread(gfxbuf,sizeof(char),MAXGFXSIZE,f); fclose(f); dMagnetic_init(handle,ALIGNMENT,magbuf,magsize,gfxbuf,gfxsize); // initialize the virtual machine dMagnetic_setCBnewOutput(handle,cb_output,NULL); // set the callback for output dMagnetic_setCBinputString(handle,cb_input,NULL); // set the callback for input dMagnetic_setCBdrawPicture(handle,cb_picture,NULL,picture); // set the callback for drawing pictures #if 1 retval=dMagnetic_run(handle); // run the virtual machine #else dMagnetic_dumppics(handle); // decode all the pictures do { int i; tdMagneticVMstate vmState; retval=dMagnetic_singleStep(handle); // perform one command at a time dMagnetic_getVMstate(handle,&vmState); // read the state of the VM. to print it. printf("\x1b[1;33m %04x PCR:%08x SR:%02x ",vmState.lastOpcode,vmState.pcr,vmState.sr); for (i=0;i<8;i++) printf("A%d:%08x ",i,vmState.aregs[i]); for (i=0;i<8;i++) printf("D%d:%08x ",i,vmState.dregs[i]); printf("\x1b[0m\n"); } while (retval==DMAGNETIC_OK); #endif } while (retval==DMAGNETIC_OK_RESTART); free(picture); free(handle); free(gfxbuf); free(magbuf); return retval; } dmagnetic-0.37/src/frontends/helloworld/build.sh0000644000175000017500000000024214430741336020516 0ustar useruser#!/bin/sh ( cd ../../../ make libdmagnetic.a ) gcc -c -o helloworld.o helloworld.c -I../../libdmagnetic gcc -o helloworld helloworld.o -L../../../ -ldmagnetic dmagnetic-0.37/src/frontends/default/0000755000175000017500000000000014430741336016336 5ustar useruserdmagnetic-0.37/src/frontends/default/configuration.h0000644000175000017500000000301714430741336021357 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CONFIGURATION_H #define CONFIGURATION_H int retrievefromini(FILE *f,char* section,char* entry,char* retstring,int retstringsize); int retrievefromcommandline(int argc,char** argv,char* parameter, char* retstring,int retstringspace); #endif dmagnetic-0.37/src/frontends/default/helpscreens.c0000644000175000017500000003716114430741336021025 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "default_callbacks.h" #include "helpscreens.h" #include "pathnames.h" #include "version.h" #define NUMGAMES 7 #define NUMPLATFORMS 10 #define PLATFORM_MAG (1<<0) #define PLATFORM_GFX (1<<1) #define PLATFORM_MSDOS (1<<2) #define PLATFORM_TWORSC (1<<3) #define PLATFORM_D64 (1<<4) #define PLATFORM_AMSTRADCPC (1<<5) #define PLATFORM_SPECTRUM (1<<6) #define PLATFORM_ARCHIMEDES (1<<7) #define PLATFORM_ATARIXL (1<<8) #define PLATFORM_APPLEII (1<<9) typedef struct _tPlatformInfo { char* name; char* dir; char* suffix; char uppercase; char special; int disksexpected; // some games require komma seperated names for the images. PAWN1.D64,PAWN2.D64, for example int active; unsigned short mask; char *cmdline; char *helptext; } tPlatformInfo; typedef struct _tGameInfo { char *name; char *description; char *maggfxname; int disknum; char *specialPrefix; unsigned short ported; } tGameInfo; const tPlatformInfo cdMagnetic_platformInfo[NUMPLATFORMS]={ {.name="mag", .dir=PATH_USR_LOCAL_SHARE_GAMES"magneticscrolls/", .suffix=".mag" , .uppercase=0, .special=0, .disksexpected=1, .active=1, .mask=PLATFORM_MAG, .cmdline="-mag MAGFILE.mag", .helptext="to provide the game binary\n" }, {.name="gfx", .dir=PATH_USR_LOCAL_SHARE_GAMES"magneticscrolls/", .suffix=".gfx", .uppercase=0, .special=0, .disksexpected=1, .active=1, .mask=PLATFORM_GFX, .cmdline="-gfx GFXFILE.gfx", .helptext="to provide the game graphics\n" }, {.name="msdos", .dir="/MSDOS/C/", .suffix="", .uppercase=1, .special=0, .disksexpected=1, .active=0, .mask=PLATFORM_MSDOS, .cmdline="-msdosdir DIRECTORY/", .helptext="to provide the binaries from MSDOS\n" }, {.name="tworsc", .dir=PATH_USR_LOCAL_SHARE"games/", .suffix="TWO.RSC", .uppercase=0, .special=1, .disksexpected=1, .active=0, .mask=PLATFORM_TWORSC, .cmdline="-tworsc DIRECTORY/TWO.RSC", .helptext="to use resource files from Wonderland\n\tor The Magnetic Scrolls Collection Vol.1\n" }, {.name="d64", .dir="/8/", .suffix=".D64" , .uppercase=1, .special=0, .disksexpected=2, .active=0, .mask=PLATFORM_D64, .cmdline="-d64 IMAGE1.d64,IMAGE2.d64", .helptext="use d64 images. (Separated by ,)\n" }, {.name="amstradcpc", .dir="/dsk/amstradcpc/", .suffix=".DSK", .uppercase=1, .special=0, .disksexpected=2, .active=0, .mask=PLATFORM_AMSTRADCPC, .cmdline="-amstradcpc IMAGE1.DSK,IMAGE2.DSK", .helptext="use DSK images. (Separated by ,)\n" }, {.name="spectrum", .dir="/dsk/spectrum/", .suffix=".DSK" , .uppercase=0, .special=0, .disksexpected=1, .active=0, .mask=PLATFORM_SPECTRUM, .cmdline="-spectrum IMAGE.DSK", .helptext="use DSK images for the Spectrum+3\n" }, {.name="archimedes", .dir="/adf/", .suffix=".adf", .uppercase=0, .special=0, .disksexpected=1, .active=0, .mask=PLATFORM_ARCHIMEDES, .cmdline="-archimedes IMAGE.adf", .helptext="use adf/adl images from the Archimedes\n" }, {.name="atarixl", .dir="/atr/", .suffix=".ATR" , .uppercase=1, .special=0, .disksexpected=2, .active=0, .mask=PLATFORM_ATARIXL, .cmdline="-atarixl IMAGE1.atr,IMAGE2.atr", .helptext="use .atr images from the AtariXL\n" }, {.name="appleii", .dir="/appleii/", .suffix=".NIB", .uppercase=1, .special=0, .disksexpected=3, .active=0, .mask=PLATFORM_APPLEII, .cmdline="-appleii 1.NIB,2.2MG,3.WOZ", .helptext="use Apple ][ images\n" } }; const tGameInfo cdMagnetic_gameInfo[NUMGAMES]={ {.name="pawn", .description="The Pawn", .maggfxname="pawn", .disknum=2, .specialPrefix="", .ported=PLATFORM_MAG|PLATFORM_GFX|PLATFORM_MSDOS|PLATFORM_D64|PLATFORM_AMSTRADCPC|PLATFORM_SPECTRUM|PLATFORM_ARCHIMEDES|PLATFORM_ATARIXL |PLATFORM_APPLEII}, {.name="guild", .description="The Guild of Thieves", .maggfxname="guild", .disknum=2, .specialPrefix="MSC/G", .ported=PLATFORM_MAG|PLATFORM_GFX|PLATFORM_MSDOS|PLATFORM_D64|PLATFORM_AMSTRADCPC|PLATFORM_SPECTRUM|PLATFORM_ARCHIMEDES|PLATFORM_ATARIXL|PLATFORM_TWORSC|PLATFORM_APPLEII}, {.name="jinxter", .description="Jinxter", .maggfxname="jinxter", .disknum=2, .specialPrefix="", .ported=PLATFORM_MAG|PLATFORM_GFX|PLATFORM_MSDOS|PLATFORM_D64|PLATFORM_AMSTRADCPC|PLATFORM_SPECTRUM|PLATFORM_ARCHIMEDES|PLATFORM_ATARIXL |PLATFORM_APPLEII}, {.name="corruption", .description="Corruption", .maggfxname="corrupt", .disknum=3, .specialPrefix="MSC/C", .ported=PLATFORM_MAG|PLATFORM_GFX|PLATFORM_MSDOS|PLATFORM_D64|PLATFORM_AMSTRADCPC|PLATFORM_SPECTRUM|PLATFORM_ARCHIMEDES |PLATFORM_TWORSC|PLATFORM_APPLEII}, {.name="fish", .description="Fish!", .maggfxname="fish", .disknum=2, .specialPrefix="MSC/F", .ported=PLATFORM_MAG|PLATFORM_GFX|PLATFORM_MSDOS|PLATFORM_D64|PLATFORM_AMSTRADCPC|PLATFORM_SPECTRUM|PLATFORM_ARCHIMEDES |PLATFORM_TWORSC }, {.name="myth", .description="Myth", .maggfxname="myth", .disknum=1, .specialPrefix="", .ported=PLATFORM_MAG|PLATFORM_GFX|PLATFORM_MSDOS|PLATFORM_D64|PLATFORM_AMSTRADCPC|PLATFORM_SPECTRUM }, {.name="wonderland", .description="Wonderland", .maggfxname="wonder", .disknum=1, .specialPrefix="wonderland/", .ported=PLATFORM_MAG|PLATFORM_GFX |PLATFORM_TWORSC }, }; void helpscreens_header() { fprintf(stderr,"*** dMagnetic %d.%d%d\n",VERSION_MAJOR,VERSION_MINOR,VERSION_REVISION); fprintf(stderr,"*** Use at your own risk\n"); fprintf(stderr,"*** Copyright 2023 by dettus@dettus.net\n"); fprintf(stderr,"***************************************\n"); fprintf(stderr,"\n"); } void helpscreens_license() { printf("Copyright 2023, dettus@dettus.net\n"); printf("\n"); printf("Redistribution and use in source and binary forms, with or without modification,\n"); printf("are permitted provided that the following conditions are met:\n"); printf("\n"); printf("1. Redistributions of source code must retain the above copyright notice, this \n"); printf(" list of conditions and the following disclaimer.\n"); printf("\n"); printf("2. Redistributions in binary form must reproduce the above copyright notice, \n"); printf(" this list of conditions and the following disclaimer in the documentation \n"); printf(" and/or other materials provided with the distribution.\n"); printf("\n"); printf("THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n"); printf("ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED \n"); printf("WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \n"); printf("DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE \n"); printf("FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL \n"); printf("DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR \n"); printf("SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \n"); printf("CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, \n"); printf("OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE \n"); printf("OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"); printf("\n"); } void helpscreens_basic(char* argv0,FILE *output,int mention_help) { fprintf(output,"Please provide a .mag or a .gfx file by using one of\n"); fprintf(output,"\n"); fprintf(output,"%s -mag GAMEFILE.mag\n",argv0); fprintf(output,"%s -gfx GAMEFILE.gfx\n",argv0); fprintf(output,"\n"); if (mention_help) { fprintf(output,"To see other options, or to get more help, use\n"); fprintf(output,"\n"); fprintf(output,"%s -help\n",argv0); fprintf(output,"%s -license\n",argv0); fprintf(output,"\n"); } } void helpscreens_general(char* argv0,FILE *output) { int i,j,maxlen; helpscreens_basic(argv0,output,0); fprintf(output,"\n"); fprintf(output,"For conveniance, games can be referenced in an .ini file\n"); fprintf(output,"\n"); fprintf(output,"%s [-ini dMagnetic.ini] GAME\n",argv0); fprintf(output,"where GAME is one of \n["); maxlen=0; for (i=0;idMagnetic.ini generate a generic inifile\n"); fprintf(output,"\n"); fprintf(output,"\n"); fprintf(output,"SELECTING INPUT SOURCES DIRECTLY\n"); maxlen=0; for (i=0;imaxlen) { maxlen=l; } } for (i=0;i #include #include #include "version.h" #include "configuration.h" #ifdef EXPERIMENTAL_IFI #include "ifi_callbacks.h" #else #include "default_callbacks.h" #endif #include "maggfxloader.h" #include "helpscreens.h" #include "pathnames.h" #include "dMagnetic.h" #define MAXMAGSIZE 184000 // the largest .mag file is 183915 bytes. (Wonder.mag) #define MAXGFXSIZE 3000000 // the largest .gfx file is 2534110 bytes. HOWEVER, this buffer is shared with the loader module. int cbDumpPicture(void* context,tdMagneticPicture* pPicture,char* picname,int mode) { int i; int j; FILE *f; char filename[1024]; snprintf(filename,1024,"%s%s.xpm",(char*)context,picname); f=fopen(filename,"wb"); if (!f) { fprintf(stderr,"Unable to open [%s] for writing\n",filename); return -1; } printf("*** Dumping %s\n",filename); fprintf(f,"/* XPM */\n"); fprintf(f,"static char *xpm[] = {\n"); fprintf(f,"/* columns rows colors chars-per-pixel */\n"); fprintf(f,"\"%d %d 16 1 \",\n",pPicture->width,pPicture->height); for (i=0;i<16;i++) { unsigned int red,green,blue; red =(pPicture->palette[i]>>(2*PICTURE_BITS_PER_RGB_CHANNEL))&PICTURE_MAX_RGB_VALUE; green=(pPicture->palette[i]>>(1*PICTURE_BITS_PER_RGB_CHANNEL))&PICTURE_MAX_RGB_VALUE; blue =(pPicture->palette[i]>>(0*PICTURE_BITS_PER_RGB_CHANNEL))&PICTURE_MAX_RGB_VALUE; red*=0xff;green*=0xff;blue*=0xff; red/=PICTURE_MAX_RGB_VALUE;green/=PICTURE_MAX_RGB_VALUE;blue/=PICTURE_MAX_RGB_VALUE; fprintf(f,"\"%x c #%02X%02X%02X\",\n",i,red,green,blue); } fprintf(f,"/* pixels */\n"); for (i=0;iheight;i++) { fprintf(f,"\""); for (j=0;jwidth;j++) { fprintf(f,"%x",pPicture->pixels[i*pPicture->width+j]); } fprintf(f,"\""); if (i!=(pPicture->height-1)) fprintf(f,","); fprintf(f,"\n"); } fprintf(f,"};\n"); fclose(f); return 0; } int init(int argc,char** argv,FILE *f_inifile, int *pNodoc, eBinType* pBinType,char* magfilename,char* gfxfilename,char* binfilename) { int gamenamegiven; int retval; if (magfilename==NULL || gfxfilename==NULL || binfilename==NULL) { return -1; } *pNodoc=0; *pBinType=BINTYPE_NONE; retval=0; magfilename[0]=gfxfilename[0]=binfilename[0]=0; gamenamegiven=0; if ((retrievefromcommandline(argc,argv,"pawn",NULL,0)) || (retrievefromcommandline(argc,argv,"guild",NULL,0)) || (retrievefromcommandline(argc,argv,"jinxter",NULL,0)) || (retrievefromcommandline(argc,argv,"corruption",NULL,0)) || (retrievefromcommandline(argc,argv,"fish",NULL,0)) || (retrievefromcommandline(argc,argv,"myth",NULL,0)) || (retrievefromcommandline(argc,argv,"wonderland",NULL,0)) || (retrievefromcommandline(argc,argv,"wonder",NULL,0))) { gamenamegiven=1; } if (!f_inifile && gamenamegiven) { fprintf(stderr,"Game name was given, but no suitable .ini file found\n"); fprintf(stderr,"please run %s -helpini for more help\n",argv[0]); return 1; } { int i; char* gameprefix[]={"pawn","guild","jinxter","corruption","fish","myth","wonderland","wonder"}; char magname[32]; char gfxname[32]; char tworscname[32]; char msdosname[32]; char d64name[32]; char amstradcpcname[32]; char spectrumname[32]; char archimedesname[32]; char atarixlname[32]; char appleiiname[32]; for (i=0;i<8;i++) { snprintf(magname,32,"%smag",gameprefix[i]); snprintf(gfxname,32,"%sgfx",gameprefix[i]); snprintf(tworscname,32,"%stworsc",gameprefix[i]); snprintf(msdosname,32,"%smsdos",gameprefix[i]); snprintf(d64name,32,"%sd64",gameprefix[i]); snprintf(amstradcpcname,32,"%samstradcpc",gameprefix[i]); snprintf(spectrumname,32,"%sspectrum",gameprefix[i]); snprintf(archimedesname,32,"%sarchimedes",gameprefix[i]); snprintf(atarixlname,32,"%satarixl",gameprefix[i]); snprintf(appleiiname,32,"%sappleii",gameprefix[i]); if (retrievefromcommandline(argc,argv,gameprefix[i],NULL,0)) { magfilename[0]=gfxfilename[0]=0; if (retrievefromini(f_inifile,"[FILES]",magname,magfilename,MAXFILENAMESIZE)&& retrievefromini(f_inifile,"[FILES]",gfxname,gfxfilename,MAXFILENAMESIZE)) { *pBinType=BINTYPE_MAGGFX; } else if (retrievefromini(f_inifile,"[FILES]",tworscname,binfilename,MAXFILENAMESIZE)) { *pBinType=BINTYPE_TWORSC; } else if (retrievefromini(f_inifile,"[FILES]",msdosname,binfilename,MAXFILENAMESIZE)) { *pBinType=BINTYPE_MSDOS; } else if (retrievefromini(f_inifile,"[FILES]",d64name,binfilename,MAXFILENAMESIZE)) { *pBinType=BINTYPE_D64; } else if (retrievefromini(f_inifile,"[FILES]",amstradcpcname,binfilename,MAXFILENAMESIZE)) { *pBinType=BINTYPE_AMSTRADCPC; } else if (retrievefromini(f_inifile,"[FILES]",spectrumname,binfilename,MAXFILENAMESIZE)) { *pBinType=BINTYPE_SPECTRUM; } else if (retrievefromini(f_inifile,"[FILES]",archimedesname,binfilename,MAXFILENAMESIZE)) { *pBinType=BINTYPE_ARCHIMEDES; } else if (retrievefromini(f_inifile,"[FILES]",atarixlname,binfilename,MAXFILENAMESIZE)) { *pBinType=BINTYPE_ATARIXL; } else if (retrievefromini(f_inifile,"[FILES]",appleiiname,binfilename,MAXFILENAMESIZE)) { *pBinType=BINTYPE_APPLEII; } } } } if (retrievefromcommandline(argc,argv,"-mag",magfilename,MAXFILENAMESIZE)) { gfxfilename[0]=0; binfilename[0]=0; *pBinType=BINTYPE_MAGGFX; } if (retrievefromcommandline(argc,argv,"-gfx",gfxfilename,MAXFILENAMESIZE)) { binfilename[0]=0; *pBinType=BINTYPE_MAGGFX; } if (retrievefromcommandline(argc,argv,"-msdosdir",binfilename,MAXFILENAMESIZE)) { *pBinType=BINTYPE_MSDOS; } if (retrievefromcommandline(argc,argv,"-tworsc",binfilename,MAXFILENAMESIZE)) { *pBinType=BINTYPE_TWORSC; } if (retrievefromcommandline(argc,argv,"-d64",binfilename,MAXFILENAMESIZE)) { *pBinType=BINTYPE_D64; } if (retrievefromcommandline(argc,argv,"-amstradcpc",binfilename,MAXFILENAMESIZE)) { *pBinType=BINTYPE_AMSTRADCPC; } if (retrievefromcommandline(argc,argv,"-spectrum",binfilename,MAXFILENAMESIZE)) { *pBinType=BINTYPE_SPECTRUM; } if (retrievefromcommandline(argc,argv,"-archimedes",binfilename,MAXFILENAMESIZE)) { *pBinType=BINTYPE_ARCHIMEDES; } if (retrievefromcommandline(argc,argv,"-atarixl",binfilename,MAXFILENAMESIZE)) { *pBinType=BINTYPE_ATARIXL; } if (retrievefromcommandline(argc,argv,"-appleii",binfilename,MAXFILENAMESIZE)) { *pBinType=BINTYPE_APPLEII; } { char result[64]; *pNodoc=0; if (retrievefromini(f_inifile,"[GAMEPLAY]","nodoc",result,sizeof(result))) { if (result[0]=='y' || result[0]=='Y') *pNodoc=1; // "yes","Yes" --> 1. otherwise 0 } if (retrievefromcommandline(argc,argv,"-nodoc",NULL,0)) { *pNodoc=1; } } if (magfilename[0]==0 && gfxfilename[0]==0 && binfilename[0]==0) { int i; eFileType fileType; // last chance: // check if the commandline had a .mag or .gfx file referenced directly retval=-1; for (i=1;i=MAXFILENAMESIZE) l=MAXFILENAMESIZE-1; memcpy(magfilename,argv[i],l+1); printf("Using [%s] as .mag file \n",magfilename); retval=0; *pBinType=BINTYPE_MAGGFX; } if (fileType==FILETYPE_GFX) { int l; l=strlen(argv[i]); if (l>=MAXFILENAMESIZE) l=MAXFILENAMESIZE-1; memcpy(gfxfilename,argv[i],l+1); printf("Using [%s] as .gfx file\n",gfxfilename); retval=0; *pBinType=BINTYPE_MAGGFX; } } } return retval; } int dumpmaggfx(int argc,char** argv,char* magbuf,int magsize,char* gfxbuf,int gfxsize) { FILE *f; int finish; finish=0; char filename[MAXFILENAMESIZE]; if (retrievefromcommandline(argc,argv,"-dumpmag",filename,MAXFILENAMESIZE)) { finish=1; printf("Writing new .mag file [%s]\n",filename); f=fopen(filename,"wb"); if (!f) { fprintf(stderr,"unable to open [%s]\n",filename); } fwrite(magbuf,sizeof(char),magsize,f); fclose(f); } if (retrievefromcommandline(argc,argv,"-dumpgfx",filename,MAXFILENAMESIZE)) { finish=1; printf("Writing new .gfx file [%s]\n",filename); f=fopen(filename,"wb"); if (!f) { fprintf(stderr,"unable to open [%s]\n",filename); } fwrite(gfxbuf,sizeof(char),gfxsize,f); fclose(f); } if (finish) { printf("finishing now\n"); exit(0); } return 0; } int main(int argc,char** argv) { int i; char inifilename[1024]; int retval; FILE *f_inifile=NULL; void* hGUI; int sizeGUI; void *hEngine; char *homedir; char random_mode; unsigned int random_seed; int egamode; int dumppics; char* magbuf; char* gfxbuf; tdMagneticPicture *picture; eBinType binType; char magfilename[MAXFILENAMESIZE]; char gfxfilename[MAXFILENAMESIZE]; char binfilename[MAXFILENAMESIZE]; int nodoc; int handlesize; // figure out the location of the inifile. if (!(retrievefromcommandline(argc,argv,"--version",NULL,0))) { helpscreens_header(); #define LOCNUM 16 const char *locations[LOCNUM]={ PATH_ETC, PATH_USR_LOCAL_SHARE, PATH_USR_LOCAL_SHARE_GAMES, PATH_USR_LOCAL_SHARE"dMagnetic/", PATH_USR_LOCAL_SHARE_GAMES"dMagnetic/", PATH_USR_LOCAL_GAMES, PATH_USR_LOCAL_GAMES"dMagnetic/", PATH_USR_SHARE, PATH_USR_SHARE_GAMES, PATH_USR_SHARE_GAMES"dMagnetic/", PATH_USR_SHARE"dMagnetic/", PATH_USR_GAMES, PATH_USR_GAMES"dMagnetic/", PATH_USR_SHARE"doc/dmagnetic/", PATH_USR_PKG_SHARE"doc/dMagnetic/", "./"}; // this should always be the last one. f_inifile=NULL; if (f_inifile==NULL) { homedir=getenv("HOME"); snprintf(inifilename,1023,"%s/dMagnetic.ini",homedir); f_inifile=fopen(inifilename,"rb"); } if (f_inifile==NULL) { homedir=getenv("HOME"); snprintf(inifilename,1023,"%s/.dMagnetic.ini",homedir); f_inifile=fopen(inifilename,"rb"); } for (i=0;i0x7fffffff) { printf("illegal random seed. please use a value between %d and %d\n",1,0x7fffffff); return 1; } } } if (argc) { char result[64]; if (retrievefromcommandline(argc,argv,"-rmode",result,sizeof(result))) { if (result[0]=='p') random_mode=0; else if (result[0]=='r') random_mode=1; else { printf("illegal parameter for -rmode. please use one of "); printf("pseudo "); printf("real "); printf("\n"); return 1; } } if (retrievefromcommandline(argc,argv,"-rseed",result,sizeof(result))) { random_seed=atoi(result); if (random_seed<1 || random_seed>0x7fffffff) { printf("illegal parameter for -rseed. please use a value between %d and %d\n",1,0x7fffffff); return 1; } } } egamode=0; if (f_inifile) { char result[64]; if (retrievefromcommandline(argc,argv,"-ega",result,sizeof(result))) { egamode=1; } } dumppics=0; if (f_inifile) { char result[64]; if (retrievefromcommandline(argc,argv,"-dumppics",result,sizeof(result))) { dumppics=1; } } if (f_inifile) fclose(f_inifile); magbuf=malloc(MAXMAGSIZE); gfxbuf=malloc(MAXGFXSIZE); picture=malloc(sizeof(tdMagneticPicture)); if (magbuf==NULL || gfxbuf==NULL) { fprintf(stderr,"ERROR: unable to allocate memory for the data files\n"); if (picture!=NULL) free(picture); if (magbuf!=NULL) free(magbuf); if (gfxbuf!=NULL) free(gfxbuf); return -1; } // this is the main loop. f_inifile=fopen(inifilename,"rb"); if (init(argc,argv,f_inifile,&nodoc,&binType,magfilename,gfxfilename,binfilename)) { helpscreens_loaderfailed(argv[0]); free(picture); free(magbuf); free(gfxbuf); fclose(f_inifile); return 1; } retval=dMagnetic_getsize(&handlesize,64); hEngine=malloc(handlesize); if (retval || hEngine==NULL) { fprintf(stderr,"Unable to allocate memory for backend\n"); free(picture); free(magbuf); free(gfxbuf); fclose(f_inifile); } default_setEngine(hGUI,hEngine); do { int magsize; int gfxsize; magsize=MAXMAGSIZE; gfxsize=MAXGFXSIZE; if (maggfxloader(magbuf,&magsize,gfxbuf,&gfxsize,nodoc,binType,magfilename,gfxfilename,binfilename)) // reload everything. { helpscreens_loaderfailed(argv[0]); return 1; } if (dumpmaggfx(argc,argv,magbuf,magsize,gfxbuf,gfxsize)) // this needs to be called after the magbuffer and gfx buffer have been filled { helpscreens_loaderfailed(argv[0]); return 1; } dMagnetic_init(hEngine,64,magbuf,magsize,gfxbuf,gfxsize); // the buffer have been filled. if (dumppics) { retval=dMagnetic_setCBdrawPicture(hEngine,cbDumpPicture,"",picture); retval|=dMagnetic_dumppics(hEngine); return retval; } retval=0; // configure after the reinit retval|=dMagnetic_configrandom(hEngine,random_mode,random_seed); retval|=dMagnetic_setEGAMode(hEngine,egamode); // set the call back hooks for this GUI retval|=dMagnetic_setCBnewOutput(hEngine,default_cbNewOutput,hGUI); retval|=dMagnetic_setCBinputString(hEngine,default_cbInputString, hGUI); retval|=dMagnetic_setCBdrawPicture(hEngine,default_cbDrawPicture, hGUI,picture); retval|=dMagnetic_setCBloadGame(hEngine,default_cbLoadGame, hGUI); retval|=dMagnetic_setCBsaveGame(hEngine,default_cbSaveGame, hGUI); if (retval) { fprintf(stderr,"ERROR: setting the API hooks failed\n"); return 1; } // final warning! ;) { int version; retval=dMagnetic_getGameVersion(hEngine,&version); if (version==4 && retval==0) { fprintf(stderr,"\n"); fprintf(stderr,"---------------------------------------\n"); fprintf(stderr,"- Version 4 of the VM requires you to -\n"); fprintf(stderr,"- activate the graphics manually. Use -\n"); fprintf(stderr,"- the command GRAPHICS now -\n"); fprintf(stderr,"---------------------------------------\n"); fprintf(stderr,"\n"); } } // here we go! retval=dMagnetic_run(hEngine); if (retval==DMAGNETIC_NOK_UNKNOWN_INSTRUCTION) { int i; tdMagneticVMstate vmState; dMagnetic_getVMstate(hEngine,&vmState); fprintf(stderr,"Unknown opcode %04X \n",((unsigned int)vmState.lastOpcode)&0xffff); fprintf(stderr,"PCR=0x%08X SR=0x%02X %c%c%c%c%c\n",vmState.pcr,vmState.sr, (vmState.sr&0x10)?'X':'x', (vmState.sr&0x08)?'N':'n', (vmState.sr&0x04)?'Z':'z', (vmState.sr&0x02)?'V':'v', (vmState.sr&0x01)?'C':'c' );//cvznx for (i=0;i<8;i++) { fprintf(stderr,"A%d=0x%08X D%d=0x%08X\n",i,vmState.aregs[i],i,vmState.dregs[i]); } } } while (retval==DMAGNETIC_OK_RESTART); free(picture); free(hEngine); free(gfxbuf); free(magbuf); // this concludes the main loop free(hGUI); return 0; } dmagnetic-0.37/src/frontends/default/default_callbacks.c0000644000175000017500000005373114430741336022136 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // the purpose of this file is to provide a callback-fallback until proper // user interfaces have been established. It will do ;) #include #include #include #include "default_render.h" #include "default_palette.h" #include "dMagnetic.h" #include "configuration.h" #include "default_callbacks.h" #ifdef EXPERIMENTAL_SAVEGAME_SLOTS #include typedef struct _tSaveGameSlots { int valid; char filename[65]; int gamelen; unsigned char gamedata[131072]; int year; int month; int day; int hour; int minute; int sec; } tSaveGameSlots; #endif #define MAGIC 0x68654879 // = yHeh, the place where I grew up ;) #define MAX_SRESX 4096 #define ALIGNMENT_LEFT 1 #define ALIGNMENT_BLOCK 2 #define ALIGNMENT_RIGHT 3 typedef enum _tMode { eMODE_NONE, eMODE_MONOCHROME, eMODE_LOW_ANSI, eMODE_LOW_ANSI2, eMODE_HIGH_ANSI, eMODE_HIGH_ANSI2, eMODE_SIXEL, eMODE_UTF } tMode; typedef struct _tContext { unsigned int magic; // ansi output int columns; int rows; tMode mode; // 0=none. 1=ascii art. 2=ansi art. 3=high ansi. 4=high ansi2 // log the input FILE* f_logfile; int echomode; int capital; // =1 if the next character should be a capital letter. int lastchar; int jinxterslide; // THIS workaround makes the sliding puzzle in Jinxter solvable int headlineflagged; char low_ansi_characters[128]; // characters that are allowed for low ansi rendering char monochrome_characters[128]; // characters that are allowed for monochrome rendering int monochrome_inverted; // this is the line buffer. // text is stored here, before // being printed on the console. int textalign; // 0=left aligned. 1=block aligned. 2=right aligned. int skip_newline; // sixel parameter int screenheight; int screenwidth; int forceres; // for the "advanced saves", where the whole virtual machine is being written to a file void *hEngine; int advancedSaves; #ifdef EXPERIMENTAL_SAVEGAME_SLOTS tSaveGameSlots saveGameSlots[10]; char advancedSavesFilename[1024]; #endif } tContext; int default_printline(char* text,int startidx,int endidx,int spaceidx,int columns,int newline,int end,int alignment) { int i; int delta; int spaces; int outcnt; char c; if (!newline) // in case there is a newline, the line ends there. { endidx=spaceidx; // otherwise, there is a word after which the line ends. } endidx--; // the last character was either a space or a newline delta=columns-endidx+startidx; // how much 'room' is there? if (alignment==ALIGNMENT_RIGHT && delta<(columns-1)) // in right alignment { if (newline) delta++; for (i=0;i=spaces) { accu-=spaces; printf(" "); } } if (outcnt || c!='\n') { printf("%c",c); } outcnt++; } } if (!end) printf("\n"); return 0; } int default_cbNewOutput(void* context,char* headline,char* text,char* picname) { tContext *pContext=(tContext*)context; int i; // printf("\x1b[1;37;41m%s\x1b[0m\n",headline); // printf("\x1b[1;37;42m%s\x1b[0m\n",text); // printf("\x1b[1;47;44m%s\x1b[0m\n",picname); if (headline[0]) // when there is a headline, print it { if (pContext->mode==eMODE_NONE) { printf("\n[%s]\n",headline); } else { if (pContext->skip_newline) { printf("\n"); pContext->skip_newline=0; } for (i=0;icolumns-strlen(headline)-3;i++) { printf("-"); } if (pContext->mode==eMODE_LOW_ANSI || pContext->mode==eMODE_HIGH_ANSI) // high or low ansi -> make the headline text pop out printf("[\x1b[0;30;47m%s\x1b[0m]-\n",headline); else printf("[%s]-\n",headline); } } if (text[0]) { int i; int l; int col; int lastspace; int newline; int skip_newline; int linestart; l=strlen(text); lastspace=-1; newline=0; col=0; linestart=0; i=0; while (icolumns) // this is the end of the line. { default_printline(text,linestart,i,lastspace, pContext->columns,newline,skip_newline,pContext->textalign); if (!newline) { i=lastspace; } linestart=i; newline=0; col=0; pContext->skip_newline=skip_newline; } } } return 0; } int default_cbInputString(void* context,int* len,char* string) { int l; tContext *pContext=(tContext*)context; printf(" "); fflush(stdout); if (feof(stdin)) exit(0); if (fgets(string,256,stdin)==NULL) exit(0); if (pContext->echomode) { for (l=0;l='a' && string[l]<='z') { printf("%c",string[l]&0x5f); } else { printf("%c",string[l]); // print upper case letters. } } } if (pContext->f_logfile) { fprintf(pContext->f_logfile,"%s",string); fflush(pContext->f_logfile); } l=strlen(string); *len=l; pContext->capital=1; // the next line after the input is most definiately the beginning of a sentence! return 0; } int default_cbDrawPicture(void* context,tdMagneticPicture* picture,char* picname,int mode) { tContext *pContext=(tContext*)context; int retval; if (pContext->mode==eMODE_NONE) return 0; // flush the output buffer printf("\n"); switch (pContext->mode) { case eMODE_MONOCHROME: retval=default_render_monochrome(pContext->monochrome_characters,pContext->monochrome_inverted,picture,pContext->rows,pContext->columns); break; case eMODE_LOW_ANSI: retval=default_render_lowansi(pContext->low_ansi_characters,picture,pContext->rows,pContext->columns); break; case eMODE_LOW_ANSI2: retval=default_render_lowansi2(pContext->low_ansi_characters,picture,pContext->rows,pContext->columns); break; case eMODE_HIGH_ANSI: case eMODE_HIGH_ANSI2: retval=default_render_highansi(picture,pContext->rows,pContext->columns,(pContext->mode==eMODE_HIGH_ANSI2)); break; case eMODE_SIXEL: retval=default_render_sixel(picture,pContext->screenwidth,pContext->screenheight,pContext->forceres); break; case eMODE_UTF: retval=default_render_utf(picture,pContext->rows,pContext->columns); break; default: retval=0; break; } return retval; } int default_setEngine(void* context,void *hEngine) { tContext *pContext=(tContext*)context; pContext->hEngine=hEngine; return 0; } #ifdef EXPERIMENTAL_SAVEGAME_SLOTS int default_cbSaveGame(void* context,char* filename,void* ptr,int len) { tContext *pContext=(tContext*)context; void *ptr2; int len2; FILE *f; int n; int i; int done; int retval; char line[256]; retval=0; if (pContext->advancedSaves) { dMagnetic_getVM(pContext->hEngine,&ptr2,&len2); f=fopen(pContext->advancedSavesFilename,"rb"); retval=0; if (f) { n=fread(pContext->saveGameSlots,sizeof(tSaveGameSlots),10,f); fclose(f); if (n!=10) { printf("*** FATAL SAVEGAME SLOTS ERROR\n"); exit(1); } } else { for (i=0;i<10;i++) { memset(&(pContext->saveGameSlots[i]),0,sizeof(tSaveGameSlots)); } } done=0; do { int inputlen; printf("*** APOLOGIES ABOUT THE CONFUSION! *** \n"); printf("*** THE PREVIOUS QUESTIONS CAME FROM THE GAME.\n"); printf("*** This is from the interpreter.\n"); printf("\n"); printf("*** Please select slot:\n"); for (i=0;i<10;i++) { printf("<%d> ",i); if (pContext->saveGameSlots[i].valid) { printf("%04d-%02d-%02d %02d:%02d:%02d ",pContext->saveGameSlots[i].year,pContext->saveGameSlots[i].month,pContext->saveGameSlots[i].day, pContext->saveGameSlots[i].hour, pContext->saveGameSlots[i].minute, pContext->saveGameSlots[i].sec); printf("%32s %5d bytes\n",pContext->saveGameSlots[i].filename,pContext->saveGameSlots[i].gamelen); } else { printf("free\n"); } } inputlen=sizeof(line); printf(":> "); default_cbInputString(context,&inputlen,line); if (line[0]>='0' && line[0]<='9') { int slot; time_t t; struct tm* now; t=time(NULL); now=gmtime(&t); slot=line[0]-'0'; printf("*** Thank you. Saving now\n"); printf("*** please disregard any warning about problems with the save.\n"); done=1; pContext->saveGameSlots[slot].valid=1; pContext->saveGameSlots[slot].year=now->tm_year+1900; pContext->saveGameSlots[slot].month=now->tm_mon+1; pContext->saveGameSlots[slot].day=now->tm_mday; pContext->saveGameSlots[slot].hour=now->tm_hour; pContext->saveGameSlots[slot].minute=now->tm_min; pContext->saveGameSlots[slot].sec=now->tm_sec; memcpy(pContext->saveGameSlots[slot].filename,filename,64); memcpy(pContext->saveGameSlots[slot].gamedata,ptr2,len2); pContext->saveGameSlots[slot].gamelen=len2; retval=0; f=fopen(pContext->advancedSavesFilename,"wb"); n=fwrite(pContext->saveGameSlots,sizeof(tSaveGameSlots),10,f); fclose(f); } else if (line[0]=='\n' || line[0]=='\r') { printf("*** Aborting save now.\n"); done=1; retval=-1; } else { printf("*** What?\n\n"); done=0; } } while (!done); } else { f=fopen(filename,"wb"); if (!f) { printf("Unable to open file [%s]\n",(char*)ptr); return 0; } n=fwrite(ptr,sizeof(char),len,f); fclose(f); if (n!=len) retval=-1; } return retval; } int default_cbLoadGame(void* context,char* filename,void* ptr,int len) { tContext *pContext=(tContext*)context; void *ptr2; int len2; FILE *f; int n; int i; int done; int retval; char line[256]; retval=0; if (pContext->advancedSaves) { dMagnetic_getVM(pContext->hEngine,&ptr2,&len2); f=fopen(pContext->advancedSavesFilename,"rb"); if (f) { n=fread(pContext->saveGameSlots,sizeof(tSaveGameSlots),10,f); fclose(f); if (n!=10) { printf("*** FATAL SAVEGAME SLOTS ERROR\n"); exit(1); } } else { printf("*** Nothing to load yet\n"); return -1; } done=0; do { int inputlen; printf("*** APOLOGIES ABOUT THE CONFUSION! *** \n"); printf("*** THE PREVIOUS QUESTIONS CAME FROM THE GAME.\n"); printf("*** This is from the interpreter.\n"); printf("\n"); printf("*** Please select slot:\n"); for (i=0;i<10;i++) { printf("<%d> ",i); if (pContext->saveGameSlots[i].valid) { printf("%04d-%02d-%02d %02d:%02d:%02d ",pContext->saveGameSlots[i].year,pContext->saveGameSlots[i].month,pContext->saveGameSlots[i].day, pContext->saveGameSlots[i].hour, pContext->saveGameSlots[i].minute, pContext->saveGameSlots[i].sec); printf("%32s %5d bytes\n",pContext->saveGameSlots[i].filename,pContext->saveGameSlots[i].gamelen); } else { printf("free\n"); } } inputlen=sizeof(line); printf(":> "); default_cbInputString(context,&inputlen,line); if (line[0]>='0' && line[0]<='9') { int slot; slot=line[0]-'0'; printf("*** Thank you. Loading now\n"); printf("*** please disregard any warning about problems with the save.\n"); if (pContext->saveGameSlots[slot].valid && pContext->saveGameSlots[slot].gamelen==len2) { memcpy(ptr2,pContext->saveGameSlots[slot].gamedata,len2); done=1; retval=0; } } else if (line[0]=='\n' || line[0]=='\r') { printf("*** Aborting load now.\n"); done=1; retval=-1; } else { printf("*** What?\n\n"); done=0; } } while (!done); printf("loaded\n"); } else { f=fopen(filename,"rb"); if (!f) { printf("Unable to open file [%s]\n",(char*)ptr); return 0; } n=fread(ptr,sizeof(char),len,f); fclose(f); if (n!=len) retval=-1; } return retval; } #else int default_cbSaveGame(void* context,char* filename,void* ptr,int len) { tContext *pContext=(tContext*)context; FILE *f; int n; void *ptr2; f=fopen(filename,"wb"); if (!f) { printf("Unable to open file [%s]\n",(char*)ptr); return 0; } if (pContext->advancedSaves) { dMagnetic_getVM(pContext->hEngine,&ptr2,&len); n=fwrite(ptr2,sizeof(char),len,f); } else { n=fwrite(ptr,sizeof(char),len,f); } fclose(f); if (n==len) return 0; return -1; } int default_cbLoadGame(void* context,char* filename,void* ptr,int len) { tContext *pContext=(tContext*)context; FILE *f; int n; void *ptr2; f=fopen(filename,"rb"); if (!f) { printf("Unable to open file [%s]\n",(char*)ptr); return 0; } if (pContext->advancedSaves) { dMagnetic_getVM(pContext->hEngine,&ptr2,&len); n=fread(ptr2,sizeof(char),len,f); } else { n=fread(ptr,sizeof(char),len,f); } fclose(f); if (n==len) return 0; return -1; } #endif int default_getsize(int* size) { if (size==NULL) return DEFAULT_NOK; *size=sizeof(tContext); return DEFAULT_OK; } const char *default_low_ansi_characters="1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\\/;|(){}[]<>?"; const char *default_monochrome_characters=" .,-+=*oxm#@OXM"; int default_open(void* hContext,FILE *f_inifile,int argc,char** argv) { tContext *pContext=(tContext*)hContext; char result[1024]; if (pContext==NULL) return DEFAULT_NOK; memset(pContext,0,sizeof(tContext)); pContext->magic=MAGIC; // the hiearchy is: first the default values. // if there is a .ini file, overwrite them with those values. // paramaters from the command line have the highest prioprity. pContext->rows=40; pContext->columns=120; pContext->mode=eMODE_LOW_ANSI; // 0=none. 1=monochrome. 2=low_ansi. 3=high_ansi. 4=high_ansi2, 5=sixel pContext->f_logfile=NULL; pContext->echomode=0; pContext->textalign=ALIGNMENT_BLOCK; pContext->screenwidth=320; pContext->screenheight=200; pContext->forceres=0; pContext->advancedSaves=1; strncpy(pContext->low_ansi_characters,default_low_ansi_characters,sizeof(pContext->low_ansi_characters)-1); strncpy(pContext->monochrome_characters,default_monochrome_characters,sizeof(pContext->monochrome_characters)-1); if (f_inifile) { if (retrievefromini(f_inifile,"[DEFAULTGUI]","rows",result,sizeof(result))) { pContext->rows=atoi(result); } if (retrievefromini(f_inifile,"[DEFAULTGUI]","columns",result,sizeof(result))) { pContext->columns=atoi(result); } if (retrievefromini(f_inifile,"[DEFAULTGUI]","mode",result,sizeof(result))) { if (strncmp(result,"none",4)==0) pContext->mode=eMODE_NONE; else if (strncmp(result,"monochrome_inv",14)==0) {pContext->mode=eMODE_MONOCHROME;pContext->monochrome_inverted=1;} else if (strncmp(result,"monochrome",10)==0) pContext->mode=eMODE_MONOCHROME; else if (strncmp(result,"low_ansi2",9)==0) pContext->mode=eMODE_LOW_ANSI2; else if (strncmp(result,"low_ansi",8)==0) pContext->mode=eMODE_LOW_ANSI; else if (strncmp(result,"high_ansi2",10)==0) pContext->mode=eMODE_HIGH_ANSI2; else if (strncmp(result,"high_ansi",9)==0) pContext->mode=eMODE_HIGH_ANSI; else if (strncmp(result,"sixel",5)==0) pContext->mode=eMODE_SIXEL; else if (strncmp(result,"utf",4)==0) pContext->mode=eMODE_UTF; } if (retrievefromini(f_inifile,"[DEFAULTGUI]","align",result,sizeof(result))) { if (strncmp(result,"left",4)==0) pContext->textalign=ALIGNMENT_LEFT; if (strncmp(result,"block",5)==0) pContext->textalign=ALIGNMENT_BLOCK; if (strncmp(result,"right",5)==0) pContext->textalign=ALIGNMENT_RIGHT; } if (retrievefromini(f_inifile,"[DEFAULTGUI]","low_ansi_characters",pContext->low_ansi_characters,sizeof(pContext->low_ansi_characters))) { if (pContext->low_ansi_characters[0]==0x00) { fprintf(stderr,"Error in .ini file: low_ansi_character\n"); return -1; } } if (retrievefromini(f_inifile,"[DEFAULTGUI]","monochrome_characters",pContext->monochrome_characters,sizeof(pContext->monochrome_characters))) { if (pContext->monochrome_characters[0]==0x00) { fprintf(stderr,"Error in .ini file: monochrome_character\n"); return -1; } } if (retrievefromini(f_inifile,"[DEFAULTGUI]","sixel_forceresolution",result,sizeof(result))) { if (result[0]=='y' || result[0]=='Y' || result[0]=='t' || result[0]=='T' || result[0]=='1') pContext->forceres=1; // set it to one if the entry reads "yes"/"True"/1 } if (retrievefromini(f_inifile,"[DEFAULTGUI]","sixel_resolution",result,sizeof(result))) { int i; int l; l=strlen(result); pContext->screenwidth=pContext->screenheight=0; for (i=0;iscreenwidth =atoi(&result[0]); pContext->screenheight=atoi(&result[i+1]); } } if (pContext->screenwidth==0 || pContext->screenwidth>MAX_SRESX || pContext->screenheight==0) { printf("illegal parameter for sixelresultion. please use something like 1024x768\n"); return DEFAULT_NOK; } } if (retrievefromini(f_inifile,"[DEFAULTGUI]","savegames",result,sizeof(result))) { if (result[0]=='c' || result[0]=='C') pContext->advancedSaves=0; // "classic" or "Classic" if (result[0]=='a' || result[0]=='A') pContext->advancedSaves=1; // "advanced" or "Advanced" } } if (argc) { char result[64]; if (retrievefromcommandline(argc,argv,"-valign",result,sizeof(result))) { if (strncmp(result,"left",4)==0) pContext->textalign=ALIGNMENT_LEFT; else if (strncmp(result,"block",5)==0) pContext->textalign=ALIGNMENT_BLOCK; else if (strncmp(result,"right",5)==0) pContext->textalign=ALIGNMENT_RIGHT; else { printf("unknown parameter for -valign. please use one of\n"); printf("left "); printf("block "); printf("right "); printf("\n"); return DEFAULT_NOK; } } if (retrievefromcommandline(argc,argv,"-vmode",result,sizeof(result))) { if (strncmp(result,"none",4)==0) pContext->mode=eMODE_NONE; else if (strncmp(result,"monochrome_inv",14)==0) {pContext->mode=eMODE_MONOCHROME;pContext->monochrome_inverted=1;} else if (strncmp(result,"monochrome",10)==0) pContext->mode=eMODE_MONOCHROME; else if (strncmp(result,"low_ansi2",9)==0) pContext->mode=eMODE_LOW_ANSI2; else if (strncmp(result,"low_ansi",8)==0) pContext->mode=eMODE_LOW_ANSI; else if (strncmp(result,"high_ansi2",10)==0) pContext->mode=eMODE_HIGH_ANSI2; else if (strncmp(result,"high_ansi",9)==0) pContext->mode=eMODE_HIGH_ANSI; else if (strncmp(result,"sixel",5)==0) pContext->mode=eMODE_SIXEL; else if (strncmp(result,"utf",4)==0) pContext->mode=eMODE_UTF; else { printf("unknown parameter for -vmode. please use one of\n"); printf("none "); printf("monochrome "); printf("monochrome_inv "); printf("low_ansi "); printf("low_ansi2 "); printf("high_ansi "); printf("high_ansi2 "); printf("sixel "); printf("utf "); printf("\n"); return DEFAULT_NOK; } } if (retrievefromcommandline(argc,argv,"-vrows",result,sizeof(result))) { int rows; rows=atoi(result); if (rows<1 || rows>500) { printf("illegal parameter for -vrows. please use values between 1 and 500\n"); return DEFAULT_NOK; } pContext->rows=rows; } if (retrievefromcommandline(argc,argv,"-vcols",result,sizeof(result))) { int cols; cols=atoi(result); if (cols<1 || cols>600) { printf("illegal parameter for -vcols. please use values between 1 and 600\n"); return DEFAULT_NOK; } pContext->columns=cols; } if (retrievefromcommandline(argc,argv,"-vecho",NULL,0)) { pContext->echomode=1; } if (retrievefromcommandline(argc,argv,"-vlog",result,sizeof(result))) { fprintf(stderr,"Opening logfile [%s] for writing\n",result); pContext->f_logfile=fopen(result,"wb"); if (pContext->f_logfile==NULL) { fprintf(stderr,"Error opening logfile [%s]\n",result); exit(0); } } if (retrievefromcommandline(argc,argv,"-sres",result,sizeof(result))) { int i; int l; l=strlen(result); pContext->screenwidth=pContext->screenheight=0; for (i=0;iscreenwidth =atoi(&result[0]); pContext->screenheight=atoi(&result[i+1]); } } if (pContext->screenwidth==0 || pContext->screenwidth>MAX_SRESX || pContext->screenheight==0) { printf("illegal parameter for -sres. please use something like 1024x768\n"); return DEFAULT_NOK; } } if (retrievefromcommandline(argc,argv,"-sforce",result,sizeof(result))) { pContext->forceres=1; } #ifdef EXPERIMENTAL_SAVEGAME_SLOTS snprintf(pContext->advancedSavesFilename,64,"savegames.bin"); if (retrievefromcommandline(argc,argv,"-savegameslots",result,sizeof(result))) { pContext->advancedSaves=1; strncpy(pContext->advancedSavesFilename,result,sizeof(pContext->advancedSavesFilename)); } #else if (retrievefromcommandline(argc,argv,"-saves",result,sizeof(result))) { if (result[0]=='c' || result[0]=='C') pContext->advancedSaves=0; // "classic" or "Classic" if (result[0]=='a' || result[0]=='A') pContext->advancedSaves=1; // "advanced" or "Advanced" } #endif } return DEFAULT_OK; } dmagnetic-0.37/src/frontends/default/pathnames.h0000644000175000017500000000360714430741336020475 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef PATHNAMES_H #define PATHNAMES_H // some distributions require a specific prefix before the pathnames. // those macros allow them to patch the prefixes in a centralized location. #define PATH_ETC "/etc/" #define PATH_USR "/usr/" #define PATH_USR_LOCAL "/usr/local/" #define PATH_USR_LOCAL_SHARE "/usr/local/share/" #define PATH_USR_LOCAL_SHARE_GAMES "/usr/local/share/games/" #define PATH_USR_LOCAL_GAMES "/usr/local/games/" #define PATH_USR_SHARE "/usr/share/" #define PATH_USR_SHARE_GAMES "/usr/share/games/" #define PATH_USR_GAMES "/usr/games/" #define PATH_USR_PKG_SHARE "/usr/pkg/share/" #endif dmagnetic-0.37/src/frontends/default/default_render.c0000644000175000017500000006032214430741336021470 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "default_palette.h" #include "default_render.h" #include "dMagnetic.h" // the following table is a bitmap, representing all the available ASCII characters. const unsigned long long asciibitmap[95]={ 0x0000000000000000, // 0x00180018183c3c18, // ! 0x0000000000003636, // " 0x0036367f367f3636, // # 0x000c1f301e033e0c, // $ 0x0063660c18336300, // % 0x006e333b6e1c361c, // & 0x0000000000030606, // ' 0x00180c0606060c18, // ( 0x00060c1818180c06, // ) 0x0000663cff3c6600, // * 0x00000c0c3f0c0c00, // + 0x060c0c0000000000, // , 0x000000003f000000, // - 0x000c0c0000000000, // . 0x000103060c183060, // / 0x003e676f7b73633e, // 0 0x003f0c0c0c0c0e0c, // 1 0x003f33061c30331e, // 2 0x001e33301c30331e, // 3 0x0078307f33363c38, // 4 0x001e3330301f033f, // 5 0x001e33331f03061c, // 6 0x000c0c0c1830333f, // 7 0x001e33331e33331e, // 8 0x000e18303e33331e, // 9 0x000c0c00000c0c00, // : 0x060c0c00000c0c00, // ; 0x00180c0603060c18, // < 0x00003f00003f0000, // = 0x00060c1830180c06, // > 0x000c000c1830331e, // ? 0x001e037b7b7b633e, // @ 0x0033333f33331e0c, // A 0x003f66663e66663f, // B 0x003c66030303663c, // C 0x001f36666666361f, // D 0x007f46161e16467f, // E 0x000f06161e16467f, // F 0x007c66730303663c, // G 0x003333333f333333, // H 0x001e0c0c0c0c0c1e, // I 0x001e333330303078, // J 0x006766361e366667, // K 0x007f66460606060f, // L 0x0063636b7f7f7763, // M 0x006363737b6f6763, // N 0x001c36636363361c, // O 0x000f06063e66663f, // P 0x00381e3b3333331e, // Q 0x006766363e66663f, // R 0x001e33380e07331e, // S 0x001e0c0c0c0c2d3f, // T 0x003f333333333333, // U 0x000c1e3333333333, // V 0x0063777f6b636363, // W 0x0063361c1c366363, // X 0x001e0c0c1e333333, // Y 0x007f664c1831637f, // Z 0x001e06060606061e, // [ 0x00406030180c0603, // BACKSLASH 0x001e18181818181e, // ] 0x0000000063361c08, // ^ 0xff00000000000000, // _ 0x0000000000180c0c, // ` 0x006e333e301e0000, // a 0x003b66663e060607, // b 0x001e3303331e0000, // c 0x006e33333e303038, // d 0x001e033f331e0000, // e 0x000f06060f06361c, // f 0x1f303e33336e0000, // g 0x006766666e360607, // h 0x001e0c0c0c0e000c, // i 0x1e33333030300030, // j 0x0067361e36660607, // k 0x001e0c0c0c0c0c0e, // l 0x00636b7f7f330000, // m 0x00333333331f0000, // n 0x001e3333331e0000, // o 0x0f063e66663b0000, // p 0x78303e33336e0000, // q 0x000f06666e3b0000, // r 0x001f301e033e0000, // s 0x00182c0c0c3e0c08, // t 0x006e333333330000, // u 0x000c1e3333330000, // v 0x00367f7f6b630000, // w 0x0063361c36630000, // x 0x1f303e3333330000, // y 0x003f260c193f0000, // z 0x00380c0c070c0c38, // { 0x0018181800181818, // | 0x00070c0c380c0c07, // } 0x0000000000003b6e // ~ }; int default_render_lowansi(char* allowed,tdMagneticPicture* picture,int rows,int cols) { unsigned char maxplut[16]={0}; int x,y,a,c,i,j,k,p; // counting variables int minccnt,maxc; int maxpcnt,maxp; int accuy; // step one: // find a good substitute for the palette. if (picture->pictureType==PICTURE_C64) { // the c64 had a fixed palette. maxplut[ 0]=0x0;maxplut[ 1]=0xe;maxplut[ 2]=0x1;maxplut[ 3]=0x6; // black, white, red, cyan maxplut[ 4]=0x5;maxplut[ 5]=0x2;maxplut[ 6]=0x4;maxplut[ 7]=0xb; // purple, green, blue, yellow maxplut[ 8]=0x9;maxplut[ 9]=0x3;maxplut[10]=0xd;maxplut[11]=0x8; // orange, brown, pink, dark grey maxplut[12]=0x7;maxplut[13]=0xa;maxplut[14]=0xc;maxplut[15]=0xf; // medium grey, light green, light blue, light grey } else { default_palette(picture,maxplut); } // step 2: render the picture. use the color and the character that best represents a 8x8 block. y=0; accuy=0; for (i=0;iheight;i++) { accuy+=rows; // since the output is smaller than the actual picture, count a few lines. // at some point, there are enough to print one character. if (accuy>=picture->height) { int accux; int lastmaxp; lastmaxp=-1; accux=0; x=0; for (j=0;jwidth;j++) { accux+=cols; if (accux>=picture->width) { unsigned long long pb; int scalex,scaley; scalex=(j-x)/8; scaley=(i-y)/8; if (scalex==0) scalex=1; if (scaley==0) scaley=1; // at this point, a rectangle from X:x..j, Y:y..i is being rendered. // first: find the most common characters and the most common color. // find within the rectangle the largest amount of pixels with the same color maxp=0; maxpcnt=0; for (p=0;p<16;p++) { int cnt0; int x2,y2; cnt0=0; for (x2=x;x2=0 && x2width && y2>=0 && y2height) if (picture->pixels[y2*picture->width+x2]==p) cnt0++; } } if (cnt0>maxpcnt) { maxpcnt=cnt0; maxp=p; } } // create a bitmap of the next 8x8 pixel block pb=0; for (k=0;k<64;k++) { int x2,y2; int line,row; row=k/8; line=k%8; x2=x+scalex*4-scalex*line; y2=y+scaley*4-scaley*row; pb<<=1; if (x2>=0 && x2width && y2>=0 && y2height) if (picture->pixels[y2*picture->width+x2]==maxp) pb|=1; } // compare the bitmap to the bitmap for a character. // find the one with the lowest number of mismatches minccnt=64; maxc=0; for (a=0;a>=1; } if ((cnt0)<=minccnt) { minccnt=cnt0; maxc=c; } } maxp=maxplut[maxp]; if (maxp!=lastmaxp) printf("\x1b[%d;%dm",maxp/8,30+maxp%8); lastmaxp=maxp; if (maxc<32 || maxc>=127) printf(" "); else printf("%c",maxc); accux-=picture->width; x=j; // the next rectangle will begin at the edge of this one. } } accuy-=picture->height; y=i; // the next rectangle will begin in this line printf("\x1b[0m\n"); } } return 0; } int default_render_lowansi2(char* allowed,tdMagneticPicture* picture,int rows,int cols) { int x,y,a,c,i,j,k,p; // counting variables int minccnt,maxc; int maxpcnt,maxp; int accuy; int redsum,greensum,bluesum,pixcnt; int lastx,lasty; // render the picture. use the color and the character that best represents a 8x8 block. y=0; accuy=0; lasty=0; for (i=0;iheight;i++) { accuy+=rows; // since the output is smaller than the actual picture, count a few lines. // at some point, there are enough to print one character. if (accuy>=picture->height) { int accux; int lastmaxp; lastmaxp=-1; accux=0; x=0; lastx=0; for (j=0;jwidth;j++) { accux+=cols; if (accux>=picture->width) { unsigned long long pb; int scalex,scaley; int l; int maxp2; int mul; redsum=greensum=bluesum=pixcnt=0; for (k=lastx;k<=j;k++) { for (l=lasty;l<=i;l++) { unsigned int p; p=picture->palette[(int)picture->pixels[k+l*picture->width]]; redsum +=(PICTURE_GET_RED(p)); greensum+=(PICTURE_GET_GREEN(p)); bluesum +=(PICTURE_GET_BLUE(p)); pixcnt++; } } lastx=j; maxp2=default_findrgbcluster(redsum/pixcnt,greensum/pixcnt,bluesum/pixcnt); if (picture->pictureType==PICTURE_HALFTONE) mul=2; else mul=1; scalex=(j-x)/(8*mul); scaley=(i-y)/(8*mul); if (scalex==0) scalex=1; if (scaley==0) scaley=1; // at this point, a rectangle from X:x..j, Y:y..i is being rendered. // first: find the most common characters and the most common color. // find within the rectangle the largest amount of pixels with the same color maxp=0; maxpcnt=0; for (p=0;p<16;p++) { int cnt0; int x2,y2; cnt0=0; for (x2=x;x2=0 && x2width && y2>=0 && y2height) if (picture->pixels[y2*picture->width+x2]==p) cnt0++; } } if (cnt0>maxpcnt) { maxpcnt=cnt0; maxp=p; } } // create a bitmap of the next 8x8 pixel block pb=0; for (k=0;k<64;k++) { int x2,y2; int line,row; row=k/(8*mul); line=k%(8*mul); x2=x+scalex*(4*mul)-scalex*line; y2=y+scaley*(4*mul)-scaley*row; pb<<=1; if (x2>=0 && x2width && y2>=0 && y2height) if (picture->pixels[y2*picture->width+x2]==maxp) pb|=1; } // compare the bitmap to the bitmap for a character. // find the one with the lowest number of mismatches minccnt=64; maxc=0; for (a=0;a>=1; } if ((cnt0)<=minccnt) { minccnt=cnt0; maxc=c; } } if (maxp2!=lastmaxp) printf("\x1b[%d;%dm",maxp2/8,30+maxp2%8); lastmaxp=maxp2; if (maxc<32 || maxc>=127) printf(" "); else printf("%c",maxc); accux-=picture->width; x=j; // the next rectangle will begin at the edge of this one. } } lasty=i; accuy-=picture->height; y=i; // the next rectangle will begin in this line printf("\x1b[0m\n"); } } return 0; } int default_render_monochrome(char* greyscales,int inverted,tdMagneticPicture* picture,int rows,int cols) { int i; int j; int k,l; int y_up,y_down,x_left,x_right; int accux,accuy; int grey; int mingrey,maxgrey; int cnt; int pixcnt; int p; int scalenum=strlen(greyscales); int pass; // first: try to find the brightest/darkest pixels // second pass: the position of the character within the monochrome ramp should be proportional to the brightness of a "pixel". cnt=0; mingrey=maxgrey=0; p=0; for (pass=0;pass<2;pass++) { if (pass==1 && mingrey==maxgrey) { maxgrey++; } y_up=0; accux=accuy=0; for (i=0;iheight;i++) { accuy+=rows; if (accuy>=picture->height || i==picture->height-1) { accuy-=picture->height; y_down=i+1; x_left=0; for (j=0;jwidth;j++) { accux+=cols; if (accux>=picture->width || j==picture->width-1) { x_right=j+1; accux-=picture->width; // at this point, a rectangle between y_up,y_down, x_left,x_right contains the pixels that need to be greyscaled. grey=0; pixcnt=0; for (k=y_up;kheight && lwidth) { p=picture->pixels[k*picture->width+l]; } // compensate for luminance (0.2126*R + 0.7152*G + 0.0722*B) grey+=2126*(PICTURE_GET_RED(picture->palette[p])); grey+=7152*(PICTURE_GET_GREEN(picture->palette[p])); grey+= 722*(PICTURE_GET_BLUE(picture->palette[p])); pixcnt++; } } grey/=pixcnt; if (pass==0) { // first pass: find the dimmest and the brightest "pixels" if (cnt==0 || grey>maxgrey) maxgrey=grey; if (cnt==0 || greymingrey) // avoid division by 0 { grey/=(maxgrey-mingrey); } if (inverted) grey=scalenum-1-grey; if (grey<0) grey=0; if (grey>=scalenum) grey=scalenum-1; if(strlen(greyscales)) { printf("%c",greyscales[grey%strlen(greyscales)]); } } x_left=x_right; } } y_up=y_down; if (pass!=0) printf("\n"); } } } return 0; } int default_render_highansi(tdMagneticPicture *picture,int rows,int columns,int blur) { int i,j; int accuy; int lasty; lasty=0; accuy=0; for (i=0;iheight;i++) { unsigned int rgb,lastrgb; accuy+=rows; lastrgb=-1; if (accuy>=picture->height || i==picture->height-1) { int accux; int lastx; accuy-=picture->height; accux=0; lastx=0; for (j=0;jwidth;j++) { accux+=columns; if (accux>=picture->width || j==picture->width-1) { int red,green,blue; int x,y; int pixcnt; red=0; green=0; blue=0; accux-=picture->width; pixcnt=0; if (blur) { for (y=lasty;y<=i;y++) { for (x=lastx;x<=j;x++) { unsigned int p; p=picture->palette[(int)(picture->pixels[y*(picture->width)+x])]; red +=(PICTURE_GET_RED(p)); green+=(PICTURE_GET_GREEN(p)); blue +=(PICTURE_GET_BLUE(p)); pixcnt++; } } red*=PICTURE_MAX_RGB_VALUE;green*=PICTURE_MAX_RGB_VALUE;blue*=PICTURE_MAX_RGB_VALUE; pixcnt*=PICTURE_MAX_RGB_VALUE; red/=pixcnt;green/=pixcnt;blue/=pixcnt; if (red>PICTURE_MAX_RGB_VALUE) red=PICTURE_MAX_RGB_VALUE; if (green>PICTURE_MAX_RGB_VALUE) green=PICTURE_MAX_RGB_VALUE; if (blue>PICTURE_MAX_RGB_VALUE) blue=PICTURE_MAX_RGB_VALUE; rgb=(red<<20)|(green<<10)|blue; } else { rgb=picture->palette[(int)(picture->pixels[i*(picture->width)+j])]; } if (rgb!=lastrgb) { int red,green,blue; red =PICTURE_GET_RED(rgb); green=PICTURE_GET_GREEN(rgb); blue =PICTURE_GET_BLUE(rgb); red*=255;green*=255;blue*=255; red /=PICTURE_MAX_RGB_VALUE; green/=PICTURE_MAX_RGB_VALUE; blue /=PICTURE_MAX_RGB_VALUE; printf("\x1b[48;2;%d;%d;%dm", red,green,blue); lastrgb=rgb; } printf(" "); lastx=j; } } printf("\x1b[0m\n"); lasty=i; } } return 0; } int default_render_sixel(tdMagneticPicture* picture,int screenwidth,int screenheight,int forceres) { // so, according to James, some terminals have only 16 registers for the colours. // thus, the colour for text foreground and the background might be overwritten, // when too many sixel colours are being defined. // // the solution is to make sure that the sixth entry contains the brightest // and the 15th entry the darkest colour. So I am reordering the palette. #define FOREGROUND_POSITION 6 #define BACKGROUND_POSITION 15 #define TOTAL_COLOURS 16 int i,j; int accuy; int y; int minval=0; int minpos=0; int maxval=0; int maxpos=0; int paletteorder[TOTAL_COLOURS]; y=0; accuy=0; // find a good aspect ratio { int ratiox,ratioy; #define MAGICFIXPOINT 1800 // magic factor was chosen when I removed the float code. simply because it worked with the check and it did not cause overflows with 32 bit machines if (picture->width<=0 || picture->height<=1) return 0; // make sure that there will be no division by 0 ratiox=(MAGICFIXPOINT*screenwidth)/(picture->width); ratioy=(MAGICFIXPOINT*screenheight)/(picture->height-1); if (!forceres) { if (ratioxwidth)/MAGICFIXPOINT); screenheight=(int)((ratioy*(picture->height-1))/MAGICFIXPOINT); if (!forceres) { while (screenheight%6) screenheight++; // make sure that the last line consists of a full block of sixels. } } // find the darkest and the brightest color. // move them to pallette position 0 or 15, to deal with an issue that james found { int j; for (i=0;ipalette[i]; red =PICTURE_GET_RED(rgb); green=PICTURE_GET_GREEN(rgb); blue =PICTURE_GET_BLUE(rgb); val=red+green+blue; // just add the values. luminance correction would be overkill if (i==0 || minval>val) { minval=val; minpos=i; } if (i==0 || maxvalpalette[paletteorder[i]]; red =PICTURE_GET_RED(rgb); green=PICTURE_GET_GREEN(rgb); blue =PICTURE_GET_BLUE(rgb); red*=100;green*=100;blue*=100; red /=PICTURE_MAX_RGB_VALUE; green/=PICTURE_MAX_RGB_VALUE; blue /=PICTURE_MAX_RGB_VALUE; printf("#%02d;2;%d;%d;%d",paletteorder[i],red,green,blue); } while (y<(picture->height-1)) { int y0; int accuy0; int curpixel; accuy0=accuy; y0=y; for (i=0;iwidth) { char bitmask; y=y0; accuy=accuy0; curpixel=picture->pixels[y*picture->width+x]; bitmask=0; for (j=0;j<6;j++) { bitmask>>=1; if (curpixel==i) bitmask|=0x20; accuy+=(picture->height-1); while (accuy>=screenheight) { accuy-=screenheight; y++; if (y<(picture->height-1)) { curpixel=picture->pixels[y*picture->width+x]; } else { curpixel=0; } } } while (accuxwidth; } accux-=screenwidth; x++; } printf("$"); } printf("-"); } printf("\x1b\\\x1b[0m\n"); return 0; } int default_render_utf(tdMagneticPicture* picture,int rows,int cols) { // By choosing the following utf-characters: // 0x20,0xe29680,0xe29690,0xe2969a,0xe29696,0xe29697,0xe29698,0xe2969d // // i could transform each character field into a 2x2 bitmap: // .. ## .# #. .. .. #. .# // .. .. .# .# #. .# .. .. // // when choosing the background and the foreground colour wisely, it // would create the illusion of finer grained pixels, even though, // technically, it is not exact. It would work best when each 2x2 bitmap // has only 1 or 2 colours. But with 3 or 4 it creates artifacts. // they are negligable! // // it can easily be seen that those 8 utf symbols are enough #define NUM_utf8chars 8 #define CALC_RGBDELTA(rgb1,rgb2) ( \ (PICTURE_GET_RED(rgb1)-PICTURE_GET_RED(rgb2))*(PICTURE_GET_RED(rgb1)-PICTURE_GET_RED(rgb2))+ \ (PICTURE_GET_GREEN(rgb1)-PICTURE_GET_GREEN(rgb2))*(PICTURE_GET_GREEN(rgb1)-PICTURE_GET_GREEN(rgb2))+ \ (PICTURE_GET_BLUE(rgb1)-PICTURE_GET_BLUE(rgb2))*(PICTURE_GET_BLUE(rgb1)-PICTURE_GET_BLUE(rgb2))) #define NUM_COLOURS 16 // for each of the utf characters, define a bitmap according to the following scheme: // 12 // 48 const unsigned int default_cbDrawPicture_utf8chars[NUM_utf8chars]={0x200000,0xe29680,0xe29690,0xe2969a,0xe29696,0xe29697,0xe29698,0xe2969d}; const unsigned char default_cbDrawPicture_utf8map[NUM_utf8chars] ={0x0, 1|2, 2|8, 1|8, 4, 8, 1, 2}; int x; int y; int accux; int accuy; accuy=0; for (y=0;yheight;y++) { accuy+=rows; if (accuy>=picture->height) { unsigned int lastfg,lastbg; lastfg=lastbg=0xffffffff; accuy-=picture->height; accux=0; for (x=0;xwidth;x++) { accux+=cols; if (accux>=picture->width) { unsigned int rgb1,rgb2,rgb4,rgb8; unsigned int mindelta; unsigned int bestutfsymbol; unsigned int bestfg; unsigned int bestbg; int fg,bg,sym; int fgred,fggreen,fgblue; int bgred,bggreen,bgblue; accux-=picture->width; // find out what colours are in the 2x2 bitmap rgb1=rgb2=rgb4=rgb8=picture->palette[(int)(picture->pixels[(y+0)*(picture->width)+x+0])]; if (xwidth-1) rgb2=picture->palette[(int)(picture->pixels[(y+0)*(picture->width)+x+1])]; if (yheight-1) rgb4=rgb8=picture->palette[(int)(picture->pixels[(y+1)*(picture->width)+x+0])]; if (xwidth-1 && yheight-1) rgb8=picture->palette[(int)(picture->pixels[(y+1)*(picture->width)+x+1])]; // now for the main event: try to find a matching foreground/background/symbol combination // using a neighest neighbour-approach with the RGB values helps in nuanced picture to // mask some artifacts mindelta=bestfg=bestbg=0xffffffff; bestutfsymbol=0; for (fg=0;fgpalette[fg]:picture->palette[bg]); delta+=CALC_RGBDELTA(rgb2,(default_cbDrawPicture_utf8map[sym]&2)?picture->palette[fg]:picture->palette[bg]); delta+=CALC_RGBDELTA(rgb4,(default_cbDrawPicture_utf8map[sym]&4)?picture->palette[fg]:picture->palette[bg]); delta+=CALC_RGBDELTA(rgb8,(default_cbDrawPicture_utf8map[sym]&8)?picture->palette[fg]:picture->palette[bg]); if (deltapalette[fg]; bestbg=picture->palette[bg]; bestutfsymbol=default_cbDrawPicture_utf8chars[sym]; } } } } // now the best fg/bg/sym combination is known. let's render it! // first: the colour if (lastfg!=bestfg) { lastfg=bestfg; fgred =PICTURE_GET_RED(bestfg); fggreen=PICTURE_GET_GREEN(bestfg); fgblue =PICTURE_GET_BLUE(bestfg); fgred*=255;fggreen*=255;fgblue*=255; fgred /=PICTURE_MAX_RGB_VALUE; fggreen/=PICTURE_MAX_RGB_VALUE; fgblue /=PICTURE_MAX_RGB_VALUE; printf("\x1b[38;2;%d;%d;%dm",fgred,fggreen,fgblue); } if (lastbg!=bestbg) { lastbg=bestbg; bgred =PICTURE_GET_RED(bestbg); bggreen=PICTURE_GET_GREEN(bestbg); bgblue =PICTURE_GET_BLUE(bestbg); bgred*=255;bggreen*=255;bgblue*=255; bgred /=PICTURE_MAX_RGB_VALUE; bggreen/=PICTURE_MAX_RGB_VALUE; bgblue /=PICTURE_MAX_RGB_VALUE; printf("\x1b[48;2;%d;%d;%dm",bgred,bggreen,bgblue); } // since the uft8symbols above are defined as BIG endian numbers, and do not have a 0x00 at the end, // they can be displayed one byte at a time. MSB first. while (bestutfsymbol) { printf("%c",(bestutfsymbol>>16)&0xff); bestutfsymbol<<=8; bestutfsymbol&=0x00ffff00; } } } // that was one line printf("\x1b[0m\n"); } } return 0; } dmagnetic-0.37/src/frontends/default/default_palette.c0000644000175000017500000001652314430741336021653 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "default_palette.h" #include "dMagnetic.h" /// this lookuptable has the following structure: /// 4 bits substitute color, 3x4 bit RGB values. /// this is a direct 1 <--> 1 conversion. const unsigned short rgblut[]={ 0x0000,0x1400,0x3420,0x4004,0x5404,0x6044,0x7444,0x8222,0x9722,0xa272,0xb772,0xc227,0xd727,0xe277,0xf777,0x2121, 0xb554,0x9654,0x6454,0xa452,0x4223,0x7455,0x2342,0x8333,0x1322,0x7443,0x2230,0xc456,0xe565,0x7555,0xb653,0x0110, 0x4334,0xf666,0xe665,0x3553,0x1210,0x3432,0x2332,0xc444,0xa665,0x7332,0xb665,0xf775,0xb442,0x2232,0x6122,0xc223, 0x8111,0x5212,0x4112,0xb631,0x3310,0x8211,0x1410,0x9520,0x2221,0x0100,0xb742,0xb740,0x9731,0x9620,0xa351,0xe454, 0xb460,0x6343,0x2243,0xc354,0x0121,0xb443,0x3331,0x3332,0xf565,0x2341,0x8221,0x3321,0xa040,0xa450,0xb550,0x9763, 0x1100,0x0111,0xb664,0x9754,0xb753,0x3642,0x1211,0x5433,0xc556,0xb552,0x3441,0x0011,0xe566,0xe344,0xf677,0x6233, 0x4222,0x3221,0xe676,0xc445,0x0222,0x3543,0x0010,0x2120,0x5101,0xc667,0xb663,0x1311,0x9533,0x0001,0x6455,0x9655, 0x1200,0x3110,0x4001,0x9543,0xd544,0x6011,0x9652,0xb764,0xc123,0xe234,0xc224,0x1542,0x6022,0x5323,0xc555,0x9432, 0x3431,0x8110,0x9422,0xb520,0xc567,0xa352,0xe466,0x6344,0x7333,0x9433,0x2030,0x1510,0x2231,0xd704,0x2020,0xc005, 0x9544,0x8433,0xa453,0x9322,0x3532,0x7665,0xa565,0x9733,0xf776,0xb765,0x4445,0x1432,0xb774,0x5322,0xa573,0xe563, 0x3210,0xa673,0x9700,0xf675,0xf665,0xf555,0xf554,0xc334,0xe675,0xd766,0xa564,0x9775,0x7554,0x3440,0x8554,0x0333, 0x0332,0xc335,0x1433,0xd655,0xd645,0x7544,0x1644,0xd755,0xb775,0x3443,0xc345,0xa342,0x8443,0xc236,0x1623,0x9743, 0xb762,0x2151,0x5507,0xd606,0xd767,0xa675,0x8122,0xa343,0x0245,0x1402,0x4024,0xc245,0x6133,0x5313,0x5202,0xb630, 0x4012,0xe345,0xc234,0xa332,0x3320 }; #define RGBLUTSIZE 213 // another strategy would be to calculate the center of the clusters that make up the 16 colors. const unsigned int rgbcenters[16]={0x0aa36cc2,0x217380c2,0x0f77049d,0x20f678b2,0x0c23cdb6,0x1b6249b6,0x107789c5,0x2799e638,0x16d57924,0x3487fd74,0x236c457f,0x366ba166,0x1be82eda,0x3ad81f1b,0x26dcbeda,0x36ce6323}; int default_findrgbcluster(int red,int green,int blue) { int i; int closest=0; int mindelta=-1; int delta; int r0,r1,g0,g1,b0,b1; r0=red; g0=green; b0=blue; #if 0 // if I ever want to calculate better rgb centers. { int rgbsums[16][3]={{0}}; int colcnt[16]={0}; for (i=0;i>12)&0xf; red =(rgblut[i]>> 8)&0xf; green =(rgblut[i]>> 4)&0xf; blue =(rgblut[i]>> 0)&0xf; rgbsums[col][0]+=red; rgbsums[col][1]+=green; rgbsums[col][2]+=blue; colcnt[col]+=7; } printf("const unsigned int rgbcenters[16]={"); for (i=0;i<16;i++) { rgbsums[i][0]*=PICTURE_MAX_RGB_VALUE; rgbsums[i][1]*=PICTURE_MAX_RGB_VALUE; rgbsums[i][2]*=PICTURE_MAX_RGB_VALUE; rgbsums[i][0]/=(colcnt[i]); rgbsums[i][1]/=(colcnt[i]); rgbsums[i][2]/=(colcnt[i]); rgbcenters[i]=(rgbsums[i][0]<<20)|(rgbsums[i][1]<<10)|(rgbsums[i][2]); printf("0x%08x,",rgbcenters[i]); } printf("};\n"); } #endif for (i=0;i<16;i++) { r1=(rgbcenters[i]>>20)&0x3ff; g1=(rgbcenters[i]>>10)&0x3ff; b1=(rgbcenters[i]>> 0)&0x3ff; delta =(r0-r1)*(r0-r1); delta+=(g0-g1)*(g0-g1); delta+=(b0-b1)*(b0-b1); if (delta>15)&1)+ (((x)>>14)&1)+ (((x)>>13)&1)+ (((x)>>12)&1)+ \ (((x)>>11)&1)+ (((x)>>10)&1)+ (((x)>> 9)&1)+ (((x)>> 8)&1)+ \ (((x)>> 7)&1)+ (((x)>> 6)&1)+ (((x)>> 5)&1)+ (((x)>> 4)&1)+ \ (((x)>> 3)&1)+ (((x)>> 2)&1)+ (((x)>> 1)&1)+ (((x)>> 0)&1)) // each rgb from the pictures has been assigned one or more alternative // ANSI colors. this method is searching for a combination of alternatives // that is most diverse. total=1; // step 1: collect the alternatives for (i=0;i<16;i++) { unsigned int rgb; rgb=picture->palette[i]; { int red,green,blue; red= PICTURE_GET_RED(rgb); green= PICTURE_GET_GREEN(rgb); blue= PICTURE_GET_BLUE(rgb); red*=7;green*=7;blue*=7; red+=(PICTURE_MAX_RGB_VALUE/2); green+=(PICTURE_MAX_RGB_VALUE/2); blue+=(PICTURE_MAX_RGB_VALUE/2); red/=PICTURE_MAX_RGB_VALUE;green/=PICTURE_MAX_RGB_VALUE;blue/=PICTURE_MAX_RGB_VALUE; rgb=(red<<8)|(green<<4)|blue; } numcols[i]=0; for (j=0;j>12)&0xf; } } // plan B: if there has been no direct match, make sure there is at least one alternative if (numcols[i]==0) { ansicols[i][numcols[i]++]=default_findrgbcluster(PICTURE_GET_RED(picture->palette[i]),PICTURE_GET_GREEN(picture->palette[i]),PICTURE_GET_BLUE(picture->palette[i])); } total*=numcols[i]; if (total>=16777216) total=16777216; // arbitrary upper limit. i do not want to do the "pondering..." for too long. } maxcols=0; maxi=0; // step 2: go through all the possible combinations and see which one is the most diverse. for (i=0;imaxcols) // more colors in this combination-> more bits in the mask -> more diversity in the final picture. { maxcols=bitcnt; maxi=i; // best combination so far. } } // at this point, maxi is the most diverse combination. // step 3: map it to the output palette for (i=0;i<16;i++) { maxplut[i]=ansicols[i][maxi%numcols[i]]; maxi/=numcols[i]; } return 0; } dmagnetic-0.37/src/frontends/default/default_callbacks.h0000644000175000017500000000453414430741336022140 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // the purpose of this file is to provide a callback-fallback until proper // user interfaces have been established. It will do ;) #ifndef LINEA_DEFAULT_CALLBACKS_H #define LINEA_DEFAULT_CALLBACKS_H #include "dMagnetic.h" #define DEFAULT_OK 0 #define DEFAULT_NOK -1 // interface to the lineA int default_cbNewOutput(void* context,char* headline,char* text,char* picname); int default_cbInputString(void* context,int* len,char* string); int default_cbDrawPicture(void* context,tdMagneticPicture* picture,char* picname,int mode); int default_cbSaveGame(void* context,char* filename,void* ptr,int len); int default_cbLoadGame(void* context,char* filename,void* ptr,int len); // interface to the main application int default_getsize(int* size); // if there is a .ini-file, extract the section for this user interface. the same goes for the command line parameters int default_open(void* hContext,FILE* f_inifile,int argc,char** argv); int default_setEngine(void *hContext,void* hEngine); extern const char *default_low_ansi_characters; extern const char *default_monochrome_characters; #endif dmagnetic-0.37/src/frontends/default/configuration.c0000644000175000017500000000707214430741336021357 0ustar useruser/* Copyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "configuration.h" int retrievefromini(FILE *f,char* section,char* entry,char* retstring,int retstringspace) { char line[1024]; int i; int j; int l,ls,le; char lc; int state; int found; found=0; state=0; // state 0: search for the [section] // state 1: search for the entry= // state 2: done if (!f) // no .ini file? { return found; // nothing to find in here! } ls=strlen(section); le=strlen(entry); fseek(f,0,SEEK_SET); do { if (fgets(line,sizeof(line),f)==NULL) return 0; l=strlen(line); j=0; lc=0; // reduce the line: remove spaces, unless they are escaped. for (i=0;i=32) // escaped { line[j++]=c; // keep this character } else if (c==';' || c<32) { line[j++]=0; // terminate the line here } else if (c>32 && c!='\\') { line[j++]=c; } lc=c; } line[j]=0; l=strlen(line); if (l) { if (line[0]=='[' && state==0) // this is a section, see if it is a match. { int match; match=(ls==l); // cannot be a match for (i=0;ile && line[le]=='=') match=1; for (i=0;i(l-le)) { memcpy(retstring,&line[le+1],l-le); found=1; } } } } while (!feof(f) && state!=2 && !found); return found; } int retrievefromcommandline(int argc,char** argv,char* parameter, char* retstring,int retstringspace) { int i; int found; found=0; for (i=0;istrlen(argv[i+1])) { memcpy(retstring,argv[i+1],strlen(argv[i+1])+1); } else found=-2; } } } } } return found; } dmagnetic-0.37/testcode/0000755000175000017500000000000014430741336013733 5ustar useruserdmagnetic-0.37/testcode/minitest.gfx0000644000175000017500000000311014430741336016270 0ustar useruserMaPiHd@ DD""r"''wwWF肆  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVs jDl!0F˜#bCd`͚6|EBY"QrEB"QrEB"QrEB"QrEB"QrEB"QrEB"EE4 i ѼTZ*$)$TZ*.SBQhQhMEBEE3 jI. Pressing Enter should close dMagnetic. dmagnetic-0.37/testcode/minitest.mag0000644000175000017500000000062014430741336016253 0ustar useruserMaSc*f<<r<o<o<m< <X<<<<V<i<r<t<u<a<l< <m<a<c<h<i<n<e< <i<s< <r<u<n<n<i<n<g<.< <P<l<e<a<s<e< <p<r<e<s<s< <E<n<t<e<r<.< <>< dmagnetic-0.37/checks.mk0000644000175000017500000001665414430741336013726 0ustar useruser#Copyright 2023, dettus@dettus.net # #Redistribution and use in source and binary forms, with or without modification, #are permitted provided that the following conditions are met: # #1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # #2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # #THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED #WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE #DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE #FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL #DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR #SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER #CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, #OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ### the following lines are the post-compilation test. This is a formality on some operating systems ########################### ECHO_CMD?=echo SHA256_CMD?=sha256 AWK_CMD?=awk ## the checks work by checking the sha256 sum of the output. ## since this one relies on the input, as well as the .ini file, ## it might not work as a post-install check. it is more of a ## post-compilation check, to see if it would support a new platform. ## check those graphic modes CHECKS= \ check-none \ check-monochrome \ check-monochrome_inv \ check-low_ansi \ check-low_ansi2 \ check-high_ansi \ check-high_ansi2 \ check-sixel \ check-utf ### this is the input. it does not really matter, but I thought I put in some easter egg in here. ### :::W WEYHE IST BUNT!! INPUT_none= "Weyhe-Leeste" INPUT_monochrome= "Sudweyhe" INPUT_monochrome_inv= "Melchiorshausen" INPUT_low_ansi= "Ahausen" INPUT_low_ansi2= "Kirchweyhe" INPUT_high_ansi= "Jeebel" INPUT_high_ansi2= "Erichshof" INPUT_sixel= "Dreye" INPUT_utf= "Lahausen" ### this is the output ### Do not change the CHECKSUM_none, because of the automated tests on the brew.sh repository. CHECKSUM_none= "3211640dc669f6b960a84a51559fc88a25bbc26966f01cdf44b9f4d9f4d71e1c" # DO!NOT!CHANGE! CHECKSUM_monochrome= "f64136e269dbcc87236cd8e091fbb95deae7de9d063b89814749fc71fbb5f632" CHECKSUM_monochrome_inv="99662688679715515bf8a13982ad07a9322e561609103337df8bfa702d8bc8de" CHECKSUM_low_ansi= "f88fb79247d9e9062c034f488b2a973e2e7cc0a8a299e3ea3d6ec293d5bf59fc" CHECKSUM_low_ansi2= "671dd3823eadb6591db0345f5e990a78a249da2431fc6c6e66371883431ee5ac" CHECKSUM_high_ansi= "0176d9f03f63c02bdb77af948a6ca7ab021e67744900c4b0bb1b8ed177fe721e" CHECKSUM_high_ansi2= "9e650c68fc910510967f2989f91c79511bef0d4c8ee8a6d636ae413f57bc1abf" CHECKSUM_sixel= "f7f997a8c881bf46968f93ffe635383c2349c6c3508ae7f783aae4405d7e043e" CHECKSUM_utf= "b09b342cdd4d4ba8489291c7f704965e0d868418cbf63b0a121445dbda07d481" ## so, here is my problem: I wanted to be able to run those checks in GNU make as well as BSD make. ## both of them have great features, like check-%, for-loops and check-{none,monochrome,sixel} as ## targets. but none of those features worked in both makes. ## the only way it truely did what it was supposed to do was by having a little bit of spaghettified ## code. please enjoy. ## ## btw: if you want to run the checks on Linux, run ## make SHA256_CMD="sha256sum" check # the code for the 8 checks is IDENTICAL. the only difference is the target's name. check-none: dMagnetic dMagnetic.ini if [ "`${ECHO_CMD} ${INPUT_${@:check-%=%}} | ./dMagnetic -ini dMagnetic.ini -vmode "${@:check-%=%}" -vcols 300 -vrows 300 -vecho -sres 1024x768 -mag testcode/minitest.mag | ${SHA256_CMD} | ${AWK_CMD} -F' ' '{ print $$1; }' - `" = ${CHECKSUM_${@:check-%=%}} ] ; then ${ECHO_CMD} "$@ OK" ; else ${ECHO_CMD} "$@ failed" ; exit 1 ; fi check-monochrome: dMagnetic dMagnetic.ini if [ "`${ECHO_CMD} ${INPUT_${@:check-%=%}} | ./dMagnetic -ini dMagnetic.ini -vmode "${@:check-%=%}" -vcols 300 -vrows 300 -vecho -sres 1024x768 -mag testcode/minitest.mag | ${SHA256_CMD} | ${AWK_CMD} -F' ' '{ print $$1; }' - `" = ${CHECKSUM_${@:check-%=%}} ] ; then ${ECHO_CMD} "$@ OK" ; else ${ECHO_CMD} "$@ failed" ; exit 1 ; fi check-monochrome_inv: dMagnetic dMagnetic.ini if [ "`${ECHO_CMD} ${INPUT_${@:check-%=%}} | ./dMagnetic -ini dMagnetic.ini -vmode "${@:check-%=%}" -vcols 300 -vrows 300 -vecho -sres 1024x768 -mag testcode/minitest.mag | ${SHA256_CMD} | ${AWK_CMD} -F' ' '{ print $$1; }' - `" = ${CHECKSUM_${@:check-%=%}} ] ; then ${ECHO_CMD} "$@ OK" ; else ${ECHO_CMD} "$@ failed" ; exit 1 ; fi check-low_ansi: dMagnetic dMagnetic.ini if [ "`${ECHO_CMD} ${INPUT_${@:check-%=%}} | ./dMagnetic -ini dMagnetic.ini -vmode "${@:check-%=%}" -vcols 300 -vrows 300 -vecho -sres 1024x768 -mag testcode/minitest.mag | ${SHA256_CMD} | ${AWK_CMD} -F' ' '{ print $$1; }' - `" = ${CHECKSUM_${@:check-%=%}} ] ; then ${ECHO_CMD} "$@ OK" ; else ${ECHO_CMD} "$@ failed" ; exit 1 ; fi check-low_ansi2: dMagnetic dMagnetic.ini if [ "`${ECHO_CMD} ${INPUT_${@:check-%=%}} | ./dMagnetic -ini dMagnetic.ini -vmode "${@:check-%=%}" -vcols 300 -vrows 300 -vecho -sres 1024x768 -mag testcode/minitest.mag | ${SHA256_CMD} | ${AWK_CMD} -F' ' '{ print $$1; }' - `" = ${CHECKSUM_${@:check-%=%}} ] ; then ${ECHO_CMD} "$@ OK" ; else ${ECHO_CMD} "$@ failed" ; exit 1 ; fi check-high_ansi: dMagnetic dMagnetic.ini if [ "`${ECHO_CMD} ${INPUT_${@:check-%=%}} | ./dMagnetic -ini dMagnetic.ini -vmode "${@:check-%=%}" -vcols 300 -vrows 300 -vecho -sres 1024x768 -mag testcode/minitest.mag | ${SHA256_CMD} | ${AWK_CMD} -F' ' '{ print $$1; }' - `" = ${CHECKSUM_${@:check-%=%}} ] ; then ${ECHO_CMD} "$@ OK" ; else ${ECHO_CMD} "$@ failed" ; exit 1 ; fi check-high_ansi2: dMagnetic dMagnetic.ini if [ "`${ECHO_CMD} ${INPUT_${@:check-%=%}} | ./dMagnetic -ini dMagnetic.ini -vmode "${@:check-%=%}" -vcols 300 -vrows 300 -vecho -sres 1024x768 -mag testcode/minitest.mag | ${SHA256_CMD} | ${AWK_CMD} -F' ' '{ print $$1; }' - `" = ${CHECKSUM_${@:check-%=%}} ] ; then ${ECHO_CMD} "$@ OK" ; else ${ECHO_CMD} "$@ failed" ; exit 1 ; fi check-sixel: dMagnetic dMagnetic.ini if [ "`${ECHO_CMD} ${INPUT_${@:check-%=%}} | ./dMagnetic -ini dMagnetic.ini -vmode "${@:check-%=%}" -vcols 300 -vrows 300 -vecho -sres 1024x768 -mag testcode/minitest.mag | ${SHA256_CMD} | ${AWK_CMD} -F' ' '{ print $$1; }' - `" = ${CHECKSUM_${@:check-%=%}} ] ; then ${ECHO_CMD} "$@ OK" ; else ${ECHO_CMD} "$@ failed" ; exit 1 ; fi check-utf: dMagnetic dMagnetic.ini if [ "`${ECHO_CMD} ${INPUT_${@:check-%=%}} | ./dMagnetic -ini dMagnetic.ini -vmode "${@:check-%=%}" -vcols 300 -vrows 300 -vecho -sres 1024x768 -mag testcode/minitest.mag | ${SHA256_CMD} | ${AWK_CMD} -F' ' '{ print $$1; }' - `" = ${CHECKSUM_${@:check-%=%}} ] ; then ${ECHO_CMD} "$@ OK" ; else ${ECHO_CMD} "$@ failed" ; exit 1 ; fi ############## invoke all the tests ############################################ check: ${CHECKS} @${ECHO_CMD} "***********************************************" @${ECHO_CMD} "Post-compilation tests for dMagnetic successful" @${ECHO_CMD} "***********************************************" do-test: check dmagnetic-0.37/LICENSE.txt0000644000175000017500000000241414430741336013745 0ustar useruserCopyright 2023, dettus@dettus.net Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.