diff --git a/ucon64/2.0/src/GoodCodes.txt b/ucon64/2.0/src/GoodCodes.txt new file mode 100644 index 0000000..f91b686 --- /dev/null +++ b/ucon64/2.0/src/GoodCodes.txt @@ -0,0 +1,173 @@ + .................. +...............: STANDARD CODES ::............... +: :\ +: [a] Alternate [p] Pirate :\ +: [b] Bad Dump [t] Trained :\ +: [f] Fixed [T-] OldTranslation :\ +: [T+] NewerTranslation :\ +: [h] Hack (-) Unknown Year :\ +: [o] Overdump [!] Verified Good Dump :\ +: (M#) Multilanguage (# of Languages) :\ +: (###) Checksum (??k) ROM Size :\ +: ZZZ_ Unclassified (Unl) Unlicensed :\ +:...............................................:\ + \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + + ................. +................: SPECIAL CODES ::............... +: :\ +: .-----Gameboy-----. .----Super Nintendo----. :\ +: [ [C] Color ] [ (BS) BS ROMs ] :\ +: [ [S] Super ] [ (ST) Sufami Turbo ] :\ +: [ [BF] Bung Fix ] [ (NP) Nintendo Power ] :\ +: `-----------------' `----------------------' :\ +: .--------Atari---------. :\ +: .-----Genesis-----. [ (PAL) Euro Version ] :\ +: [ (1) Japan ] `----------------------' :\ +: [ (4) USA ] .---------GBA----------. :\ +: [ (5) NTSC Only ] [ [hI??] Intro hacks ] :\ +: [ (8) PAL Only ] `----------------------' :\ +: [ (B) non USA ] .--------Coleco--------. :\ +: [ [c] Checksum ] [ (Adam) ADAM Version ] :\ +: [ [x] Bad Checksum] `----------------------' :\ +: [ [R-] Countries ] :\ +: `-----------------' :\ +: .--------NES/FC--------. :\ +: .--NeoGeo Pocket--. [ (PC10) PlayChoice 10 ] :\ +: [ [M] Mono Only ] [ (VS) Versus ] :\ +: `-----------------' [ [hFFE] FFE Copier fmt] :\ +: `----------------------' :\ +:...............................................:\ + \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + + ................. +................: COUNTRY CODES ::............... +: :\ +: (1) Japan & Korea (4) USA & BrazilNTSC :\ +: (A) Australia (J) Japan :\ +: (B) non USA (Genesis) (K) Korea :\ +: (C) China (NL) Netherlands :\ +: (E) Europe (PD) Public Domain :\ +: (F) France (S) Spain :\ +: (F) World (Genesis) :\ +: (FC) French Canadian (SW) Sweden :\ +: (FN) Finland (U) USA :\ +: (G) Germany (UK) England :\ +: (GR) Greece (Unk) Unknown Country :\ +: (HK) Hong Kong (I) Italy :\ +: (H) Holland (Unl) Unlicensed :\ +:...............................................:\ + \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + + ....................... +.............: STANDARD CODE NOTES ::............ +: :\ +: [a] This is simply an alternate version of a :\ +: ROM. Many games have been re-released to :\ +: fix bugs or even to eliminate Game Genie :\ +: codes (Yes, Nintendo hates that device). :\ +: ------------------- :\ +: [b] A bad dump often occurs with an older :\ +: game or a faulty dumper (bad connection). :\ +: Another common source of [b] ROMs is a :\ +: corrupted upload to a release FTP. :\ +: ------------------- :\ +: [f] A fixed game has been altered in some way :\ +: so that it will run better on a copier :\ +: or emulator. :\ +: ------------------- :\ +: [h] Something in this ROM is not quite as it :\ +: should be. Often a hacked ROM simply has :\ +: a changed header or has been enabled to :\ +: run in different regions. Other times it :\ +: could be a release group intro, or just :\ +: some kind of cheating or funny hack. :\ +: ------------------- :\ +: [o] An overdumped ROM image has more data :\ +: than is actually in the cart. The extra :\ +: information means nothing and is removed :\ +: from the true image. :\ +: ------------------- :\ +: [t] A trainer is special code which executes :\ +: before the game is begun. It allows you :\ +: to access cheats from a menu. :\ +: ------------------- :\ +: [!] Verified good dump. Thank God for these! :\ +:...............................................:\ + \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + + ...................... +.............: SPECIAL CODE NOTES ::............. +: :\ +: **** SNES **** :\ +: (BS) These Japanese ROMs were distributed :\ +: through a satellite system in Japan :\ +: known as the Broadcast Satellaview. :\ +: They were transmitted along with a TV :\ +: show which was connected to the game in :\ +: some way. These games were only playable :\ +: during the show, and thus stop after an :\ +: hour, and many were timed so that only :\ +: certain time periods were playable. :\ +: ------------------- :\ +: (ST) The Sufami Turbo device allowed two :\ +: GameBoy sized carts to be plugged into :\ +: the SNES. Certain carts combined into :\ +: new games much like the Sonic & Knuckles :\ +: lock-on technology by Sega. :\ +: ------------------- :\ +: (NP) Nintendo Power has been known to release :\ +: games only available to its subscribers. :\ +: Most of these ROMs are Japanese, as this :\ +: practice occured mainly in Japan. :\ +: ------------------- :\ +: :\ +: **** Genesis **** :\ +: (1) Carts with this code will run on both :\ +: Japanese and Korean machines. :\ +: ------------------- :\ +: (4) While this code is technically the same :\ +: as a (U) code, it is a newer header :\ +: format and represents that the cart will :\ +: run on USA and Brazil NTSC machines. :\ +: ------------------- :\ +: (B) This country code indicates that the :\ +: cart will run on any non US machine. :\ +: ------------------- :\ +: [c] This code represents a cart with known :\ +: faulty checksum routines. :\ +: ------------------- :\ +: :\ +: **** GameBoy **** :\ +: [BF] Bung released a programmable cartridge :\ +: compatable with the GameBoy which could :\ +: hold any data you wished to play. :\ +: However, many games do not function on :\ +: Bung v1.0 carts and have to be 'fixed.' :\ +: ------------------- :\ +: :\ +: **** Nintendo **** :\ +: PC10 The PlayChoice 10 was an arcade unit :\ +: which played exact copies of NES games :\ +: in an arcade cabinet. The machines had a :\ +: choice of 10 games to choose from and :\ +: ran for about 3 minutes on 25 cents. :\ +: ------------------- :\ +: :\ +: VS The Versus system ran on similar hard- :\ +: ware to the PC10 machines, but simply :\ +: allowed you to play against each other. :\ +:...............................................:\ + \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + + ........... +...................: Credits ::.................. +: :\ +: Document written by Psych0phobiA / q^-o|o-^p :\ +: :\ +: All codes developed by Cowering for the :\ +: Goodxxxx series ROM file renaming utilities. :\ +: :\ +: Visit #rareroms on NewNet in IRC! :\ +:...............................................:\ + \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ \ No newline at end of file diff --git a/ucon64/2.0/src/Makefile.in b/ucon64/2.0/src/Makefile.in new file mode 100644 index 0000000..7c73fef --- /dev/null +++ b/ucon64/2.0/src/Makefile.in @@ -0,0 +1,380 @@ +.PHONY: all clean distclean install uninstall + +@DEFINE_DLOPEN_MAKE@ +@DEFINE_ZLIB_MAKE@ +@DEFINE_DISCMAGE_MAKE@ +@DEFINE_USB_MAKE@ +@DEFINE_LIBCD64_MAKE@ + +CC=@CC@ +CFLAGS=-I. -Wall -W -O3 @DEFS@ +LDFLAGS=-s +TARGETS= + +ifdef USE_DISCMAGE +LIBNAME_DM=discmage +endif + +# The test for Cygwin should be done before the test for DJGPP, because the +# environment variable DJGPP can be set under Bash for people who have +# installed both GCC (and friends) ports. + +GCC_WIN=0 +# test cygwin before DJGPP; OSTYPE is not exported on Cygwin +ifeq ($(TERM),cygwin) +GCC_WIN=1 +endif +# test msys before DJGPP; MSYS, MinGW's POSIX build environment +ifeq ($(OSTYPE),msys) +GCC_WIN=1 +endif + +ifeq ($(GCC_WIN),1) + +ifdef USE_DISCMAGE +FULLLIBNAME_DM=$(LIBNAME_DM).dll +ifndef DLOPEN +LDFLAGS+=libdiscmage/$(LIBNAME_DM).a +endif +endif + +else +ifdef DJGPP + +ifdef USE_DISCMAGE +FULLLIBNAME_DM=$(LIBNAME_DM).dxe +ifndef DLOPEN +LDFLAGS+=libdiscmage/$(LIBNAME_DM).a +endif +endif + +else # Unix, BeOS or Mac OS X (Darwin) + +ifeq ($(findstring openbsd,$(OSTYPE)),openbsd) # for example "openbsd3.4" +# i386_iopl() is located in libi386.a +LDFLAGS+=@LIBI386_MAKE@ +endif + +ifdef USE_DISCMAGE +ifeq ($(findstring darwin,$(OSTYPE)),darwin) # for example "darwin7.0" +FULLLIBNAME_DM=$(LIBNAME_DM).dylib +else +FULLLIBNAME_DM=$(LIBNAME_DM).so +endif +endif + +ifdef DLOPEN + +ifneq ($(OSTYPE),beos) +ifeq ($(findstring freebsd,$(OSTYPE)),) # false if OSTYPE contains "freebsd" +ifeq ($(findstring openbsd,$(OSTYPE)),) # false if OSTYPE contains "openbsd" +LDFLAGS+=-ldl +endif +endif +endif + +else # DLOPEN +ifdef USE_DISCMAGE # GNU specific: "simply expanded variable" +FULLLIBNAME_DM:=$(addprefix lib,$(FULLLIBNAME_DM)) +LDFLAGS+=-Llibdiscmage -l$(LIBNAME_DM) +endif + +endif + +endif # DJGPP +endif # GCC_WIN + +TARGETS+=libdiscmage/$(FULLLIBNAME_DM) + + +ifdef USE_LIBCD64 +LDFLAGS+=backup/libcd64/libcd64.a +TARGETS+=backup/libcd64/libcd64.a +endif + + +OBJECTS=ucon64.o ucon64_dat.o ucon64_misc.o ucon64_opts.o \ + misc/chksum.o misc/file.o misc/getopt.o misc/getopt2.o \ + misc/misc.o misc/parallel.o misc/property.o misc/string.o \ + patch/aps.o patch/bsl.o patch/gg.o patch/ips.o patch/pal4u.o \ + patch/ppf.o patch/xps.o \ + console/dc.o console/gb.o console/gba.o console/genesis.o \ + console/jaguar.o console/lynx.o console/n64.o console/neogeo.o \ + console/nes.o console/ngp.o console/pce.o console/psx.o console/sms.o \ + console/snes.o console/swan.o \ + backup/cd64.o backup/cmc.o backup/dex.o backup/doctor64.o \ + backup/doctor64jr.o backup/f2a.o backup/fal.o backup/ffe.o \ + backup/fig.o backup/gbx.o backup/gd.o backup/interceptor.o \ + backup/lynxit.o backup/mccl.o backup/mcd.o backup/md-pro.o \ + backup/mgd.o backup/msg.o backup/pce-pro.o backup/pl.o \ + backup/psxpblib.o backup/sflash.o backup/smc.o backup/smd.o \ + backup/smsgg-pro.o backup/ssc.o backup/swc.o backup/tototek.o \ + backup/ufo.o backup/yoko.o backup/z64.o +ifdef USE_ZLIB +LDFLAGS+=-lz +OBJECTS+=misc/archive.o misc/map.o misc/unzip.o +endif +ifdef USE_USB +LDFLAGS+=-lusb +OBJECTS+=misc/usb.o +endif + +ifdef DLOPEN +OBJECTS+=misc/dlopen.o +ifndef USE_ZLIB +ifeq ($(GCC_WIN),1) +else +ifdef DJGPP # DJGPP code in dlopen needs map code +OBJECTS+=misc/map.o +endif # DJGPP +endif # GCC_WIN +endif # USE_ZLIB +else +ifeq ($(GCC_WIN),1) # Cygwin/MinGW code in ucon64_misc needs dlopen code +OBJECTS+=misc/dlopen.o +endif # GCC_WIN +endif # DLOPEN + + +TARGET=ucon64 +ifeq ($(GCC_WIN),1) +TARGET:=$(addsuffix .exe,$(TARGET)) # adding .exe avoids "problems" with Cygwin/MinGW +else +ifdef DJGPP # OSTYPE is not defined by default under DOS +TARGET:=$(addsuffix .exe,$(TARGET)) +endif # DJGPP +endif # GCC_WIN +TARGETS+=$(TARGET) + +all: $(TARGETS) + + +CLEAN_CMD=rm -f $(TARGET) $(OBJECTS) *.core *.stackdump *~ */*~ */*/*~; \ +cd libdiscmage && $(MAKE) clean; \ +cd ../backup/libcd64 && $(MAKE) clean + +clean: +ifeq ($(GCC_WIN),1) + $(CLEAN_CMD) +else +ifdef DJGPP + del *.o + del patch\*.o + del console\*.o + del backup\*.o + del misc\*.o + del $(TARGET) + cd libdiscmage + $(MAKE) clean + cd ../backup/libcd64 + $(MAKE) clean + cd ../.. +else # Unix, BeOS or Mac OS X (Darwin) + $(CLEAN_CMD) +endif # DJGPP +endif # GCC_WIN + + +DISTCLEAN_CMD=rm -f Makefile config.log config.status config.cache config.h; \ +cd libdiscmage && $(MAKE) distclean; \ +cd ../backup/libcd64 && $(MAKE) clean +# libcd64 Makefile has no distclean target + +distclean: clean +ifeq ($(GCC_WIN),1) + $(DISTCLEAN_CMD) +else +ifdef DJGPP + del Makefile + del config.log + del config.status + del config.cache + del config.h + cd libdiscmage + $(MAKE) distclean + cd ../backup/libcd64 + $(MAKE) clean + cd ../.. +else + $(DISTCLEAN_CMD) +endif # DJGPP +endif # GCC_WIN + + +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ + +backup/cd64.o: backup/cd64.c + $(CC) $(CFLAGS) -Ibackup/libcd64 -c $< -o $@ + + +ifdef USE_DISCMAGE +libdiscmage/$(FULLLIBNAME_DM): +ifeq ($(GCC_WIN),1) + cd libdiscmage && $(MAKE) +else +ifdef DJGPP + cd libdiscmage + $(MAKE) + cd .. +else + cd libdiscmage && $(MAKE) +endif # DJGPP +endif # GCC_WIN +endif # USE_DISCMAGE + +ifdef USE_LIBCD64 +backup/libcd64/libcd64.a: +ifeq ($(GCC_WIN),1) + cd backup/libcd64 && $(MAKE) +else +ifdef DJGPP + cd backup/libcd64 + $(MAKE) + cd ../.. +else + cd backup/libcd64 && $(MAKE) +endif # DJGPP +endif # GCC_WIN +endif # USE_DISCMAGE + +$(TARGET): $(OBJECTS) + $(CC) $(OBJECTS) $(LDFLAGS) -o $@ + + +install: +ifeq ($(TERM),cygwin) # test cygwin before DJGPP +else +ifeq ($(OSTYPE),msys) # test msys before DJGPP +else +ifdef DJGPP +else +ifeq ($(OSTYPE),beos) + ./install_beos.sh +else + ./install.sh +endif # beos +endif # DJGPP +endif # msys +endif # cygwin +ifndef DLOPEN + cd libdiscmage && $(MAKE) install +endif + + +uninstall: +ifeq ($(TERM),cygwin) # test cygwin before DJGPP +else +ifeq ($(OSTYPE),msys) # test msys before DJGPP +else +ifdef DJGPP +else +ifeq ($(OSTYPE),beos) + rm -f $(HOME)/config/bin/$(TARGET) +else + rm -f /usr/local/bin/$(TARGET) +endif # beos +endif # DJGPP +endif # msys +endif # cygwin +ifndef DLOPEN + cd libdiscmage && $(MAKE) uninstall +endif + + +# Dependencies + +# Most source files include these +UCON64_STD_H=ucon64.h ucon64_misc.h misc/misc.h config.h ucon64_defines.h + +misc/archive.o: misc/archive.h misc/map.h config.h +misc/chksum.o: misc/chksum.h config.h +misc/dlopen.o: misc/dlopen.h misc/dxedll_pub.h config.h +misc/getopt.o: misc/getopt.h +misc/map.o: misc/map.h config.h +misc/misc.o: misc/misc.h misc/archive.h config.h +misc/parallel.o: misc/parallel.h config.h +misc/usb.o: misc/usb.h config.h +misc/unzip.o: misc/unzip.h config.h +ucon64.o: misc/dlopen.h misc/getopt.h ucon64_dat.h ucon64_opts.h \ + console/dc.h console/gb.h console/gba.h console/genesis.h \ + console/jaguar.h console/lynx.h console/n64.h console/neogeo.h \ + console/nes.h console/ngp.h console/pce.h console/psx.h console/sms.h \ + console/snes.h console/swan.h \ + backup/cd64.h backup/dex.h backup/doctor64.h backup/doctor64jr.h \ + backup/f2a.h backup/fal.h backup/ffe.h backup/fig.h backup/gbx.h \ + backup/gd.h backup/interceptor.h backup/lynxit.h backup/mccl.h \ + backup/mcd.h backup/md-pro.h backup/mgd.h backup/msg.h \ + backup/pce-pro.h backup/pl.h backup/smc.h backup/smd.h \ + backup/smsgg-pro.h backup/ssc.h backup/swc.h backup/tototek.h \ + backup/ufo.h backup/yoko.h backup/z64.h \ + patch/aps.h patch/bsl.h patch/gg.h patch/ips.h patch/pal4u.h \ + patch/ppf.h patch/xps.h $(UCON64_STD_H) +ucon64_dat.o: ucon64_dat.h $(UCON64_STD_H) +ucon64_misc.o: misc/dlopen.h $(UCON64_STD_H) +ucon64_opts.o: misc/dlopen.h misc/getopt.h ucon64_dat.h ucon64_opts.h \ + console/dc.h console/gb.h console/gba.h console/genesis.h \ + console/jaguar.h console/lynx.h console/n64.h console/neogeo.h \ + console/nes.h console/ngp.h console/pce.h console/psx.h \ + console/sms.h console/snes.h console/swan.h \ + backup/cd64.h backup/dex.h backup/doctor64.h \ + backup/doctor64jr.h backup/f2a.h backup/fal.h backup/ffe.h \ + backup/fig.h backup/gbx.h backup/gd.h backup/interceptor.h \ + backup/lynxit.h backup/mccl.h backup/mcd.h backup/md-pro.h \ + backup/mgd.h backup/msg.h backup/pce-pro.h backup/pl.h \ + backup/smc.h backup/smd.h backup/smsgg-pro.h backup/ssc.h \ + backup/swc.h backup/tototek.h backup/ufo.h backup/yoko.h \ + backup/z64.h \ + patch/aps.h patch/bsl.h patch/gg.h patch/ips.h patch/pal4u.h \ + patch/ppf.h patch/xps.h $(UCON64_STD_H) +console/dc.o: console/dc.h $(UCON64_STD_H) +console/gb.o: console/gb.h backup/mgd.h $(UCON64_STD_H) +console/gba.o: console/gba.h $(UCON64_STD_H) +console/genesis.o: console/genesis.h backup/smd.h backup/mgd.h $(UCON64_STD_H) +console/jaguar.o: console/jaguar.h $(UCON64_STD_H) +console/lynx.o: console/lynx.h $(UCON64_STD_H) +console/n64.o: console/n64.h $(UCON64_STD_H) +console/neogeo.o: console/neogeo.h $(UCON64_STD_H) +console/nes.o: console/nes.h $(UCON64_STD_H) +console/ngp.o: console/ngp.h $(UCON64_STD_H) +console/pce.o: console/pce.h backup/mgd.h $(UCON64_STD_H) +console/psx.o: console/psx.h $(UCON64_STD_H) +console/sms.o: console/sms.h backup/smd.h backup/mgd.h $(UCON64_STD_H) +console/snes.o: console/snes.h backup/mgd.h $(UCON64_STD_H) +console/swan.o: console/swan.h $(UCON64_STD_H) +backup/cd64.o: backup/cd64.h $(UCON64_STD_H) +backup/cmc.o: backup/cmc.h $(UCON64_STD_H) +backup/dex.o: backup/dex.h backup/psxpblib.h $(UCON64_STD_H) +backup/doctor64.o: backup/doctor64.h $(UCON64_STD_H) +backup/doctor64jr.o: backup/doctor64jr.h $(UCON64_STD_H) +backup/f2a.o: backup/f2a.h $(UCON64_STD_H) +backup/fal.o: backup/fal.h backup/cartlib.c $(UCON64_STD_H) +backup/ffe.o: backup/ffe.h $(UCON64_STD_H) +backup/fig.o: backup/fig.h console/snes.h $(UCON64_STD_H) +backup/gd.o: backup/gd.h console/snes.h $(UCON64_STD_H) +backup/gbx.o: backup/gbx.h $(UCON64_STD_H) +backup/lynxit.o: backup/lynxit.h $(UCON64_STD_H) +backup/mccl.o: backup/mccl.h $(UCON64_STD_H) +backup/mcd.o: backup/mcd.h $(UCON64_STD_H) +backup/md-pro.o: backup/md-pro.h backup/tototek.h $(UCON64_STD_H) +backup/mgd.o: backup/mgd.h $(UCON64_STD_H) +backup/msg.o: backup/msg.h backup/ffe.h $(UCON64_STD_H) +backup/pce-pro.o: backup/pce-pro.h backup/tototek.h $(UCON64_STD_H) +backup/pl.o: backup/pl.h $(UCON64_STD_H) +backup/psxpblib.o: backup/psxpblib.h $(UCON64_STD_H) +backup/sflash.o: backup/sflash.h backup/tototek.h $(UCON64_STD_H) +backup/smc.o: backup/smc.h backup/ffe.h $(UCON64_STD_H) +backup/smd.o: backup/smd.h backup/ffe.h $(UCON64_STD_H) +backup/smsgg-pro.o: backup/smsgg-pro.h backup/tototek.h $(UCON64_STD_H) +backup/swc.o: backup/swc.h backup/ffe.h console/snes.h $(UCON64_STD_H) +backup/tototek.o: backup/tototek.h $(UCON64_STD_H) +backup/ufo.o: backup/ufo.h $(UCON64_STD_H) +backup/yoko.o: backup/yoko.h $(UCON64_STD_H) +backup/z64.o: backup/z64.h $(UCON64_STD_H) +patch/aps.o: patch/aps.h $(UCON64_STD_H) +patch/bsl.o: patch/bsl.h $(UCON64_STD_H) +patch/gg.o: patch/gg.h $(UCON64_STD_H) +patch/ips.o: patch/ips.h $(UCON64_STD_H) +patch/pal4u.o: patch/pal4u.h $(UCON64_STD_H) +patch/ppf.o: patch/ppf.h $(UCON64_STD_H) +patch/xps.o: patch/xps.h $(UCON64_STD_H) diff --git a/ucon64/2.0/src/Makefile.orig b/ucon64/2.0/src/Makefile.orig new file mode 100644 index 0000000..ccb1e17 --- /dev/null +++ b/ucon64/2.0/src/Makefile.orig @@ -0,0 +1,380 @@ +.PHONY: all clean distclean install uninstall + +DLOPEN=1 +#USE_ZLIB=1 +USE_DISCMAGE=1 +#USE_USB=1 +USE_LIBCD64=1 + +CC=gcc +CFLAGS=-I. -Wall -W -O3 -DHAVE_CONFIG_H +LDFLAGS=-s +TARGETS= + +ifdef USE_DISCMAGE +LIBNAME_DM=discmage +endif + +# The test for Cygwin should be done before the test for DJGPP, because the +# environment variable DJGPP can be set under Bash for people who have +# installed both GCC (and friends) ports. + +GCC_WIN=0 +# test cygwin before DJGPP; OSTYPE is not exported on Cygwin +ifeq ($(TERM),cygwin) +GCC_WIN=1 +endif +# test msys before DJGPP; MSYS, MinGW's POSIX build environment +ifeq ($(OSTYPE),msys) +GCC_WIN=1 +endif + +ifeq ($(GCC_WIN),1) + +ifdef USE_DISCMAGE +FULLLIBNAME_DM=$(LIBNAME_DM).dll +ifndef DLOPEN +LDFLAGS+=libdiscmage/$(LIBNAME_DM).a +endif +endif + +else +ifdef DJGPP + +ifdef USE_DISCMAGE +FULLLIBNAME_DM=$(LIBNAME_DM).dxe +ifndef DLOPEN +LDFLAGS+=libdiscmage/$(LIBNAME_DM).a +endif +endif + +else # Unix, BeOS or Mac OS X (Darwin) + +ifeq ($(findstring openbsd,$(OSTYPE)),openbsd) # for example "openbsd3.4" +# i386_iopl() is located in libi386.a +LDFLAGS+= +endif + +ifdef USE_DISCMAGE +ifeq ($(findstring darwin,$(OSTYPE)),darwin) # for example "darwin7.0" +FULLLIBNAME_DM=$(LIBNAME_DM).dylib +else +FULLLIBNAME_DM=$(LIBNAME_DM).so +endif +endif + +ifdef DLOPEN + +ifneq ($(OSTYPE),beos) +ifeq ($(findstring freebsd,$(OSTYPE)),) # false if OSTYPE contains "freebsd" +ifeq ($(findstring openbsd,$(OSTYPE)),) # false if OSTYPE contains "openbsd" +LDFLAGS+=-ldl +endif +endif +endif + +else # DLOPEN +ifdef USE_DISCMAGE # GNU specific: "simply expanded variable" +FULLLIBNAME_DM:=$(addprefix lib,$(FULLLIBNAME_DM)) +LDFLAGS+=-Llibdiscmage -l$(LIBNAME_DM) +endif + +endif + +endif # DJGPP +endif # GCC_WIN + +TARGETS+=libdiscmage/$(FULLLIBNAME_DM) + + +ifdef USE_LIBCD64 +LDFLAGS+=backup/libcd64/libcd64.a +TARGETS+=backup/libcd64/libcd64.a +endif + + +OBJECTS=ucon64.o ucon64_dat.o ucon64_misc.o ucon64_opts.o \ + misc/chksum.o misc/file.o misc/getopt.o misc/getopt2.o \ + misc/misc.o misc/parallel.o misc/property.o misc/string.o \ + patch/aps.o patch/bsl.o patch/gg.o patch/ips.o patch/pal4u.o \ + patch/ppf.o patch/xps.o \ + console/dc.o console/gb.o console/gba.o console/genesis.o \ + console/jaguar.o console/lynx.o console/n64.o console/neogeo.o \ + console/nes.o console/ngp.o console/pce.o console/psx.o console/sms.o \ + console/snes.o console/swan.o \ + backup/cd64.o backup/cmc.o backup/dex.o backup/doctor64.o \ + backup/doctor64jr.o backup/f2a.o backup/fal.o backup/ffe.o \ + backup/fig.o backup/gbx.o backup/gd.o backup/interceptor.o \ + backup/lynxit.o backup/mccl.o backup/mcd.o backup/md-pro.o \ + backup/mgd.o backup/msg.o backup/pce-pro.o backup/pl.o \ + backup/psxpblib.o backup/sflash.o backup/smc.o backup/smd.o \ + backup/smsgg-pro.o backup/ssc.o backup/swc.o backup/tototek.o \ + backup/ufo.o backup/yoko.o backup/z64.o +ifdef USE_ZLIB +LDFLAGS+=-lz +OBJECTS+=misc/archive.o misc/map.o misc/unzip.o +endif +ifdef USE_USB +LDFLAGS+=-lusb +OBJECTS+=misc/usb.o +endif + +ifdef DLOPEN +OBJECTS+=misc/dlopen.o +ifndef USE_ZLIB +ifeq ($(GCC_WIN),1) +else +ifdef DJGPP # DJGPP code in dlopen needs map code +OBJECTS+=misc/map.o +endif # DJGPP +endif # GCC_WIN +endif # USE_ZLIB +else +ifeq ($(GCC_WIN),1) # Cygwin/MinGW code in ucon64_misc needs dlopen code +OBJECTS+=misc/dlopen.o +endif # GCC_WIN +endif # DLOPEN + + +TARGET=ucon64 +ifeq ($(GCC_WIN),1) +TARGET:=$(addsuffix .exe,$(TARGET)) # adding .exe avoids "problems" with Cygwin/MinGW +else +ifdef DJGPP # OSTYPE is not defined by default under DOS +TARGET:=$(addsuffix .exe,$(TARGET)) +endif # DJGPP +endif # GCC_WIN +TARGETS+=$(TARGET) + +all: $(TARGETS) + + +CLEAN_CMD=rm -f $(TARGET) $(OBJECTS) *.core *.stackdump *~ */*~ */*/*~; \ +cd libdiscmage && $(MAKE) clean; \ +cd ../backup/libcd64 && $(MAKE) clean + +clean: +ifeq ($(GCC_WIN),1) + $(CLEAN_CMD) +else +ifdef DJGPP + del *.o + del patch\*.o + del console\*.o + del backup\*.o + del misc\*.o + del $(TARGET) + cd libdiscmage + $(MAKE) clean + cd ../backup/libcd64 + $(MAKE) clean + cd ../.. +else # Unix, BeOS or Mac OS X (Darwin) + $(CLEAN_CMD) +endif # DJGPP +endif # GCC_WIN + + +DISTCLEAN_CMD=rm -f Makefile config.log config.status config.cache config.h; \ +cd libdiscmage && $(MAKE) distclean; \ +cd ../backup/libcd64 && $(MAKE) clean +# libcd64 Makefile has no distclean target + +distclean: clean +ifeq ($(GCC_WIN),1) + $(DISTCLEAN_CMD) +else +ifdef DJGPP + del Makefile + del config.log + del config.status + del config.cache + del config.h + cd libdiscmage + $(MAKE) distclean + cd ../backup/libcd64 + $(MAKE) clean + cd ../.. +else + $(DISTCLEAN_CMD) +endif # DJGPP +endif # GCC_WIN + + +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ + +backup/cd64.o: backup/cd64.c + $(CC) $(CFLAGS) -Ibackup/libcd64 -c $< -o $@ + + +ifdef USE_DISCMAGE +libdiscmage/$(FULLLIBNAME_DM): +ifeq ($(GCC_WIN),1) + cd libdiscmage && $(MAKE) +else +ifdef DJGPP + cd libdiscmage + $(MAKE) + cd .. +else + cd libdiscmage && $(MAKE) +endif # DJGPP +endif # GCC_WIN +endif # USE_DISCMAGE + +ifdef USE_LIBCD64 +backup/libcd64/libcd64.a: +ifeq ($(GCC_WIN),1) + cd backup/libcd64 && $(MAKE) +else +ifdef DJGPP + cd backup/libcd64 + $(MAKE) + cd ../.. +else + cd backup/libcd64 && $(MAKE) +endif # DJGPP +endif # GCC_WIN +endif # USE_DISCMAGE + +$(TARGET): $(OBJECTS) + $(CC) $(OBJECTS) $(LDFLAGS) -o $@ + + +install: +ifeq ($(TERM),cygwin) # test cygwin before DJGPP +else +ifeq ($(OSTYPE),msys) # test msys before DJGPP +else +ifdef DJGPP +else +ifeq ($(OSTYPE),beos) + ./install_beos.sh +else + ./install.sh +endif # beos +endif # DJGPP +endif # msys +endif # cygwin +ifndef DLOPEN + cd libdiscmage && $(MAKE) install +endif + + +uninstall: +ifeq ($(TERM),cygwin) # test cygwin before DJGPP +else +ifeq ($(OSTYPE),msys) # test msys before DJGPP +else +ifdef DJGPP +else +ifeq ($(OSTYPE),beos) + rm -f $(HOME)/config/bin/$(TARGET) +else + rm -f /usr/local/bin/$(TARGET) +endif # beos +endif # DJGPP +endif # msys +endif # cygwin +ifndef DLOPEN + cd libdiscmage && $(MAKE) uninstall +endif + + +# Dependencies + +# Most source files include these +UCON64_STD_H=ucon64.h ucon64_misc.h misc/misc.h config.h ucon64_defines.h + +misc/archive.o: misc/archive.h misc/map.h config.h +misc/chksum.o: misc/chksum.h config.h +misc/dlopen.o: misc/dlopen.h misc/dxedll_pub.h config.h +misc/getopt.o: misc/getopt.h +misc/map.o: misc/map.h config.h +misc/misc.o: misc/misc.h misc/archive.h config.h +misc/parallel.o: misc/parallel.h config.h +misc/usb.o: misc/usb.h config.h +misc/unzip.o: misc/unzip.h config.h +ucon64.o: misc/dlopen.h misc/getopt.h ucon64_dat.h ucon64_opts.h \ + console/dc.h console/gb.h console/gba.h console/genesis.h \ + console/jaguar.h console/lynx.h console/n64.h console/neogeo.h \ + console/nes.h console/ngp.h console/pce.h console/psx.h console/sms.h \ + console/snes.h console/swan.h \ + backup/cd64.h backup/dex.h backup/doctor64.h backup/doctor64jr.h \ + backup/f2a.h backup/fal.h backup/ffe.h backup/fig.h backup/gbx.h \ + backup/gd.h backup/interceptor.h backup/lynxit.h backup/mccl.h \ + backup/mcd.h backup/md-pro.h backup/mgd.h backup/msg.h \ + backup/pce-pro.h backup/pl.h backup/smc.h backup/smd.h \ + backup/smsgg-pro.h backup/ssc.h backup/swc.h backup/tototek.h \ + backup/ufo.h backup/yoko.h backup/z64.h \ + patch/aps.h patch/bsl.h patch/gg.h patch/ips.h patch/pal4u.h \ + patch/ppf.h patch/xps.h $(UCON64_STD_H) +ucon64_dat.o: ucon64_dat.h $(UCON64_STD_H) +ucon64_misc.o: misc/dlopen.h $(UCON64_STD_H) +ucon64_opts.o: misc/dlopen.h misc/getopt.h ucon64_dat.h ucon64_opts.h \ + console/dc.h console/gb.h console/gba.h console/genesis.h \ + console/jaguar.h console/lynx.h console/n64.h console/neogeo.h \ + console/nes.h console/ngp.h console/pce.h console/psx.h \ + console/sms.h console/snes.h console/swan.h \ + backup/cd64.h backup/dex.h backup/doctor64.h \ + backup/doctor64jr.h backup/f2a.h backup/fal.h backup/ffe.h \ + backup/fig.h backup/gbx.h backup/gd.h backup/interceptor.h \ + backup/lynxit.h backup/mccl.h backup/mcd.h backup/md-pro.h \ + backup/mgd.h backup/msg.h backup/pce-pro.h backup/pl.h \ + backup/smc.h backup/smd.h backup/smsgg-pro.h backup/ssc.h \ + backup/swc.h backup/tototek.h backup/ufo.h backup/yoko.h \ + backup/z64.h \ + patch/aps.h patch/bsl.h patch/gg.h patch/ips.h patch/pal4u.h \ + patch/ppf.h patch/xps.h $(UCON64_STD_H) +console/dc.o: console/dc.h $(UCON64_STD_H) +console/gb.o: console/gb.h backup/mgd.h $(UCON64_STD_H) +console/gba.o: console/gba.h $(UCON64_STD_H) +console/genesis.o: console/genesis.h backup/smd.h backup/mgd.h $(UCON64_STD_H) +console/jaguar.o: console/jaguar.h $(UCON64_STD_H) +console/lynx.o: console/lynx.h $(UCON64_STD_H) +console/n64.o: console/n64.h $(UCON64_STD_H) +console/neogeo.o: console/neogeo.h $(UCON64_STD_H) +console/nes.o: console/nes.h $(UCON64_STD_H) +console/ngp.o: console/ngp.h $(UCON64_STD_H) +console/pce.o: console/pce.h backup/mgd.h $(UCON64_STD_H) +console/psx.o: console/psx.h $(UCON64_STD_H) +console/sms.o: console/sms.h backup/smd.h backup/mgd.h $(UCON64_STD_H) +console/snes.o: console/snes.h backup/mgd.h $(UCON64_STD_H) +console/swan.o: console/swan.h $(UCON64_STD_H) +backup/cd64.o: backup/cd64.h $(UCON64_STD_H) +backup/cmc.o: backup/cmc.h $(UCON64_STD_H) +backup/dex.o: backup/dex.h backup/psxpblib.h $(UCON64_STD_H) +backup/doctor64.o: backup/doctor64.h $(UCON64_STD_H) +backup/doctor64jr.o: backup/doctor64jr.h $(UCON64_STD_H) +backup/f2a.o: backup/f2a.h $(UCON64_STD_H) +backup/fal.o: backup/fal.h backup/cartlib.c $(UCON64_STD_H) +backup/ffe.o: backup/ffe.h $(UCON64_STD_H) +backup/fig.o: backup/fig.h console/snes.h $(UCON64_STD_H) +backup/gd.o: backup/gd.h console/snes.h $(UCON64_STD_H) +backup/gbx.o: backup/gbx.h $(UCON64_STD_H) +backup/lynxit.o: backup/lynxit.h $(UCON64_STD_H) +backup/mccl.o: backup/mccl.h $(UCON64_STD_H) +backup/mcd.o: backup/mcd.h $(UCON64_STD_H) +backup/md-pro.o: backup/md-pro.h backup/tototek.h $(UCON64_STD_H) +backup/mgd.o: backup/mgd.h $(UCON64_STD_H) +backup/msg.o: backup/msg.h backup/ffe.h $(UCON64_STD_H) +backup/pce-pro.o: backup/pce-pro.h backup/tototek.h $(UCON64_STD_H) +backup/pl.o: backup/pl.h $(UCON64_STD_H) +backup/psxpblib.o: backup/psxpblib.h $(UCON64_STD_H) +backup/sflash.o: backup/sflash.h backup/tototek.h $(UCON64_STD_H) +backup/smc.o: backup/smc.h backup/ffe.h $(UCON64_STD_H) +backup/smd.o: backup/smd.h backup/ffe.h $(UCON64_STD_H) +backup/smsgg-pro.o: backup/smsgg-pro.h backup/tototek.h $(UCON64_STD_H) +backup/swc.o: backup/swc.h backup/ffe.h console/snes.h $(UCON64_STD_H) +backup/tototek.o: backup/tototek.h $(UCON64_STD_H) +backup/ufo.o: backup/ufo.h $(UCON64_STD_H) +backup/yoko.o: backup/yoko.h $(UCON64_STD_H) +backup/z64.o: backup/z64.h $(UCON64_STD_H) +patch/aps.o: patch/aps.h $(UCON64_STD_H) +patch/bsl.o: patch/bsl.h $(UCON64_STD_H) +patch/gg.o: patch/gg.h $(UCON64_STD_H) +patch/ips.o: patch/ips.h $(UCON64_STD_H) +patch/pal4u.o: patch/pal4u.h $(UCON64_STD_H) +patch/ppf.o: patch/ppf.h $(UCON64_STD_H) +patch/xps.o: patch/xps.h $(UCON64_STD_H) diff --git a/ucon64/2.0/src/Makefile.vc6 b/ucon64/2.0/src/Makefile.vc6 new file mode 100644 index 0000000..3c794c5 --- /dev/null +++ b/ucon64/2.0/src/Makefile.vc6 @@ -0,0 +1,223 @@ +DLOPEN=1 +#USE_ZLIB=1 +USE_DISCMAGE=1 +#USE_USB=1 +USE_LIBCD64=1 + +CC=cl.exe +CFLAGS=/nologo /I. /W3 /O2 /DHAVE_CONFIG_H +LDFLAGS=/NOLOGO setargv.obj +TARGETS= + +!ifdef USE_DISCMAGE +LIBNAME_DM=discmage +!endif + +!ifdef USE_DISCMAGE +FULLLIBNAME_DM=$(LIBNAME_DM).dll +!ifndef DLOPEN +LDFLAGS=$(LDFLAGS) libdiscmage/$(LIBNAME_DM).lib +!endif +!endif + +TARGETS=$(TARGETS) libdiscmage/$(FULLLIBNAME_DM) + + +!ifdef USE_LIBCD64 +LDFLAGS=$(LDFLAGS) backup/libcd64/cd64.lib +TARGETS=$(TARGETS) backup/libcd64/cd64.lib +!endif + + +OBJECTS=ucon64.obj ucon64_dat.obj ucon64_misc.obj ucon64_opts.obj \ + misc/chksum.obj misc/file.obj misc/getopt.obj misc/getopt2.obj \ + misc/misc.obj misc/parallel.obj misc/property.obj misc/string.obj \ + misc/dlopen.obj \ + patch/aps.obj patch/bsl.obj patch/gg.obj patch/ips.obj \ + patch/pal4u.obj patch/ppf.obj patch/xps.obj \ + console/dc.obj console/gb.obj console/gba.obj console/genesis.obj \ + console/jaguar.obj console/lynx.obj console/n64.obj \ + console/neogeo.obj console/nes.obj console/ngp.obj console/pce.obj \ + console/psx.obj console/sms.obj console/snes.obj console/swan.obj \ + backup/cd64.obj backup/cmc.obj backup/dex.obj backup/doctor64.obj \ + backup/doctor64jr.obj backup/f2a.obj backup/fal.obj backup/ffe.obj \ + backup/fig.obj backup/gbx.obj backup/gd.obj backup/interceptor.obj \ + backup/lynxit.obj backup/mccl.obj backup/mcd.obj backup/md-pro.obj \ + backup/mgd.obj backup/msg.obj backup/pce-pro.obj backup/pl.obj \ + backup/psxpblib.obj backup/sflash.obj backup/smc.obj backup/smd.obj \ + backup/smsgg-pro.obj backup/ssc.obj backup/swc.obj backup/tototek.obj \ + backup/ufo.obj backup/yoko.obj backup/z64.obj +!ifdef USE_ZLIB +LDFLAGS=$(LDFLAGS) zlib.lib +OBJECTS=$(OBJECTS) misc/map.obj misc/archive.obj misc/unzip.obj +!endif +!ifdef USE_USB +LDFLAGS=$(LDFLAGS) usb.lib +OBJECTS=$(OBJECTS) misc/usb.obj +!endif + + +TARGET=ucon64.exe +TARGETS=$(TARGETS) $(TARGET) + +all: $(TARGETS) + + +clean: + del *.obj + del patch\*.obj + del console\*.obj + del backup\*.obj + del misc\*.obj + del $(TARGET) + cd libdiscmage + $(MAKE) /NOLOGO /f Makefile.vc6 clean + cd ..\backup\libcd64 + $(MAKE) /NOLOGO /f Makefile.vc6 clean + cd ..\.. + + +distclean: clean + del config.h + cd libdiscmage + $(MAKE) /NOLOGO /f Makefile.vc6 distclean + cd ..\backup\libcd64 +# libcd64 Makefile has no distclean target + $(MAKE) /NOLOGO /f Makefile.vc6 clean + cd ..\.. + + +.c.obj: + $(CC) $(CFLAGS) /c $< /Fo$@ + +backup/cd64.obj: + $(CC) $(CFLAGS) /Ibackup/libcd64 /c $*.c /Fo$@ + + +!ifdef USE_DISCMAGE +libdiscmage/$(FULLLIBNAME_DM): + cd libdiscmage + $(MAKE) /NOLOGO /f Makefile.vc6 + cd .. +!endif + +!ifdef USE_LIBCD64 +backup/libcd64/cd64.lib: + cd backup/libcd64 + $(MAKE) /NOLOGO /f Makefile.vc6 + cd ../.. +!endif + + +$(TARGET): $(OBJECTS) + link.exe $(OBJECTS) $(LDFLAGS) /OUT:$@ + + +install: + cd libdiscmage + $(MAKE) /NOLOGO /f Makefile.vc6 install + cd .. + + +uninstall: + cd libdiscmage + $(MAKE) /NOLOGO /f Makefile.vc6 uninstall + cd .. + + +# Dependencies + +# Most source files include these +UCON64_STD_H=ucon64.h ucon64_misc.h misc/misc.h config.h ucon64_defines.h + +misc/archive.obj: misc/archive.h misc/map.h config.h +misc/chk.obj: misc/chksum.h config.h +misc/dlopen.obj: misc/dlopen.h misc/dxedll_pub.h config.h +misc/getopt.obj: misc/getopt.h +misc/map.obj: misc/map.h config.h +misc/misc.obj: misc/misc.h misc/archive.h config.h +misc/parallel.obj: misc/parallel.h config.h +misc/usb.obj: misc/usb.h config.h +misc/unzip.obj: misc/unzip.h config.h +ucon64.obj: misc/dlopen.h misc/getopt.h ucon64_dat.h ucon64_opts.h \ + console/dc.h console/gb.h console/gba.h console/genesis.h \ + console/jaguar.h console/lynx.h console/n64.h console/neogeo.h \ + console/nes.h console/ngp.h console/pce.h console/psx.h \ + console/sms.h console/snes.h console/swan.h \ + backup/cd64.h backup/dex.h backup/doctor64.h backup/doctor64jr.h \ + backup/f2a.h backup/fal.h backup/ffe.h backup/fig.h backup/gbx.h \ + backup/gd.h backup/interceptor.h backup/lynxit.h backup/mccl.h \ + backup/mcd.h backup/md-pro.h backup/mgd.h backup/msg.h \ + backup/pce-pro.h backup/pl.h backup/smc.h backup/smd.h \ + backup/smsgg-pro.h backup/ssc.h backup/swc.h backup/tototek.h \ + backup/ufo.h backup/yoko.h backup/z64.h \ + patch/aps.h patch/bsl.h patch/gg.h patch/ips.h patch/pal4u.h \ + patch/ppf.h patch/xps.h $(UCON64_STD_H) +ucon64_dat.obj: ucon64_dat.h $(UCON64_STD_H) +ucon64_misc.obj: misc/dlopen.h $(UCON64_STD_H) +ucon64_opts.obj: misc/dlopen.h misc/getopt.h ucon64_dat.h ucon64_opts.h \ + console/dc.h console/gb.h console/gba.h console/genesis.h \ + console/jaguar.h console/lynx.h console/n64.h \ + console/neogeo.h console/nes.h console/ngp.h console/pce.h \ + console/psx.h console/sms.h console/snes.h console/swan.h \ + backup/cd64.h backup/dex.h backup/doctor64.h \ + backup/doctor64jr.h backup/f2a.h backup/fal.h backup/ffe.h \ + backup/fig.h backup/gbx.h backup/gd.h backup/interceptor.h \ + backup/lynxit.h backup/mccl.h backup/mcd.h backup/md-pro.h \ + backup/mgd.h backup/msg.h backup/pce-pro.h backup/pl.h \ + backup/smc.h backup/smd.h backup/smsgg-pro.h backup/ssc.h \ + backup/swc.h backup/tototek.h backup/ufo.h backup/yoko.h \ + backup/z64.h \ + patch/aps.h patch/bsl.h patch/gg.h patch/ips.h patch/pal4u.h \ + patch/ppf.h patch/xps.h $(UCON64_STD_H) +console/dc.obj: console/dc.h $(UCON64_STD_H) +console/gb.obj: console/gb.h backup/mgd.h $(UCON64_STD_H) +console/gba.obj: console/gba.h $(UCON64_STD_H) +console/genesis.obj: console/genesis.h backup/smd.h backup/mgd.h $(UCON64_STD_H) +console/jaguar.obj: console/jaguar.h $(UCON64_STD_H) +console/lynx.obj: console/lynx.h $(UCON64_STD_H) +console/n64.obj: console/n64.h $(UCON64_STD_H) +console/neogeo.obj: console/neogeo.h $(UCON64_STD_H) +console/nes.obj: console/nes.h $(UCON64_STD_H) +console/ngp.obj: console/ngp.h $(UCON64_STD_H) +console/pce.obj: console/pce.h backup/mgd.h $(UCON64_STD_H) +console/psx.obj: console/psx.h $(UCON64_STD_H) +console/sms.obj: console/sms.h backup/smd.h backup/mgd.h $(UCON64_STD_H) +console/snes.obj: console/snes.h backup/mgd.h $(UCON64_STD_H) +console/swan.obj: console/swan.h $(UCON64_STD_H) +backup/cd64.obj: backup/cd64.h $(UCON64_STD_H) +backup/cmc.obj: backup/cmc.h $(UCON64_STD_H) +backup/dex.obj: backup/dex.h backup/psxpblib.h $(UCON64_STD_H) +backup/doctor64.obj: backup/doctor64.h $(UCON64_STD_H) +backup/doctor64jr.obj: backup/doctor64jr.h $(UCON64_STD_H) +backup/f2a.obj: backup/f2a.h $(UCON64_STD_H) +backup/fal.obj: backup/fal.h backup/cartlib.c $(UCON64_STD_H) +backup/ffe.obj: backup/ffe.h $(UCON64_STD_H) +backup/fig.obj: backup/fig.h console/snes.h $(UCON64_STD_H) +backup/gd.obj: backup/gd.h console/snes.h $(UCON64_STD_H) +backup/gbx.obj: backup/gbx.h $(UCON64_STD_H) +backup/lynxit.obj: backup/lynxit.h $(UCON64_STD_H) +backup/mcd.obj: backup/mcd.h $(UCON64_STD_H) +backup/mccl.obj: backup/mccl.h $(UCON64_STD_H) +backup/md-pro.obj: backup/md-pro.h backup/tototek.h $(UCON64_STD_H) +backup/mgd.obj: backup/mgd.h $(UCON64_STD_H) +backup/msg.obj: backup/msg.h backup/ffe.h $(UCON64_STD_H) +backup/pce-pro.obj: backup/pce-pro.h backup/tototek.h $(UCON64_STD_H) +backup/pl.obj: backup/pl.h $(UCON64_STD_H) +backup/psxpblib.obj: backup/psxpblib.h $(UCON64_STD_H) +backup/sflash.obj: backup/sflash.h backup/tototek.h $(UCON64_STD_H) +backup/smc.obj: backup/smc.h backup/ffe.h $(UCON64_STD_H) +backup/smd.obj: backup/smd.h backup/ffe.h $(UCON64_STD_H) +backup/smsgg-pro.obj: backup/smsgg-pro.h backup/tototek.h $(UCON64_STD_H) +backup/swc.obj: backup/swc.h backup/ffe.h console/snes.h $(UCON64_STD_H) +backup/tototek.obj: backup/tototek.h $(UCON64_STD_H) +backup/ufo.obj: backup/ufo.h $(UCON64_STD_H) +backup/yoko.obj: backup/yoko.h $(UCON64_STD_H) +backup/z64.obj: backup/z64.h $(UCON64_STD_H) +patch/aps.obj: patch/aps.h $(UCON64_STD_H) +patch/bsl.obj: patch/bsl.h $(UCON64_STD_H) +patch/gg.obj: patch/gg.h $(UCON64_STD_H) +patch/ips.obj: patch/ips.h $(UCON64_STD_H) +patch/pal4u.obj: patch/pal4u.h $(UCON64_STD_H) +patch/ppf.obj: patch/ppf.h $(UCON64_STD_H) +patch/xps.obj: patch/xps.h $(UCON64_STD_H) diff --git a/ucon64/2.0/src/backup/NTSC-PAL notes.txt b/ucon64/2.0/src/backup/NTSC-PAL notes.txt new file mode 100644 index 0000000..a1e5521 --- /dev/null +++ b/ucon64/2.0/src/backup/NTSC-PAL notes.txt @@ -0,0 +1,118 @@ +Legend for Code1: +0 = seems to work without modification +1 = needs crack (uCON64 option -k) +2 = needs NTSC/PAL fix (uCON64 option -f) +3 = needs backup unit header fix +4 = seems to work, but doesn't work correctly +5 = music doesn't work +6 = copy protection screen +7 = wrong television standard (NTSC/PAL) screen +8 = corrupted/distorted graphics +9 = doesn't work + + +Legend for Code2: +N1 = 3f 21 29/89 10 f0 3f 21 29/89 10 80 +N2 = ad 3f 21 29 10 d0 ad 3f 21 29 10 ea ea +P3 = ad 3f 21 89 10 d0 ad 3f 21 89 10 80 - Terranigma +N4 = ad 3f 21 89 10 d0 ad 3f 21 89 10 80/(ea ea) - Live A Live +N5 = 3f 21 29/89 10 00 f0 3f 21 29/89 10 00 80 - Clock Tower +N6 = 3f 21 29/89 10 00 d0 3f 21 29/89 10 00 ea ea - Mario no Super Picross +P7 = ad 3f 21 29 10 00 d0 ad 3f 21 29 10 00 80 +P8 = ad 3f 21 89 10 00 d0 a9 10 00 89 10 00 d0 - Eric Cantona Football ? +N9 = 3f 21 89 10 c2 XX f0 3f 21 89 10 c2 XX 80 - Front Mission - Gun Hazard +N10 = 3f 21 89 10 c2 XX d0 3f 21 89 10 c2 XX ea ea - Robotrek +N11 = 3f 21 29/89 10 c9 10 f0 3f 21 29/89 10 c9 10 80 +N12 = ad 3f 21 29 10 c9 00 f0 ad 3f 21 29 10 c9 00 80/(ea ea) <= original uCON used 80 +N13 = ad 3f 21 29 10 c9 00 d0 ad 3f 21 29 10 c9 00 80 +N14 = ad 3f 21 29 10 c9 10 d0 ad 3f 21 29 10 c9 10 ea ea +P15 = ad 3f 21 29 10 cf bd ff XX f0 ad 3f 21 29 10 cf bd ff XX 80 - Pop'n Twinbee E +N16 = 3f 21 29 10 cf XX YY 80 f0 3f 21 29 10 cf XX YY 80 80 - Gokujyou Parodius/Tokimeki Memorial +N17 = ad 3f 21 8d XX YY 29 10 8d ad 3f 21 8d XX YY 29 00 8d - Dragon Ball Z - Super Butoden 2 ? +N18 = 3f 21 00 29/89 10 f0 3f 21 00 29/89 10 80 - Kirby's Dream Course U +P19 = af 3f 21 00 29 10 d0 af 3f 21 00 29 10 80 +N20 = af 3f 21 00 29/89 10 d0 af 3f 21 00 29/89 10 ea ea - Kirby no Kirakira Kids/Final Fight Guy +P21 = af 3f 21 00 29 10 00 d0 af 3f 21 00 29 10 00 ea ea +N22 = af 3f 21 00 29/89 10 00 f0 af 3f 21 00 29/89 10 00 80 +P23 = af 3f 21 00 29 XX c9 XX f0 af 3f 21 00 29 XX c9 XX 80 - Secret of Mana E +N24 = af 3f 21 00 29 XX c9 XX f0 af 3f 21 00 29 XX c9 XX 80 - Seiken Densetsu 3 +N25 = af 3f 21 00 29 10 80 2d 00 1b af 3f 21 00 29 00 80 2d 00 1b - Seiken Densetsu 2/Secret of Mana U +N26 = 3f 21 00 89 10 c2 XX f0 3f 21 00 89 10 c2 XX 80 - Dragon - The Bruce Lee Story U +N27 = af 3f 21 00 XX YY 29 10 00 d0 af 3f 21 00 XX YY 29 10 00 ea ea - Fatal Fury Special ? +N28 = 3f 21 c2 XX 29 10 00 f0 3f 21 c2 XX 29 10 00 80 - Metal Warriors +N29 = 3f 21 c2 XX 29 10 00 d0 3f 21 c2 XX 29 10 00 ea ea - Dual Orb II +N30 = af 3f 21 ea 89 10 00 d0 a9 00 00 ea 89 10 00 d0 - Super Famista 3 ? +P31 = a2 18 01 bd 27 20 89 10 00 f0 01 a2 18 01 bd 27 20 89 10 00 ea ea - Donkey Kong Country E +N32 = a2 18 01 bd 27 20 89 10 00 d0 01 a2 18 01 bd 27 20 89 10 00 ea ea - Donkey Kong Country U +N33 = 29 10 00 a2 00 00 c9 10 00 d0 29 10 00 a2 00 00 c9 10 00 80 - Wolfenstein 3D U + + +Comment: +Code1 is valid for an SWC 2.8cc 32 Mbit PAL. For example Terranigma has a Code1 +of 0, but a Code2 of P3, because it runs without modification on a PAL SNES but +needs an NTSC/PAL fix for an NTSC SNES. +The prefix N indicates an NTSC protection code, the prefix P indicates a PAL +protection code. Cracking or fixing an NTSC protection code makes a game run +on a PAL SNES and vice versa. + + +Code1 Code2 Game +2 N5 Clock Tower (J) <= 0x29 +1 + 2 N1 Demon's Crest (U) <= (0xad) 0x29 +1 + 2 + 4 N6 Donkey Kong Country 2 - Diddy's Kong Quest (U) (V1.1) <= can't get past the first level; 0x29 +1 P31 Donkey Kong Country (E) (V1.0) +1 P31 Donkey Kong Country (E) (V1.1) +1 + 2 N32 Donkey Kong Country (U) (V1.0) +1 + 2 N32 Donkey Kong Country (U) (V1.1) +2 N26 Dragon - The Bruce Lee Story (U) +2 N4 Dr. Mario (J) (NP) <= 0xea 0xea +2 N29 Dual Orb II (J) +1 + 2 N18 Earthbound (U) <= 0x29 +2 + 3 N1 Final Fight 3 (U) <= emulation mode select byte: 0x1c; (0xad) 0x29 +2 N20 Final Fight Guy (U) <= 0x89 +2 + 3 N1 Final Fight Tough (J) <= emulation mode select byte: 0x1c; (0xad) 0x29 +1 + 2 N9 Front Mission - Gun Hazard (J) <= modification protection; code 0 for NTSC SNES; 0x20 +2 N16 Gokujou Parodius (J) <= 0xad 0xff +2 N1 Illusion of Gaia (U) <= (0xad) 0x89 +2 + 9 N16 Jikkyou Oshaberi Parodius (J) <= 0xad 0xff +2 N1 Joe & Mac 2 - Lost in the Tropics (U) (35468) <= (0xad) 0x29 +2 N1 Joe & Mac 2 - Lost in the Tropics (U) (54227) <= (0xad) 0x29 +2 + 9 N5 Kaite Tukutte Asoberu Dezaemon (J) <= 0x89 +2 N20 Kirby no Kirakira Kids (J) (NP) <= 0x29 +1 + 2 N18 Kirby's Dream Course (U) <= 0x29 +2 N4 Live A Live (J) <= 0xea 0xea +2 N28 Magical Pop'n (J) +1 + 2 N6 Mario no Super Picross (J) <= 0x89 +2 N1 Mega Man's Soccer (U) <= (0xad) 0x89 +2 N1 Mega Man VII (U) <= (0xad) 0x29 +2 + 9 N1 Mega Man X 2 (U) <= the intro can be viewed; (0xad) 0x89 +1 + 2 N1 Mega Man X (U) (V1.0) <= (0xad) 0x89 +1 + 2 N1 Mega Man X (U) (V1.1) <= (0xad) 0x89 +2 N28 Metal Warriors (U) +2 + 3 N1 + N2 Mickey to Donald Magical Adventure 3 (J) <= emulation mode select byte: 0x1c; (0xad) 0x29 +2 N2 Ninja Gaiden Trilogy (U) +0 P15 Pop'n Twinbee (E) <= 0x80 +0 P15 Pop'n Twinbee - Rainbow Bell Adventures (E) <= 0x00 +2 N10 Robotrek (U) <= 0x20 +2 N18 Romancing Sa-Ga 2 (J) <= 0x89 +2 N18 Romancing Sa-Ga 3 (J) (V1.0) <= 0x89 +2 N18 Romancing Sa-Ga 3 (J) (V1.1) <= 0x89 +2 N25 Secret of Mana (U) +0 P23 Secret of Mana (E) (V1.0) <= 0x10 0x10 +0 P23 Secret of Mana (E) (V1.1) <= 0x10 0x10 +2 N25 Seiken Densetsu 2 (J) +2 N24 Seiken Densetsu 3 (J) <= 0x10 0x00 +2 N1 Street Fighter II Turbo (U) <= (0xad) 0x29 +1 + 2 N1 Super Mario All-Stars (U) <= (0xad) 0x89 +1 + 2 N1 Super Mario All-Stars & World (U) <= (0xad) 0x89 +1 + 2 N1 Super Mario Collection (J) (V1.1) <= (0xad) 0x89 +1 + 2 N1 Super Metroid (JU) <= (0xad) 0x89 +0 P3 Terranigma (E) +1 P3 Tetris Attack (E) +1 + 2 N1 Tetris Attack (U) <= (0xad) 0x89 +2 + 8 + 9 N16 Tokimeki Memorial - Densetsu no Ki no Shita de (J) (V1.1) <= the game halts after a while; 0xad 0xff +2 N33 Wolfenstein 3D (U) +2 N1 Yoshi's Safari (U) <= I don't have a Super Scope...; (0xad) 0x29 +2 N2 Ys V - Expert (J) + +EOF diff --git a/ucon64/2.0/src/backup/SWC-compatibility.txt b/ucon64/2.0/src/backup/SWC-compatibility.txt new file mode 100644 index 0000000..5662973 --- /dev/null +++ b/ucon64/2.0/src/backup/SWC-compatibility.txt @@ -0,0 +1,419 @@ +A Super Wild Card compatibility list +Version: 1.27 +Author: dbjh "of uCON64" with great help from CL "of NSRT" and The Dumper +Date: 30 November 2003 +Hardware: Super Wild Card 2.8cc 32 Mbit PAL +Software: uCON64 1.9.8-3 + + +Legend: +0 = seems to work without modification +1 = needs crack (uCON64 option -k) +2 = needs NTSC/PAL fix (uCON64 option -f) +3 = needs backup unit header fix +4 = seems to work, but doesn't work correctly +5 = music doesn't work +6 = copy protection screen +7 = wrong television standard (NTSC/PAL) screen +8 = corrupted/distorted graphics +9 = doesn't work + + +Comment: +I use the word seems, because I have tested most games only for a few minutes +or so. Even for games that I played from the beginning till the end I can't be +sure if they run as they do from a cartridge. +Of course I tried to crack or fix the games that displayed a copy protection +screen. The games that have that code can't be cracked with uCON64. +Non-playing music seems to be a BS-specific problem. +For some games I put an extra comment after the character sequence "<=". In +some comments I mention the so-called emulation mode select byte. This is the +byte at offset 2 in the header. You can check the value of the emulation mode +select byte by passing the command line option -dbuh to uCON64. You can change +it with a hex(adecimal) editor or by using the uCON64 option -poke (new in +version 1.9.8beta8). For example, to change the emulation mode select byte for +Mickey to Donald Magical Adventure 3 (J).swc you would type: + ucon64 -poke=2:1c "Mickey to Donald Magical Adventure 3 (J).swc" +I have sent all games to the Super Wild Card via a parallel cable. Note that +for LoROM games that don't use SRAM uCON64 adjusts the emulation mode select +byte before sending the game. This causes that some games don't have code 3, +because uCON64 already makes the required modification. For example, if Addams +Family Values is loaded from diskette the emulation mode select byte should +have the value 0x2c instead of the value 0x0c (before the file is split). +For games marked with an asterisk uCON64 1.9.8-3 is required (or an updated +version of snescopy.txt and/or snesntsc.txt/snespal.txt). + + +Code Game +0 - 7th Saga, The (U) +0 - AAAHH!!! Real Monsters (U) +8 + 9 - Ace wo Nerae! (J) <= it does work with Super Mario Kart (E) plugged in +0 - ActRaiser 2 (E) +4 - ActRaiser 2 (U) <= level(s?) (Industen) can't be entered +0 - ActRaiser (U) +0 - Addams Family, The (E) +0 - Addams Family, The - Pugsley's Scavenger Hunt (Beta) +0 - Addams Family, The - Pugsley's Scavenger Hunt (E) +0 - Addams Family Values (E) +0 - Adventures of Batman & Robin, The (E) +0 - Aladdin (E) +0 - Alcahest (J) +0 - Alfred Chicken (E) (4285) +0 - Alfred Chicken (E) (6937) +0 - Alfred Chicken (U) +0 - Alien 3 (E) +0 - Alien vs. Predator (U) +0 - Another World (E) +0 - Arabian Nights - Sabaku no Seirei Ou (J) +0 - Arcade's Greatest Hits (E) +0 - Axelay (E) +0 - Bahamut Lagoon (J) +0 - Bastard!! Ankoku no Hakai-shin (J) +0 - Batman Returns (E) +0 - Batman Returns (J) +3 - Batman - Revenge of the Joker (U) <= emulation mode select byte: 0x0c +0 - Battletoads & Double Dragon - The Ultimate Team (U) +0 - Battletoads in Battlemaniacs (U) (26454) +0 - Beauty and the Beast (E) +0 - Bishoujo Senshi Sailor Moon (J) +0 - Brawl Brothers (U) +1 - Breath of Fire II (E) +0 - Breath of Fire (U) +9 - BS Bandai Satellaview-X BIOS +0 - BS Chrono Trigger - Character Library (J) +0 - BS Chrono Trigger - Jet Bike Special (J) +0 - BS Chrono Trigger - Music Library (J) +8 - BS Dan Dan Belt Conveyor Ukulele no Maki (J) (NG-Dump Known) +0 - BS Dr. Mario (J) +0 - BS Dynami Tracer (J) +0 - BS F-Zero Grand Prix 2 (J) +0 - BS Koi ha Balance - Battle of Lovers (J) +5 - BS Mario Collection 3 (J) +8 - BS Pokekame Magajin (J) +9 - BS Radical Dreamers (J) [f1] <= with the translation patch it does work +9 - BS Radical Dreamers (J) <= idem +0 - BS Super Earth Defense Force (J) +0 - BS Super Famicom Wars (J) [h1] +0 - BS Super Famicom Wars (J) +5 - BS Super Mario USA 4 (J) +8 - BS Treasure Conflix (J) +0 - BS Wai Wai Check 3-7th (J) +0 - BS Wario no Mori (J) +0 - BS Yoshi no Panepon (J) +0 - BS Zelda no Densetsu - Kamigami no Triforce (J) +9 - BS Zelda no Densetsu Kodai no Sekiban Dai 1 Hanashi (J) +9 - BS Zelda no Densetsu Kodai no Sekiban Dai 3 Hanashi (J) +1 - BS Zelda no Densetsu Remix (J) +0 - BS Zootto Mahjong! Event Version (J) +0 - Bubsy II (U) +0 - Bubsy in Claws Encounters of the Furred Kind (E) +0 - Castlevania - Vampire's Kiss (E) +0 - Choh Makai-Mura (J) +0 - Choplifter III (U) (30545) +0 - Chou Jikuu Yousai Macross - Scrambled Valkyrie (J) +0 - Chrono Trigger - Kurono Toriga (J) +0 - Chrono Trigger (U) +0 - Chuck Rock (U) +0 - Clay Fighter 2 - Judgment Clay (E) +0 - Clay Fighter (E) +7 - Clay Fighter (U) +2 - Clock Tower (J) +0 - Contra III - The Alien Wars (U) +0 - Cybernator (U) +0 - Daffy Duck - The Marvin Missions (U) +7 - Dai-3-Ji Super Robot Taisen (J) +9 - Daikaijuu Monogatari 2 (J) <= works on an SWC DX2 64 Mbit PAL +0 - Darius Twin (E) +0 - Darius Twin (U) +1 + 2 - Demon's Crest (U) +0 - Der Langrisser (J) (V1.1) +0 - Desert Strike - Return to the Gulf (U) +9 - Dirt Trax FX (E) +0 - Donald Duck Mahou no Boushi (J) +1 - Donkey Kong Country 2 - Diddy's Kong Quest (E) +1+2+4 - Donkey Kong Country 2 - Diddy's Kong Quest (U) (V1.1) <= can't get past the first level +1 - Donkey Kong Country 3 - Dixie Kong's Double Trouble (E) +0 - Donkey Kong Country - Competition Cartridge (U) +1 - Donkey Kong Country (E) (V1.0) +1 - Donkey Kong Country (E) (V1.1) +1 + 2 - Donkey Kong Country (U) (V1.0) +1 + 2 - Donkey Kong Country (U) (V1.1) +9 - Doom (U) +0 - Doraemon - Nobita to Yosei no Kuni (J) +0 - Dragon Quest I & II (J) +0 - Dragon Quest III (J) +0 - Dragon Quest VI (J) +0 - Dragon Quest V (J) +0 - Dragon's Lair (Beta) +0 - Dragon's Lair (E) +0 - Dragon - The Bruce Lee Story (E) [a1] +2 - Dragon - The Bruce Lee Story (U) +2 - Dr. Mario (J) (NP) +2 - Dual Orb II (J) +1 + 2 - Earthbound (U) +0 - Earthworm Jim 2 (U) +0 - Earthworm Jim (U) +0 - Eek! The Cat (E) +0 - E.V.O. Search For Eden (U) +0 - F-1 Grand Prix (J) +8 + 9 - F1 ROC II - Race of Champions (U) <= course is not visible +0 - Final Fantasy III (U) (V1.0) +0 - Final Fantasy III (U) (V1.1) +0 - Final Fantasy II (U) (V1.0) +0 - Final Fantasy IV (J) +0 - Final Fantasy - Mystic Quest (U) (V1.0) +0 - Final Fantasy VI (J) +0 - Final Fantasy V (J) +0 - Final Fight 2 (E) +2 + 3 - Final Fight 3 (U) <= emulation mode select byte: 0x1c +2 - Final Fight Guy (U) +2 + 3 - Final Fight Tough (J) <= emulation mode select byte: 0x1c +0 - Final Fight (U) +0 - Flashback (E) (M2) +0 - Frogger (U) +1*+2* - Front Mission - Gun Hazard (J) <= modification protection; code 0 for NTSC SNES +0 - Front Mission (J) (V1.0) +0 - F-Zero (E) +0 - Ganbare Goemon - Yuki hime Kyuushuutsu emaki (J) (V1.2) +0 - Ganpuru - Gunman's Proof (J) +2 - Gokujou Parodius (J) +0 - Goof Troop (E) +0 - Gradius III (U) +0 - Harvest Moon (U) +9 - Hayazashi Nidan Morita Shogi 2 (J) <= halts on "TRANSMIT WAIT" +0 - Hebereke's Popoitto (E) +9 - Hoshi no Kirby 3 (J) +2 - Illusion of Gaia (U) +0 - Illusion of Time (E) +0 - International Superstar Soccer Deluxe (E) +0 - International Superstar Soccer (E) +0 - Itchy & Scratchy Game, The (E) <= was 8 + 9!? +2 + 9 Jikkyou Oshaberi Parodius (J) +2 - Joe & Mac 2 - Lost in the Tropics (U) (35468) +2 - Joe & Mac 2 - Lost in the Tropics (U) (54227) +0 - Joe & Mac - Caveman Ninja (E) +0 - Joe & Mac (U) +0 - J.R.R. Tolkien's The Lord of the Rings - Volume 1 (U) +0 - Juutei Senki (J) +2 + 9 - Kaite Tukutte Asoberu Dezaemon (J) +0 - Kiki KaiKai - Nazo no Kuro Manto (J) +1 - Killer Instinct (E) +2 - Kirby no Kirakira Kids (J) (NP) +1 - Kirby's Dream Course (E) +1 + 2 - Kirby's Dream Course (U) +9 - Kirby's Dream Land 3 (U) +9 - Kirby's Fun Pak (E) +0 - Kirby's Ghost Trap (E) +0 - Krusty's Super Fun House (U) (V1.1) +0 - Legend of The Mystical Ninja, The (E) +0 - Legend of Zelda, The - A Link to the Past (E) +0 - Legend of Zelda, The - A Link to the Past (U) +0 - Lemmings (E) +2 - Live A Live (J) +0 - Lost Vikings II, The (E) +0 - Lost Vikings, The (U) +1 - Lufia II - Rise of the Sinistrals (H) +1 - Lufia II - Rise of the Sinistrals (U) +0 - Lufia & The Fortress of Doom (U) +0 - Magical Drop (J) +2 - Magical Pop'n (J) +0 - Magical Quest Starring Mickey Mouse, The (Beta) +0 - Magical Quest Starring Mickey Mouse, The (E) +0 - Magical Quest Starring Mickey Mouse, The (U) +1 + 2 - Mario no Super Picross (J) +0 - Mario Paint (E) <= I don't have a SNES mouse... +0 - Mario & Wario (J) <= idem +9 - Masou Kishin - Super Robot Taisen Gaiden - The Lord of Elemental (J) +2 - Mega Man's Soccer (U) +2 - Mega Man VII (U) +9 - Mega Man X 2 (E) <= the intro can be viewed +2 + 9 - Mega Man X 2 (U) <= idem +8 + 9 - Mega Man X 3 (U) <= game can be started, but sprites are not visible +1 - Mega Man X (E) +1 + 2 - Mega Man X (U) (V1.0) +1 + 2 - Mega Man X (U) (V1.1) +8 + 9 - Metal Combat - Falcon's Revenge (U) <= sprites are not visible +2 - Metal Warriors (U) +2 + 3 - Mickey to Donald Magical Adventure 3 (J) <= emulation mode select byte: 0x1c +0 - Micro Machines 2 - Turbo Tournament (E) +0 - Micro Machines (U) +9 - Momotarou Dentetsu Happy (J) <= halts on "SPC7110 check program v3.0" +0 - Mortal Kombat 3 (E) +0 - Mortal Kombat (Beta) +0 - Mortal Kombat (E) +0 - Mortal Kombat II (E) (V1.0) +0 - Mortal Kombat II (U) (V1.1) +0 - NBA Jam (Beta) +0 - NBA Jam (E) (V1.0) +2 - Ninja Gaiden Trilogy (U) +0 - Ogre Battle - The March of the Black Queen (U) +0 - Out of This World (U) +0 - Parodius Da! Shinwa kara Owarai he (J) +0 - Parodius - Non-Sense Fantasy (E) +0 - Phalanx - The Enforce Fighter A-144 (E) +9 - Pilotwings (E) <= black screen as soon as the real flying begins +9 - Pilotwings (U) <= with the DSP patch it does work with Super Mario Kart (E) +0 - Pitfall - The Mayan Adventure (Beta) +0 - Pitfall - The Mayan Adventure (E) +0 - Pocky & Rocky 2 (U) (54250) +0 - Pocky & Rocky (E) +0 - Pop'n Twinbee (E) +0 - Pop'n Twinbee - Rainbow Bell Adventures (E) +0 - Primal Rage (E) +0 - Primal Rage (U) (With Sound Test) +0 - Prince of Persia 2 - The Shadow & The Flame (E) +0 - Prince of Persia (E) +0 - Prince of Persia (J) +0 - Push-Over (E) +0 - Puzzle Bobble (E) +0 - Ranma Nibun no Ichi - Akanekodan no Hihou (J) +0 - Ranma Nibun no Ichi - Hard Battle (U) +2 - Robotrek (U) +0 - Rockman & Forte (J) +0 - Rock N' Roll Racing (E) +0 - Romance of the Three Kingdoms IV - Wall of Fire (U) +2 - Romancing Sa-Ga 2 (J) +2 - Romancing Sa-Ga 3 (J) (V1.0) +2 - Romancing Sa-Ga 3 (J) (V1.1) +0 - Romancing Sa-Ga (J) (V1.0) +0 - RPG Tsukuru 2 (J) +0 - R-Type III - The Third Lightning (E) (21451) +0 - Rudra no Hihou (J) +0 - Sanrio World Smash Ball! (J) +0 - Secret of Evermore (U) +0 - Secret of Mana (E) (V1.0) +0 - Secret of Mana (E) (V1.1) +2 - Secret of Mana (U) +2 - Seiken Densetsu 2 (J) +0 - Seiken Densetsu 3 (J) (Sample) +2 - Seiken Densetsu 3 (J) +0 - Shadowrun (E) +0 - Shin Megami Tensei (J) (V1.0) +0 - Sim Ant (U) (37113) +0 - Sim City (E) +0 - Sim City (U) +0 - Simpsons, The - Bart's Nightmare (U) +0 - Smash Tennis (E) +0 - Smurfs, The (E) +0 - Soldiers of Fortune (U) +0 - Sonic the Hedgehog (Unl) [p1][h1] +0 - Soul Blazer (U) +0 - Spider-Man and the X-Men in Arcade's Revenge (E) +9 - Star Fox 2 (Beta) +9 - Star Fox 2 (Beta TD) +9 - Star Fox Super Weekend Competition (U) +9 - Star Fox (U) (V1.0) +9 - Star Fox (U) (V1.2) +9 - Star Ocean (J) +9 - StarWing (E) (V1.0) +9 - StarWing (E) (V1.1) +9 - Street Fighter Alpha 2 (E) [b1] +9 - Street Fighter Alpha 2 (E) +9 - Street Fighter Alpha 2 (U) +0 - Street Fighter II Champ. Edition (Hack) +0 - Street Fighter II - The World Warrior (E) +0 - Street Fighter II - The World Warrior (U) +0 - Street Fighter II Turbo (E) (V1.1) +2 - Street Fighter II Turbo (U) +9 - Street Fighter Zero 2 (J) +0 - Street Racer (Beta) +0 - Street Racer (E) +0 - Strike Gunner (E) +9 - Stunt Race FX (E) +0 - Sunset Riders (E) +0 - Super Adventure Island II (U) +0 - Super Adventure Island (U) +0 - Super Aleste (E) +0 - Super Aleste (J) +0 - Super Aleste (J) [t1] +0 - Super Battletank - War in the Gulf (U) (V1.0) +0 - Super Bomberman 2 (E) +0 - Super Bomberman 3 (E) +0 - Super Bomberman 4 (J) +0 - Super Bomberman 5 (J) +0 - Super Bomberman (E) +0 - Super Castlevania IV (E) +0 - Super Earth Defense Force (E) +0 - Super Famicom Wars (J) (NP) +0 - Super Ghouls 'N Ghosts (E) +1 - Super Mario All-Stars (E) +1 + 2 - Super Mario All-Stars (U) +1 - Super Mario All-Stars & World (E) +1 + 2 - Super Mario All-Stars & World (U) +1 + 2 - Super Mario Collection (J) (V1.1) +9 - Super Mario Kart (E) <= it does work with Super Mario Kart (E) plugged in +9 - Super Mario Kart (U) <= idem +9 - Super Mario RPG (J) (V1.0) +9 - Super Mario RPG (J) (V1.1) (NG-Dump Known) +9 - Super Mario RPG - Legend of the Seven Stars (U) +9 - Super Mario World 2 - Yoshi's Island (E) (V1.0) +0 - Super Mario World (E) (V1.1) +0 - Super Mario World (U) +1 - Super Metroid (E) +1 + 2 - Super Metroid (JU) +0 - Super NES Super Scope 6 (U) <= I don't have a Super Scope... +0 - Super Off Road (U) +0 - Super Power League 3 (J) +9 - Super Power League 4 (J) <= halts on "SPC7110 check program v3.0" +0 - Super Probotector - The Alien Rebels (E) +0 - Super Punch-Out!! (U) +0 - Super Puyo Puyo (J) (V1.2) +0 - Super R-Type (E) +0 - Super R-Type (U) +0 - Super Smash T.V. (E) +0 - Super Smash T.V. (U) +0 - Super Star Wars (E) +0 - Super Star Wars - Return of the Jedi (E) +0 - Super Star Wars - The Empire Strikes Back (Beta) +0 - Super Star Wars - The Empire Strikes Back (E) +0 - Super Star Wars - The Empire Strikes Back (U) (V1.1) +0 - Super Star Wars (U) (31438) +0 - Super Street Fighter II - The New Challengers (E) +0 - Super Tennis (E) (V1.0) +0 - Super Turrican (E) +0 - Syndicate (E) +0 - T2 - The Arcade Game (U) +0 - Tactics Ogre - Let Us Cling Together (J) (V1.2) +9 - Tales of Phantasia (J) <= works on an SWC DX2 64 Mbit PAL +0 - Teenage Mutant Hero Turtles IV - Turtles in Time (E) +0 - Teenage Mutant Hero Turtles - Tournament Fighters (E) +0 - Teenage Mutant Ninja Turtles IV - Turtles in Time (A) +0 - Teenage Mutant Ninja Turtles IV - Turtles in Time (Beta) +0 - Teenage Mutant Ninja Turtles - Turtles in Time (J) +0 - Tenchi Muyou! Game Hen (J) +9 - Tengai Makyou Zero (J) +9 - Tengai Makyou Zero - Shounen Jump no Sho (J) +0 - Terranigma (E) +0 - Terranigma (S) +0 - Test Drive II - The Duel (U) (20429) +1 - Tetris Attack (E) +1 + 2 - Tetris Attack (U) +0 - Theme Park (E) +0 - Thoroughbred Breeder III (J) +0 - Tiny Toon Adventures - Buster Busts Loose! (E) +2+8+9 - Tokimeki Memorial - Densetsu no Ki no Shita de (J) (V1.1) <= the game halts after a while +0 - Tom & Jerry (U) +0 - Top Racer (J) +0 - Treasure Hunter G (J) +0 - Ultimate Mortal Kombat 3 (U) +0 - Ultima VII - The Black Gate (Beta) +0 - Ultima VII - The Black Gate (U) +1 - Uniracers (U) +1 - Unirally (E) +0 - U.N. Squadron (E) +9 - Vortex (E) +0 - Wario's Woods (U) +0 - Wolfenstein 3D (E) +2 - Wolfenstein 3D (U) +0 - Wolverine - Adamantium Rage (U) +0 - Worms (E) +0 - WWF Super Wrestlemania (E) +9 - X-Band Modem BIOS (U) +0 - X-Men - Mutant Apocalypse (E) +2 - Yoshi's Safari (U) <= I don't have a Super Scope... +2 - Ys V - Expert (J) +2 - Yuu Yuu Hakusho - Tokubetsuhen (J) +0 - Zelda no Densetsu - Kamigami no Triforce (J) (V1.0) +0 - Zombies (E) + +EOF diff --git a/ucon64/2.0/src/backup/backup.h b/ucon64/2.0/src/backup/backup.h new file mode 100644 index 0000000..f997bab --- /dev/null +++ b/ucon64/2.0/src/backup/backup.h @@ -0,0 +1,62 @@ +/* +backup.h - single header file for all backup unit functions + +Copyright (c) 2003 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef BACKUP_H +#define BACKUP_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef USE_PARALLEL +#include "cd64.h" +#include "cmc.h" +#include "dex.h" +#include "doctor64.h" +#include "doctor64jr.h" +#include "fal.h" +//#include "ffe.h" +#include "fig.h" +#include "gbx.h" +#include "gd.h" +#include "interceptor.h" +#include "lynxit.h" +#include "mccl.h" +#include "mcd.h" +#include "md-pro.h" +#include "mgd.h" +#include "msg.h" +#include "pce-pro.h" +#include "pl.h" +//#include "psxpblib.h" +#include "sflash.h" +#include "smc.h" +#include "smd.h" +#include "smsgg-pro.h" +#include "ssc.h" +#include "swc.h" +#include "ufo.h" +#include "yoko.h" +#include "z64.h" +#endif // USE_PARALLEL +#if defined USE_PARALLEL || defined USE_USB +#include "f2a.h" +#endif + +#endif // BACKUP_H diff --git a/ucon64/2.0/src/backup/cartlib.c b/ucon64/2.0/src/backup/cartlib.c new file mode 100644 index 0000000..848ecc9 --- /dev/null +++ b/ucon64/2.0/src/backup/cartlib.c @@ -0,0 +1,768 @@ +/* +cartlib.c - Flash Linker Advance support for uCON64 + +Copyright (c) 2001 Jeff Frohwein +Copyright (c) 2002 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#ifdef USE_PARALLEL + +// *** GBA flash cart support routines in GCC *** +// This library allows programming FA/Visoly (both Turbo +// and non-Turbo) and official Nintendo flash carts. They +// can be used with the Flash Linker or can be called +// from GBA code to allow in-system flash cart programming. +// NOTE: ALL OF THESE ROUTINES MUST BE LOCATED IN GBA RAM +// IF THIS LIBRARY IS USED FOR IN-SYSTEM PROGRAMMING. +// +// by Jeff Frohwein, Started 2001-Aug-29 +// +// v1.0 - 2001-Sept-25 - Original release +// v1.1 - 2001-Nov-13 - Slightly modified SetVisolyBackupRWMode by removing >>1. +// +// Routines - +// +// void SetFAFlashRWMode (void) +// +// Used to enable modifications of FA/Visoly cart flash chip(s) +// and also used for CartTypeDetect routine. YOU MUST CALL THIS +// ROUTINE BEFORE USING ANY OF THE OTHER FA/VISOLY ROUTINES. +// +// u8 CartTypeDetect (void) +// Return a value indicating type of cart installed: +// 0x00 = Hudson Cart, 0x2e = Standard ROM +// 0xe2 = N Flash Cart, 0xff = Unknown +// 0x17 = FA 64M, 0x96 = Turbo FA 64M +// 0x18 = FA 128M, 0x97 = Turbo FA 128M +// +// u32 EraseNintendoFlashBlocks (u32 StartAddr, u32 BlockCount) +// +// Erase official Nintendo flash cart blocks. +// Ex: EraseNintendoFlashBlocks (0x8000000, 1); // erase block 1 +// EraseNintendoFlashBlocks (0x8010000, 2); // erase blocks 2 & 3 +// +// u32 EraseNonTurboFABlocks (u32 StartAddr, u32 BlockCount) +// +// Erase older (non-Turbo) Visoly flash cart blocks. +// Ex: EraseNonTurboFABlocks (0x8000000, 1); // erase block 1 +// EraseNonTurboFABlocks (0x8020000, 2); // erase blocks 2 & 3 +// +// u32 EraseTurboFABlocks (u32 StartAddr, u32 BlockCount) +// +// Erase newer (Turbo) Visoly flash cart blocks. +// Ex: EraseTurboFABlocks (0x8000000, 1); // erase block 1 +// EraseTurboFABlocks (0x8040000, 2); // erase blocks 2 & 3 +// +// u32 WriteNintendoFlashCart (u32 SrcAddr, u32 FlashAddr, u32 Length) +// +// Write 2 x Length bytes to official Nintendo flash cart. +// Ex: WriteNintendoFlashCart (SrcAddr, 0x8000000, 2) // write 4 bytes +// +// u32 WriteNonTurboFACart (u32 SrcAddr, u32 FlashAddr, u32 Length) +// +// Write 32 x Length bytes to older (non-Turbo) Visoly flash cart. +// Ex: WriteNonTurboFACart (SrcAddr, 0x8000000, 2) // write 64 bytes +// +// u32 WriteTurboFACart (u32 SrcAddr, u32 FlashAddr, u32 Length) +// +// Write 64 x Length bytes to newer (Turbo) Visoly flash cart. +// Ex: WriteTurboFACart (SrcAddr, 0x8000000, 2) // write 128 bytes +// +// To reduce the library size and remove support for any +// of the following types, comment out one or more of +// the following lines using // +#define NONTURBO_FA_SUPPORT 1 // Visoly Non-Turbo flash carts +#define TURBO_FA_SUPPORT 1 // Visoly Turbo flash carts +#define NOA_FLASH_CART_SUPPORT 1 // Official Nintendo flash carts +//#define SET_CL_SECTION 1 // Enable setting code section for cartlib + +// +// +// + +#ifdef TURBO_FA_SUPPORT +#define COMMON_FA_SUPPORT 1 +#endif +#ifdef NONTURBO_FA_SUPPORT +#define COMMON_FA_SUPPORT 1 +#endif + +#ifdef FLINKER + // FLinker programming defines +#define _MEM_INC 1 +#define FP_TIMEOUT1 0x4000 +#define FP_TIMEOUT2 0x8000 +#define FP_TIMEOUT3 0x80000 +#define _CART_START 0 +#define FLINKER_SET l40226c () +#define READ_NTURBO_SR(a,b) WriteFlash (a, INTEL28F_READSR); \ + outpb (SPPCtrlPort, 0); \ + b = (PPReadWord() & 0xff) +#define READ_NTURBO_S(a) outpb (SPPCtrlPort, 0); \ + a = (PPReadWord() & 0xff) +#define READ_TURBO_SR(a) WriteFlash (0, INTEL28F_READSR); \ + PPWriteWord (INTEL28F_READSR); \ + outpb (SPPCtrlPort, 0); \ + a = PPReadWord() & 0xff; \ + a += (PPReadWord() & 0xff) << 8 +#define READ_TURBO_S(a) outpb (SPPCtrlPort, 0); \ + a = PPReadWord() & 0xff; \ + a += (PPReadWord() & 0xff) << 8 +#define READ_TURBO_S2(a,b,c) outpb (SPPCtrlPort, 0); \ + b = PPReadWord () & 0x80; \ + c = PPReadWord () & 0x80 +#define WRITE_FLASH_NEXT(a,b) PPWriteWord (b) +#define SET_CART_ADDR(a) SetCartAddr (a); \ + l4021d0 (3) +#define CTRL_PORT_0 outpb (SPPCtrlPort, 0) +#define CTRL_PORT_1 outpb (SPPCtrlPort, 1) + +void +WriteRepeat (int addr, int data, int count) +{ + int i; + for (i = 0; i < count; i++) + WriteFlash (addr, data); +} + +#else + // GBA in-system programming defines +#define _MEM_INC 2 +#define FP_TIMEOUT1 0x4000 // Probably could be MUCH smaller +#define FP_TIMEOUT2 0x8000 // Probably could be MUCH smaller +#define FP_TIMEOUT3 0x80000 // Probably could be MUCH smaller +#define INTEL28F_BLOCKERASE 0x20 +#define INTEL28F_CLEARSR 0x50 +#define INTEL28F_CONFIRM 0xD0 +#define INTEL28F_QUIRY 0x98 +#define INTEL28F_READARRAY 0xff +#define INTEL28F_READSR 0x70 +#define INTEL28F_RIC 0x90 +#define INTEL28F_WRTOBUF 0xe8 + +#define SHARP28F_BLOCKERASE 0x20 +#define SHARP28F_CONFIRM 0xD0 +#define SHARP28F_WORDWRITE 0x10 +#define SHARP28F_READARRAY 0xff +// typedef volatile unsigned char vu8; +// typedef volatile unsigned short int vu16; +// typedef volatile unsigned int vu32; +// typedef volatile unsigned long long int vu64; + +// typedef unsigned char u8; +// typedef unsigned short int u16; +// typedef unsigned int u32; +// typedef unsigned long long int u64; + +#define _CART_START 0x8000000 +#define _BACKUP_START 0xe000000 +#define FLINKER_SET {} +#define READ_NTURBO_SR(a,b) *(vu16 *)a = INTEL28F_READSR; \ + b = *(vu16 *)a +#define READ_NTURBO_S(a) a = *(vu16 *)_CART_START +#define READ_TURBO_SR(a) *(vu16 *)_CART_START = INTEL28F_READSR; \ + *(vu16 *)(_CART_START+2) = INTEL28F_READSR; \ + a = *(vu16 *)_CART_START & 0xff; \ + a += (*(vu16 *)(_CART_START+2) & 0xff) << 8 +#define READ_TURBO_S(a) a = *(vu16 *)_CART_START & 0xff; \ + a += (*(vu16 *)(_CART_START+2) & 0xff) << 8 +#define READ_TURBO_S2(a,b,c) b = *(vu16 *)a & 0x80; \ + c = *(vu16 *)(a+2) & 0x80 +#define WRITE_FLASH_NEXT(a,b) WriteFlash (a, b) +#define SET_CART_ADDR(a) {} +#define CTRL_PORT_0 {} +#define CTRL_PORT_1 {} +#define CL_SECTION __attribute__ ((section (".iwram"))) + +#ifdef SET_CL_SECTION + // Prototypes to allow placing routines in any section +void +WriteFlash (u32 addr, u16 data) + CL_SECTION; + u16 ReadFlash (u32 addr) CL_SECTION; + void WriteRepeat (u32 addr, u16 data, u16 count) CL_SECTION; + void VisolyModePreamble (void) CL_SECTION; + void SetVisolyFlashRWMode (void) CL_SECTION; + void SetVisolyBackupRWMode (int i) CL_SECTION; + u8 CartTypeDetect (void) CL_SECTION; + u32 EraseNintendoFlashBlocks (u32 StartAddr, u32 BlockCount) CL_SECTION; + u32 EraseNonTurboFABlocks (u32 StartAddr, u32 BlockCount) CL_SECTION; + u32 EraseTurboFABlocks (u32 StartAddr, u32 BlockCount) CL_SECTION; + u32 WriteNintendoFlashCart (u32 SrcAddr, u32 FlashAddr, + u32 Length) CL_SECTION; + u32 WriteNonTurboFACart (u32 SrcAddr, u32 FlashAddr, + u32 Length) CL_SECTION; + u32 WriteTurboFACart (u32 SrcAddr, u32 FlashAddr, u32 Length) CL_SECTION; +#endif + + void WriteFlash (u32 addr, u16 data) +{ + *(vu16 *) addr = data; +} + +u16 +ReadFlash (u32 addr) +{ + return (*(vu16 *) addr); +} + +void +WriteRepeat (u32 addr, u16 data, u16 count) +{ + u16 i; + for (i = 0; i < count; i++) + *(vu16 *) (_CART_START + (addr << 1)) = data; +} +#endif + +#ifdef COMMON_FA_SUPPORT +void +VisolyModePreamble (void) // 402438 +{ + FLINKER_SET; + WriteRepeat (0x987654, 0x5354, 1); + WriteRepeat (0x12345, 0x1234, 500); + WriteRepeat (0x7654, 0x5354, 1); + WriteRepeat (0x12345, 0x5354, 1); + WriteRepeat (0x12345, 0x5678, 500); + WriteRepeat (0x987654, 0x5354, 1); + WriteRepeat (0x12345, 0x5354, 1); + WriteRepeat (0x765400, 0x5678, 1); + WriteRepeat (0x13450, 0x1234, 1); + WriteRepeat (0x12345, 0xabcd, 500); + WriteRepeat (0x987654, 0x5354, 1); +} + +void +SetVisolyFlashRWMode (void) +{ + VisolyModePreamble (); + WriteRepeat (0xf12345, 0x9413, 1); +} + +void +SetVisolyBackupRWMode (int i) // 402550 +{ + VisolyModePreamble (); + WriteRepeat (0xa12345, i, 1); +} +#endif + +// Cart Type Detect +// Return a value indicating type of cart installed: +// 0xdc = Hudson Cart, 0x2e = Standard ROM Cart +// 0xe2 = N Flash Cart, 0xff = Unknown +// 0x17 = FA 64M, 0x96 = Turbo FA 64M +// 0x18 = FA 128M, 0x97 = Turbo FA 128M + +u8 +CartTypeDetect (void) +{ + u8 type = 0xff; + u16 Manuf, Device; + + WriteFlash (_CART_START, INTEL28F_RIC); // Read Identifier codes from flash. + // Works for intel 28F640J3A & Sharp LH28F320BJE. + Manuf = ReadFlash (_CART_START); + Device = ReadFlash (_CART_START + _MEM_INC); + + switch (Manuf) + { + case 0: // Hudson Cart + type = 0xdc; + break; + case 0x2e: // Standard ROM + type = (u8) Manuf; + break; + case 0x89: // Intel chips + switch (Device) + { + case 0x16: // i28F320J3A + case 0x17: // i28F640J3A + case 0x18: // i28F128J3A + type = (u8) Device; + break; + default: + // Check to see if this is a Visoly "Turbo" cart + Device = ReadFlash (_CART_START + _MEM_INC + _MEM_INC); + switch (Device) + { + case 0x16: // 2 x i28F320J3A + case 0x17: // 2 x i28F640J3A + case 0x18: // 2 x i28F128J3A + type = Device + 0x80; + break; + } + } + break; + case 0xb0: // Sharp chips + switch (Device) + { + case 0xe2: + type = (u8) Device; + break; + } + break; + } + WriteFlash (_CART_START, INTEL28F_READARRAY); // Set flash to normal read mode + return type; +} + +#ifdef NOA_FLASH_CART_SUPPORT +// Erase official Nintendo flash cart blocks +// Function returns true if erase was successful. +// Each block represents 64k bytes. + +u32 +EraseNintendoFlashBlocks (u32 StartAddr, u32 BlockCount) +{ + int i = 0; + int j, k; + time_t starttime = time (NULL); + + for (k = 0; k < (int) BlockCount; k++) + { + i = StartAddr + (k * 32768 * _MEM_INC); + + do + { + READ_NTURBO_SR (i, j); + } + while ((j & 0x80) == 0); + WriteFlash (i, SHARP28F_BLOCKERASE); // Erase a 64k byte block + WriteFlash (i, SHARP28F_CONFIRM); // Comfirm block erase + ucon64_gauge (starttime, (k + 1) * 64 * 1024, BlockCount * 64 * 1024); + } + + do + { + READ_NTURBO_SR (i, j); + } + while ((j & 0x80) == 0); + WriteFlash (i, SHARP28F_READARRAY); // Set normal read mode + return 1; +} +#endif + +#ifdef NONTURBO_FA_SUPPORT +// Erase older (non-Turbo) FA/Visoly flash cart blocks +// (Single flash chip) +// Function returns true if erase was successful. +// Each block represents 128k bytes. + +u32 +EraseNonTurboFABlocks (u32 StartAddr, u32 BlockCount) +{ + u16 k; + u16 Ready = 1; + u32 i = 0; + u32 Timeout; + time_t starttime = time (NULL); + + for (k = 0; k < BlockCount; k++) + { + i = StartAddr + (k * 65536 * _MEM_INC); + + Ready = 0; + Timeout = FP_TIMEOUT2; + + while ((Ready == 0) && (Timeout != 0)) + { + READ_NTURBO_SR (_CART_START, Ready); + Ready &= 0x80; + Timeout--; + } + + if (Ready) + { + WriteFlash (i, INTEL28F_BLOCKERASE); // Erase a 128k byte block + Ready = 0; + Timeout = FP_TIMEOUT3; + + while ((!Ready) && (Timeout != 0)) + { + READ_NTURBO_S (Ready); + Ready = (Ready == 0x80); + Timeout--; + } + + if (Ready) + { + WriteFlash (i, INTEL28F_CONFIRM); // Comfirm block erase + Ready = 0; + Timeout = FP_TIMEOUT3; + + while ((!Ready) && (Timeout != 0)) + { + READ_NTURBO_S (Ready); + Ready = (Ready == 0x80); + Timeout--; + } + + if (Ready) + { + READ_NTURBO_SR (_CART_START, Ready); + Ready = (Ready == 0x80); + + if (!Ready) + break; + } + else + break; + } + else + break; + } + else + break; + ucon64_gauge (starttime, (k + 1) * 128 * 1024, + BlockCount * 128 * 1024); + } + + if (!Ready) + { + WriteFlash (i, INTEL28F_CLEARSR); // Clear flash status register + } + + WriteFlash (i, INTEL28F_READARRAY); // Set flash to normal read mode + WriteFlash (i, INTEL28F_READARRAY); // Set flash to normal read mode + + return Ready != 0; +} +#endif + +#ifdef TURBO_FA_SUPPORT +// Erase newer (Turbo) FA/Visoly flash cart blocks +// (Dual chip / Interleave) +// Function returns true if erase was successful. +// Each block represents 256k bytes. + +u32 +EraseTurboFABlocks (u32 StartAddr, u32 BlockCount) +{ + u16 j, k; + u16 done1, done2; + u16 Ready = 1; + u32 i = 0; + u32 Timeout; + time_t starttime = time (NULL); + + for (k = 0; k < BlockCount; k++) + { + i = StartAddr + (k * 131072 * _MEM_INC); + + Ready = 0; + Timeout = FP_TIMEOUT2; + + while ((!Ready) && (Timeout != 0)) + { + READ_TURBO_SR (j); + Ready = (j == 0x8080); + Timeout--; + } + + if (Ready) + { + done1 = 0; + done2 = 0; + Ready = 0; + Timeout = FP_TIMEOUT3; + + while ((!Ready) && (Timeout != 0)) + { + if (done1 == 0) + WriteFlash (i, INTEL28F_BLOCKERASE); // Erase a 128k byte block in flash #1 + if (done2 == 0) + WriteFlash (i + _MEM_INC, INTEL28F_BLOCKERASE); // Erase a 128k byte block in flash #2 + + READ_TURBO_S2 (_CART_START, done1, done2); + Ready = ((done1 + done2) == 0x100); + + Timeout--; + } + + if (Ready) + { + WriteFlash (i, INTEL28F_CONFIRM); // Comfirm block erase in flash #1 + WriteFlash (i + _MEM_INC, INTEL28F_CONFIRM); // Comfirm block erase in flash #2 + + Ready = 0; + Timeout = FP_TIMEOUT3; + j = 0; + + while (((j & 0x8080) != 0x8080) && (Timeout != 0)) + { + READ_TURBO_S (j); + Ready = (j == 0x8080); + + Timeout--; + } + + if (!Ready) + break; + } + else + break; + } + else + break; + ucon64_gauge (starttime, (k + 1) * 256 * 1024, + BlockCount * 256 * 1024); + } + + if (!Ready) + { + WriteFlash (i, INTEL28F_CLEARSR); + WriteFlash (i + _MEM_INC, INTEL28F_CLEARSR); + } + + WriteFlash (_CART_START, INTEL28F_READARRAY); + WriteFlash (_CART_START + _MEM_INC, INTEL28F_READARRAY); + WriteFlash (_CART_START, INTEL28F_READARRAY); + WriteFlash (_CART_START + _MEM_INC, INTEL28F_READARRAY); + + return Ready != 0; +} +#endif + +#ifdef NOA_FLASH_CART_SUPPORT +// Write 2 x Length bytes to official Nintendo flash cart. +// Function returns true if write was successful. + +u32 +WriteNintendoFlashCart (u32 SrcAddr, u32 FlashAddr, u32 Length) +{ + int j; + int LoopCount = 0; + u16 *SrcAddr2 = (u16 *) +#ifdef __LP64__ + (u64) +#endif + SrcAddr; + + while (LoopCount < (int) Length) + { + do + { + READ_NTURBO_SR (FlashAddr, j); + } + while ((j & 0x80) == 0); + + WriteFlash (FlashAddr, SHARP28F_WORDWRITE); + WriteFlash (FlashAddr, *SrcAddr2); + SrcAddr2 += 2; + FlashAddr += _MEM_INC; + LoopCount++; + } + + do + { + READ_NTURBO_SR (FlashAddr, j); + } + while ((j & 0x80) == 0); + + WriteFlash (_CART_START, SHARP28F_READARRAY); +// CTRL_PORT_0; + return 1; +} +#endif + +#ifdef NONTURBO_FA_SUPPORT +// Write 32 x Length bytes to older (non-Turbo) FA/Visoly flash cart. +// Function returns true if write was successful. + +u32 +WriteNonTurboFACart (u32 SrcAddr, u32 FlashAddr, u32 Length) +{ + int Ready = 0; + int Timeout = 0; + int LoopCount = 0; + u16 *SrcAddr2 = (u16 *) +#ifdef __LP64__ + (u64) +#endif + SrcAddr; + + while (LoopCount < (int) Length) + { + Ready = 0; + Timeout = FP_TIMEOUT1; + + while ((Ready == 0) && (Timeout != 0)) + { + WriteFlash (FlashAddr, INTEL28F_WRTOBUF); + READ_NTURBO_S (Ready); + Ready &= 0x80; + + Timeout--; + } + + if (Ready) + { + int i; + + WriteFlash (FlashAddr, 15); // Write 15+1 16bit words + + SET_CART_ADDR (FlashAddr); + + for (i = 0; i < 16; i++) + { + WRITE_FLASH_NEXT (FlashAddr, *SrcAddr2); + SrcAddr2 += 2; + FlashAddr += _MEM_INC; + } + + WRITE_FLASH_NEXT (FlashAddr, INTEL28F_CONFIRM); + + Ready = 0; + Timeout = FP_TIMEOUT1; + + while ((Ready == 0) && (Timeout != 0)) + { + READ_NTURBO_SR (_CART_START, i); + Ready = i & 0x80; + + Timeout--; + } + + if (Ready) + { + if (i & 0x7f) + { + // One or more status register error bits are set + CTRL_PORT_1; + WriteFlash (0, INTEL28F_CLEARSR); + Ready = 0; + break; + } + } + else + { + CTRL_PORT_1; + WriteFlash (0, INTEL28F_CLEARSR); + break; + } + } + else + { + break; + } + + LoopCount++; + } + WriteFlash (_CART_START, INTEL28F_READARRAY); // Set flash to normal read mode + WriteFlash (_CART_START, INTEL28F_READARRAY); // Set flash to normal read mode + return Ready != 0; +} +#endif + +#ifdef TURBO_FA_SUPPORT +// Write 64 x Length bytes to newer (Turbo) FA/Visoly flash cart. +// Function returns true if write was successful. + +u32 +WriteTurboFACart (u32 SrcAddr, u32 FlashAddr, u32 Length) +{ + int i, k; + int done1, done2; + int Timeout; + int Ready = 0; + int LoopCount = 0; + u16 *SrcAddr2 = (u16 *) +#ifdef __LP64__ + (u64) +#endif + SrcAddr; + + while (LoopCount < (int) Length) + { + done1 = 0; + done2 = 0; + Ready = 0; + Timeout = 0x4000; + + while ((!Ready) && (Timeout != 0)) + { + if (done1 == 0) + WriteFlash (FlashAddr, INTEL28F_WRTOBUF); + if (done2 == 0) + WriteFlash (FlashAddr + _MEM_INC, INTEL28F_WRTOBUF); + + SET_CART_ADDR (FlashAddr); + READ_TURBO_S2 (FlashAddr, done1, done2); + + Ready = ((done1 + done2) == 0x100); + + Timeout--; + } + + if (Ready) + { + WriteFlash (FlashAddr, 15); // Write 15+1 16bit words + WRITE_FLASH_NEXT (FlashAddr + _MEM_INC, 15); // Write 15+1 16bit words + + SET_CART_ADDR (FlashAddr); + + for (i = 0; i < 32; i++) + { + WRITE_FLASH_NEXT (FlashAddr, *SrcAddr2); + SrcAddr2 += 2; + FlashAddr += _MEM_INC; + } + WRITE_FLASH_NEXT (FlashAddr, INTEL28F_CONFIRM); + WRITE_FLASH_NEXT (FlashAddr + _MEM_INC, INTEL28F_CONFIRM); + + Ready = 0; + Timeout = 0x4000; + k = 0; + + while (((k & 0x8080) != 0x8080) && (Timeout != 0)) + { + READ_TURBO_S (k); + Ready = (k == 0x8080); + + Timeout--; + } + + if (!Ready) + break; + } + else + break; + LoopCount++; + } + + WriteFlash (_CART_START, INTEL28F_READARRAY); + CTRL_PORT_0; + WriteFlash (_CART_START + _MEM_INC, INTEL28F_READARRAY); + CTRL_PORT_0; + + if (!Ready) + { + WriteFlash (_CART_START, INTEL28F_CLEARSR); + WriteFlash (_CART_START + _MEM_INC, INTEL28F_CLEARSR); + } + return Ready != 0; +} +#endif + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/cd64.c b/ucon64/2.0/src/backup/cd64.c new file mode 100644 index 0000000..1073d4f --- /dev/null +++ b/ucon64/2.0/src/backup/cd64.c @@ -0,0 +1,504 @@ +/* +cd64.c - CD64 support for uCON64 + +Copyright (c) 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include "misc/misc.h" +#include "misc/parallel.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_dat.h" +#include "ucon64_misc.h" +#include "cd64.h" + + +const st_getopt2_t cd64_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "CD64"/*"19XX UFO http://www.cd64.com"*/, + NULL + }, +#if defined USE_PARALLEL && defined USE_LIBCD64 + { + "xcd64", 0, 0, UCON64_XCD64, + NULL, "send/receive ROM to/from CD64; " OPTION_LONG_S "port=PORT\n" + "receives automatically (64 Mbits) when ROM does not exist", + &ucon64_wf[WF_OBJ_N64_DEFAULT_STOP_NO_ROM] + }, + { + "xcd64c", 1, 0, UCON64_XCD64C, + "N", "receive N Mbits of ROM from CD64; " OPTION_LONG_S "port=PORT", + &ucon64_wf[WF_OBJ_N64_STOP_NO_ROM] + }, + { + "xcd64b", 0, 0, UCON64_XCD64B, + NULL, "send boot emu to CD64; " OPTION_LONG_S "port=PORT", + &ucon64_wf[WF_OBJ_N64_DEFAULT_STOP] + }, + { + "xcd64s", 0, 0, UCON64_XCD64S, + NULL, "send/receive SRAM to/from CD64; " OPTION_LONG_S "port=PORT\n" + "receives automatically when SRAM file does not exist", + &ucon64_wf[WF_OBJ_N64_STOP_NO_ROM] + }, + { + "xcd64f", 0, 0, UCON64_XCD64F, + NULL, "send/receive flash RAM to/from CD64; " OPTION_LONG_S "port=PORT\n" + "receives automatically when flash RAM file does not exist", + &ucon64_wf[WF_OBJ_N64_STOP_NO_ROM] + }, + { + "xcd64e", 0, 0, UCON64_XCD64E, + NULL, "send/receive EEPROM data to/from CD64; " OPTION_LONG_S "port=PORT\n" + "receives automatically when EEPROM file does not exist", + &ucon64_wf[WF_OBJ_N64_STOP_NO_ROM] + }, + { + "xcd64m", 1, 0, UCON64_XCD64M, + "INDEX", "send/receive memory pack data to/from CD64; " OPTION_LONG_S "port=PORT\n" + "INDEX is ignored for CD64 BIOS protocol\n" + "receives automatically when memory pack file does not exist", + &ucon64_wf[WF_OBJ_N64_STOP_NO_ROM] + }, + { + "xcd64p", 1, 0, UCON64_XCD64P, + "PROT", "use protocol PROT when communicating with CD64; " OPTION_LONG_S "port=PORT\n" + "PROT=0 CD64 BIOS\n" + "PROT=1 Ghemor\n" + "PROT=2 UltraLink", + &ucon64_wf[WF_OBJ_N64_SWITCH] + }, +#endif // USE_PARALLEL && USE_LIBCD64 + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +#if defined USE_PARALLEL && defined USE_LIBCD64 + +static time_t cd64_starttime; + + +static void +cd64_progress (uint32_t current, uint32_t total) +{ + ucon64_gauge (cd64_starttime, current, total); +} + + +static int +cd64_notice_helper (FILE *file, const char *prefix, const char *format, + va_list argptr) +{ + int n_chars; + + fputs (prefix, file); + n_chars = vfprintf (file, format, argptr); + fputc ('\n', file); + fflush (file); + + return n_chars; +} + + +static int +cd64_notice (const char *format, ...) +{ + va_list argptr; + int n_chars; + + va_start (argptr, format); + n_chars = cd64_notice_helper (stdout, "NOTE (libcd64): ", format, argptr); + va_end (argptr); + + return n_chars; +} + + +static int +cd64_notice2 (const char *format, ...) +{ + va_list argptr; + int n_chars; + + va_start (argptr, format); + n_chars = cd64_notice_helper (stderr, "ERROR (libcd64): ", format, argptr); + va_end (argptr); + + return n_chars; +} + + +static int +fread_wrapper (void *io_id, void *buffer, uint32_t size) +{ + return fread (buffer, 1, size, (FILE *) io_id); +} + + +static int +fwrite_wrapper (void *io_id, void *buffer, uint32_t size) +{ + return fwrite (buffer, 1, size, (FILE *) io_id); +} + + +static int32_t +ftell_wrapper (void *io_id) +{ + return (int32_t) ftell ((FILE *) io_id); +} + + +static int +fseek_wrapper (void *io_id, int32_t offset, int whence) +{ + return fseek ((FILE *) io_id, offset, whence); +} + + +static struct cd64_t * +cd64_init (void) +{ + struct cd64_t *cd64; +#ifdef USE_PPDEV + uint16_t port = strtol (&ucon64.parport_dev[strlen (ucon64.parport_dev) - 1], NULL, 10); + method_t method = PPDEV; +#else + uint16_t port = ucon64.parport; + method_t method = RAWIO; +#endif + int is_parallel = 1; + + if ((cd64 = (struct cd64_t *) calloc (1, sizeof (struct cd64_t))) == NULL) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], sizeof (struct cd64_t)); + exit (1); + } + +#ifndef USE_PPDEV + if (ucon64.parport == UCON64_UNKNOWN) + { + fputs ("ERROR: No port or invalid port specified\n" + "TIP: Specify one with --port or in the configuration file\n", stderr); + exit (1); + } + if (port >= 0x300 && port <= 0x330) + is_parallel = 0; +#endif + + cd64->notice_callback = cd64_notice; + cd64->notice_callback2 = cd64_notice2; + + if (!cd64_create (cd64, method, port, (protocol_t) ucon64.io_mode, is_parallel)) + { + fputs ("ERROR: Could not initialise libcd64\n", stderr); + exit (1); + } + + cd64->read_callback = fread_wrapper; // actually f*2(), if zlib + cd64->write_callback = fwrite_wrapper; // support is enabled + cd64->tell_callback = ftell_wrapper; + cd64->seek_callback = fseek_wrapper; + cd64->progress_callback = cd64_progress; + strcpy (cd64->io_driver_dir, ucon64.configdir); + + // parport_print_info() displays a reasonable message (even if we're using a + // comms link) + parport_print_info (); + + if (!cd64->devopen (cd64)) + { + fputs ("ERROR: Could not open I/O device for CD64\n", stderr); + exit (1); + } +#if defined __unix__ && !defined __MSDOS__ + drop_privileges (); +#endif + + return cd64; +} + + +int +cd64_read_rom (const char *filename, int size) +{ + FILE *file; + struct cd64_t *cd64 = cd64_init (); + + if ((file = fopen (filename, "w+b")) == NULL) // cd64_download_cart() also + { // reads from file + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + + cd64_starttime = time (NULL); + cd64_download_cart (cd64, file, size * MBIT, NULL); + + cd64->devclose (cd64); + fclose (file); + free (cd64); + + return 0; +} + + +int +cd64_write_rom (const char *filename) +{ + FILE *file; + struct cd64_t *cd64 = cd64_init (); + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + + cd64_starttime = time (NULL); + cd64_upload_dram (cd64, file, ucon64.file_size, NULL, 1); + + cd64->devclose (cd64); + fclose (file); + free (cd64); + + return 0; +} + + +int +cd64_write_bootemu (const char *filename) +{ + FILE *file; + struct cd64_t *cd64 = cd64_init (); + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + + cd64_starttime = time (NULL); + cd64_upload_bootemu (cd64, file, ucon64.file_size, NULL); + + cd64->devclose (cd64); + fclose (file); + free (cd64); + + return 0; +} + + +int +cd64_read_sram (const char *filename) +{ + FILE *file; + struct cd64_t *cd64 = cd64_init (); + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + + cd64_starttime = time (NULL); + cd64_download_sram (cd64, file); + + cd64->devclose (cd64); + fclose (file); + free (cd64); + + return 0; +} + + +int +cd64_write_sram (const char *filename) +{ + FILE *file; + struct cd64_t *cd64 = cd64_init (); + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + + cd64_starttime = time (NULL); + cd64_upload_sram (cd64, file); + + cd64->devclose (cd64); + fclose (file); + free (cd64); + + return 0; +} + + +int +cd64_read_flashram (const char *filename) +{ + FILE *file; + struct cd64_t *cd64 = cd64_init (); + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + + cd64_starttime = time (NULL); + cd64_download_flashram (cd64, file); + + cd64->devclose (cd64); + fclose (file); + free (cd64); + + return 0; +} + + +int +cd64_write_flashram (const char *filename) +{ + FILE *file; + struct cd64_t *cd64 = cd64_init (); + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + + cd64_starttime = time (NULL); + cd64_upload_flashram (cd64, file); + + cd64->devclose (cd64); + fclose (file); + free (cd64); + + return 0; +} + + +int +cd64_read_eeprom (const char *filename) +{ + FILE *file; + struct cd64_t *cd64 = cd64_init (); + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + + cd64_starttime = time (NULL); + cd64_download_eeprom (cd64, file); + + cd64->devclose (cd64); + fclose (file); + free (cd64); + + return 0; +} + + +int +cd64_write_eeprom (const char *filename) +{ + FILE *file; + struct cd64_t *cd64 = cd64_init (); + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + + cd64_starttime = time (NULL); + cd64_upload_eeprom (cd64, file); + + cd64->devclose (cd64); + fclose (file); + free (cd64); + + return 0; +} + + +int +cd64_read_mempack (const char *filename, int index) +{ + FILE *file; + struct cd64_t *cd64 = cd64_init (); + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + + if (ucon64.io_mode == CD64BIOS) + index = -1; + cd64_starttime = time (NULL); + cd64_download_mempak (cd64, file, (int8_t) index); + + cd64->devclose (cd64); + fclose (file); + free (cd64); + + return 0; +} + + +int +cd64_write_mempack (const char *filename, int index) +{ + FILE *file; + struct cd64_t *cd64 = cd64_init (); + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + + if (ucon64.io_mode == CD64BIOS) + index = -1; + cd64_starttime = time (NULL); + cd64_upload_mempak (cd64, file, (int8_t) index); + + cd64->devclose (cd64); + fclose (file); + free (cd64); + + return 0; +} + +#endif // USE_PARALLEL && USE_LIBCD64 diff --git a/ucon64/2.0/src/backup/cd64.h b/ucon64/2.0/src/backup/cd64.h new file mode 100644 index 0000000..5d90829 --- /dev/null +++ b/ucon64/2.0/src/backup/cd64.h @@ -0,0 +1,41 @@ +/* +cd64.h - CD64 support for uCON64 + +Copyright (c) 2001 NoisyB +Copyright (c) 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef CD64_H +#define CD64_H + +extern const st_getopt2_t cd64_usage[]; + +#if defined USE_PARALLEL && defined USE_LIBCD64 +extern int cd64_read_rom(const char *filename, int size); +extern int cd64_write_rom(const char *filename); +extern int cd64_write_bootemu (const char *filename); +extern int cd64_read_sram(const char *filename); +extern int cd64_write_sram(const char *filename); +extern int cd64_read_flashram(const char *filename); +extern int cd64_write_flashram(const char *filename); +extern int cd64_read_eeprom(const char *filename); +extern int cd64_write_eeprom(const char *filename); +extern int cd64_read_mempack(const char *filename, int index); +extern int cd64_write_mempack(const char *filename, int index); +#endif // USE_PARALLEL && USE_LIBCD64 + +#endif // CD64_H diff --git a/ucon64/2.0/src/backup/cmc.c b/ucon64/2.0/src/backup/cmc.c new file mode 100644 index 0000000..c136c31 --- /dev/null +++ b/ucon64/2.0/src/backup/cmc.c @@ -0,0 +1,985 @@ +/* +cmc.c - Cyan's Megadrive ROM copier support for uCON64 + +Copyright (c) 1999 - 2004 Cyan Helkaraxe + +Special thanks to dbjh for helping with the uCON64 integration +of this software, and providing the wrapping code. + +CMC version: 2.5 +For hardware version 1.x + +Copies Sega Megadrive/Genesis cartridges into .BIN format ROM files. + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* +Additional information: +This software is distributed in accordance with the GNU General +Public License. The author of the hardware design/documentation and +software, Cyan Helkaraxe, retains the copyright over the 'cmc' code +('software'), the hardware design and construction documentation itself. +Cyan grants you the rights of the GNU General Public License over +the software, as stated above. The copyright does not affect your +rights in relation to the 'cmc' code under the GNU General Public +License in any way. +Cyan Helkaraxe does NOT grant you any rights relating to the hardware +design/documentation, over and above the right to build the device for +personal, not-for-profit use. Likewise, the same applies to the ROM +copier construction documentation, available at +http://www.emulationzone.org/projects/cyan/docs/ for not-for-profit +personal use. + +Obviously, feel free to make changes to this software, but if you do so, +*please* clearly mark the fact it is modified, to avoid confusion. +A define is provided below this commented area, which should be edited +as described. This is to inform users which version of the code they are +using at runtime, and if they encounter a problem, it makes it far easier +for me to help debug it. I have no evil nefarious intentions. =P + +Obviously, your changes must adhere to the GNU GPL, and please keep the +copyright, Cyan's name, e-mail and web site address in the comments +somewhere. + +If you wish to do anything with the hardware design or the documentation +that goes beyond personal use, get in touch with Cyan first; +cyan@emulationzone.org + +Disclaimer / Warranty: +This is to emphasise, not replace, any warranty information provided in +the GNU GPL or uCON64. +There is no warranty whatsoever, for either the software or accompanying +hardware/design/documentation. This software is provided free of charge, +AS-IS, in the hope that it may be useful. +Use it at your own risk. The author does not make any guarantee that you +will be able to use this software or accompanying +hardware/design/documentation, or that it will perform smoothly. There is +a possibility that damage or loss (including but not limited to financial +loss, hardware or software damage, data corruption, privacy violation, +or personal injury, suffering, legal action, imprisonment or death) may +arise through the use of this software or the accompanying +hardware/design/documentation, in addition to many other possible outcomes. +You take sole responsibility for any outcomes; by using this software or +accompanying hardware/design/ documentation, you agree that the author will +not be held responsible for anything that may occur. If your jurisdiction +does not allow the author to be isolated from responsibility, then you +must *not* use this software or accompanying hardware/design/documentation. +The author does not condone software piracy. You may not use this software +to engage in piracy. The author is not responsible for anything you choose +to use this software for, although the author strongly recommends that you +use the software for the purpose for which it was intended to be used -- +primarily as an educational tool, and secondarily to make backup copies of +your expensive/rare game cartridges, or a similarly harmless and legal +purpose. +Note that although the author isn't aware of any patent violations caused +by this software/hardware/design/documentation, it is possible that +there may be patents covering parts of this device. It is your +responsibility to check this before building or using the hardware or +software, and Cyan may not be held responsible in the event of an +infringement. + +That being said, if you do encounter any problems with the hardware or +software, then feel free to get in touch with the author; use the subject +line 'ROM Copier Technical Support'. No promises or guarantees are made, +however. + +Also note that the author is not affiliated with anyone involved with uCON64; +therefore, only correspondence relating to this particular file (the 'cmc' +code) or the accompanying hardware design should be directed to Cyan. +If you have more general uCON64 questions, Cyan is *not* the person to ask. +Likewise, the terms "the author" and "software" in this file (cmc.c, and +additionally cmc.h), along with similar terms, apply only to Cyan, and the +CMC software you see in this file. The disclaimer above, for example, relates +exclusively to the CMC code. + +All trademarks, indicated or otherwise, are the property of their +respective owners. +*/ + +/* + NOTE! + Please edit the following line, and remove the word "original" from it + if you have made any modifications to this file. This reduces user + confusion. +*/ +#define INTRO_TEXT "Cyan's Megadrive Copier (c) 1999-2004 Cyan Helkaraxe\n" \ + "Software version 2.5 original, designed for hardware version 1.x\n\n" + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include "misc/bswap.h" +#include "misc/misc.h" +#include "misc/parallel.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "cmc.h" + +#ifdef USE_PARALLEL + +#define RSLO 0x40 // reset line 1 d7 +#define RSHI 0x01 // reset line 2 d0 +#define CNLO 0x10 // counter line 1 d4 +#define CNHI 0x04 // counter line 2 d2 +#define INLO 0x40 // input 1 ack (int disabled) +#define INHI 0x10 // input 2 selectin + +#define MBYTE (1024 * 1024) +#define DEFAULT_SPEED 3 + +#ifdef _MSC_VER +// Visual C++ doesn't allow inline in C source code +#define inline __inline +#endif + + +/************************ + * Internal functions * + ************************/ + +static inline void +cyan_write_copier (unsigned char data, unsigned int parport) +// write a value to the data register of the parallel port +{ + outportb ((unsigned short) (parport + PARPORT_DATA), data); +} + + +static inline unsigned char +cyan_read_copier (unsigned int parport) +// read a value from the status register of the parallel port +{ + return inportb ((unsigned short) (parport + PARPORT_STATUS)); +} + + +static inline unsigned char +cyan_verify_copier (unsigned int parport) +// read a value back from the data register for verification +{ + return inportb ((unsigned short) (parport + PARPORT_DATA)); +} + + +/**** non-hardware, non-accessing ****/ + +static unsigned long +cyan_calculate_rom_size (unsigned char *buffer, int test_mode) +/* + Calculate the ROM size, by looking at the ROM size entry in the ROM 'header', + and the overall structure. + This function always returns a value rounded to a power of two between 128 + kbytes and 4 Mbytes. It also inspects the ROM for 0's or ffh's. If test_mode + is 1 it causes an error on that condition, frees the buffer and exits. +*/ +{ + unsigned long i = 0x80000000, reported_size; + + // look at reported size + reported_size = (buffer[0x1a4] << 24) + + (buffer[0x1a5] << 16) + + (buffer[0x1a6] << 8) + + buffer[0x1a7] + 1; + // cap + // there is a minimum valid size for ROMs, according to some sources + if (reported_size < MBIT) + reported_size = MBIT; + if (reported_size > 4 * MBYTE) + reported_size = 4 * MBYTE; + // round + if (reported_size & (reported_size - 1)) + { + while (!(reported_size & 0x80000000)) + { + i >>= 1; + reported_size = (reported_size << 1) | 1; + } + reported_size = i << 1; + } + // calculate real size + for (i = 2 * MBYTE; i >= 65536; i >>= 1) + if (memcmp (buffer, buffer + i, i)) + { + i >>= 1; + break; + } + i <<= 2; + if (reported_size < i) + reported_size = i; // pick the safest (largest) of the two + if (i == MBIT) + { + for (i = 0; i < MBIT; i++) + if ((buffer[i] != 0xff) && (buffer[i] != 0x00)) + break; + if (i == MBIT) + { + FILE *output; + + if (test_mode) + { + output = stderr; + fputs ("\nERROR: ", stderr); + } + else + { + output = stdout; + fputs ("\nWARNING: ", stdout); + } + + // "WARNING: " + fputs ( "The ROM file appears to consist of nothing but 0x00 / 0xff values.\n" + " This usually indicates a serious problem. Perhaps your parallel port\n" + " isn't configured correctly, or there is some problem with the ROM\n" + " copier. Is it getting power? Is a cartridge inserted? Is it properly\n" + " attached to the PC?\n", + output); + if (test_mode) + { + free (buffer); + exit (1); + } + } + } + return reported_size; +} + + +static int +cyan_checksum_rom (unsigned char *buffer) +// return 0 on success, -1 on failure +{ + unsigned char *buffer2 = buffer; + unsigned short reported_sum, running_sum = 0; + + reported_sum = (buffer2[0x18e] << 8) + buffer2[0x18f]; + buffer2 += ((buffer2[0x1a4] << 24) + + (buffer2[0x1a5] << 16) + + (buffer2[0x1a6] << 8) + + (buffer2[0x1a7] & 0xfe)) + 2; + if (buffer2 > buffer + 4 * MBYTE) + buffer2 = buffer + 4 * MBYTE; + buffer += 0x200; + if (buffer2 < buffer + 2) + return -1; + + while (buffer2 != buffer) + { + running_sum += *--buffer2; + running_sum += (*--buffer2) << 8; + } + + return (running_sum & 0xffff) != reported_sum ? -1 : 0; +} + + +static inline unsigned long +cyan_get_address (unsigned long b) +// return the true address (word -- 0 - 2 M) based on word input +{ + return ((b & 0x000800) >> 11) | // bit 0 + ((b & 0x002000) >> 12) | // bit 1 + ((b & 0x004000) >> 12) | // bit 2 + ((b & 0x000020) >> 2) | // bit 3 + ((b & 0x100000) >> 16) | // bit 4 + ((b & 0x020000) >> 12) | // bit 5 + ((b & 0x000400) >> 4) | // bit 6 + ((b & 0x000001) << 7) | // bit 7 + ((b & 0x000002) << 7) | // bit 8 + ((b & 0x000010) << 5) | // bit 9 + ((b & 0x000040) << 4) | // bit 10 + ((b & 0x040000) >> 7) | // bit 11 + ((b & 0x080000) >> 7) | // bit 12 + ((b & 0x000080) << 6) | // bit 13 + ((b & 0x008000) >> 1) | // bit 14 + ((b & 0x010000) >> 1) | // bit 15 + ((b & 0x001000) << 4) | // bit 16 + ((b & 0x000004) << 15) | // bit 17 + ((b & 0x000008) << 15) | // bit 18 + ((b & 0x000200) << 10) | // bit 19 + ((b & 0x000100) << 12); // bit 20 +} + + +/**** non-hardware, indirectly accessing ****/ + +static inline void +cyan_delay (int speed, unsigned int parport) +// Delays a certain amount of time depending on speed selected. 0=long delay, +// used for reset and hi counter. +{ + int i, scritch = 0; + + switch (speed) + { + case 0: + for (i = 0; i < 128; i++) + scritch += cyan_read_copier (parport); + case 1: // falling through + for (i = 0; i < 64; i++) + scritch += cyan_read_copier (parport); + case 2: // falling through + for (i = 0; i < 12; i++) + scritch += cyan_read_copier (parport); + case 3: // falling through + scritch += cyan_read_copier (parport); + scritch += cyan_read_copier (parport); + } +} + + +static void +cyan_reset (unsigned int parport) +// resets the copier +{ + cyan_delay (0, parport); + // zero all data outputs first, before going into SPP mode + cyan_write_copier (0, parport); + // reset the port to SPP, float all control lines high + cyan_write_copier (0, parport + PARPORT_CONTROL); + cyan_delay (0, parport); + cyan_write_copier (RSLO | RSHI, parport); // both reset lines hi + cyan_delay (0, parport); + cyan_write_copier (0, parport); // both reset lines lo + cyan_delay (0, parport); +} + + +static inline unsigned short +cyan_get_word (int speed, unsigned int parport) +// gets a byte pair from the ROM and return two bytes in big endian byte order +{ + unsigned short value = 0; + unsigned char tempz; + + cyan_write_copier (0, parport); + cyan_delay (speed, parport); + tempz = cyan_read_copier (parport); + value |= tempz & INLO; // bit 6 + value |= (tempz & INHI) << 8; // bit 12 + + cyan_write_copier (CNLO, parport); + cyan_delay (speed, parport); + tempz = cyan_read_copier (parport); + value |= (tempz & INLO) >> 5; // bit 1 + value |= (tempz & INHI) << 9; // bit 13 + + cyan_write_copier (0, parport); + cyan_delay (speed, parport); + tempz = cyan_read_copier (parport); + value |= (tempz & INLO) << 3; // bit 9 + value |= (tempz & INHI) << 10; // bit 14 + + cyan_write_copier (CNLO, parport); + cyan_delay (speed, parport); + tempz = cyan_read_copier (parport); + value |= (tempz & INLO) >> 1; // bit 5 + value |= (tempz & INHI) << 11; // bit 15 + + cyan_write_copier (0, parport); + cyan_delay (speed, parport); + tempz = cyan_read_copier (parport); + value |= (tempz & INLO) >> 4; // bit 2 + value |= (tempz & INHI) << 4; // bit 8 + + cyan_write_copier (CNLO, parport); + cyan_delay (speed, parport); + tempz = cyan_read_copier (parport); + value |= (tempz & INLO) << 4; // bit 10 + value |= (tempz & INHI) >> 4; // bit 0 + + cyan_write_copier (0, parport); + cyan_delay (speed, parport); + tempz = cyan_read_copier (parport); + value |= (tempz & INLO) >> 2; // bit 4 + value |= (tempz & INHI) << 3; // bit 7 + + cyan_write_copier (CNLO, parport); + cyan_delay (speed, parport); + tempz = cyan_read_copier (parport); + value |= (tempz & INLO) >> 3; // bit 3 + value |= (tempz & INHI) << 7; // bit 11 + + return me2be_16 (value); +} + + +static inline int +check_exit (void) +// check for user abort +{ + int temp; + + if (ucon64.frontend) + return 0; + if (!kbhit ()) + return 0; + temp = getch (); + if (temp == 'q' || (temp == 27)) + return 1; + return 0; +} + + +static unsigned char * +cyan_read_rom (int speed, unsigned int parport, unsigned char *buffer) +/* + Read the ROM and return a pointer to a 4 MB area of memory containing all ROM + data. Designed to be used from inside cyan_copy_rom(), although it can be + called elsewhere if a raw (but decoded) dump is required. +*/ +{ + unsigned long q; + time_t t; + + // allocate the dump area + if (!buffer) + if ((buffer = (unsigned char *) malloc (4 * MBYTE)) == NULL) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], 4 * MBYTE); + exit (1); + } + + cyan_reset (parport); // reset the copier + + t = time (NULL); + // copy routine + for (q = 0; q < 2 * MBYTE; ) // loop through all words + { + // get a (16-bit) word from the ROM + ((unsigned short *) buffer)[cyan_get_address (q)] = cyan_get_word (speed, parport); + + // periodically update progress bar, without hammering ucon64_gauge() + if (!(q & (0xfff >> (5 - speed)))) + { + if (check_exit ()) + { + free (buffer); + puts ("\n" + "Copy aborted.\n" + "Don't forget to turn the ROM copier off and never insert or remove a cartridge\n" + "with the power on"); + break; + } + ucon64_gauge (t, q * 2, 4 * MBYTE); + } + + if (!(++q & 0x3ff)) // advance loop counter and carry to hi counter (11 bits) + { + cyan_delay (0, parport); + cyan_write_copier (CNHI, parport); + cyan_delay (0, parport); + cyan_write_copier (0, parport); + cyan_delay (0, parport); + } + } + + // make sure it's left in a state where it's safe to remove the cart + cyan_reset (parport); + + if (q != 2 * MBYTE) + return NULL; + + ucon64_gauge (t, q * 2, 4 * MBYTE); // make the progress bar reach 100% + + return buffer; +} + + +static void +cyan_test_parport (unsigned int parport) +// Test the parallel port to see if it appears to be functioning correctly, and +// terminate if there's an error. +{ + unsigned short temp; + + cyan_reset (parport); + fputs ("Basic parallel port test: ", stdout); + fflush (stdout); + cyan_write_copier (170, parport); + cyan_delay (0, parport); + temp = cyan_verify_copier (parport) & 170; + cyan_reset (parport); + + // even in unidirectional mode, the parallel port is bidirectional; at + // least, for a few short moments before the driver IC smokes + if ((cyan_verify_copier (parport) & 170) != 0 || temp != 170) + { + puts ("FAILED"); + fputs ("ERROR: Parallel port error\n" + " Check that your parallel port is configured properly, in the BIOS, OS,\n" + " and uCON64, and check for short circuits on the parallel port connector.\n" + " Also ensure that the ROM copier is getting power, and a cartridge is\n" + " inserted\n", + stderr); + exit (1); + } + else + puts ("Passed"); + + // discharge caps to see if we've got power + cyan_reset (parport); + cyan_reset (parport); + cyan_write_copier (CNLO + CNHI, parport); + cyan_delay (0, parport); + for (temp = 0; temp < 1000; temp++) + { + cyan_write_copier (0, parport); + cyan_delay (3, parport); + cyan_write_copier (CNLO + CNHI, parport); + cyan_delay (3, parport); + } + cyan_reset (parport); + cyan_reset (parport); + + fputs ("Parallel port output test: ", stdout); + fflush (stdout); + cyan_write_copier (255, parport); + cyan_delay (0, parport); + temp = (cyan_verify_copier (parport) != 255); + cyan_write_copier (0, parport); + cyan_delay (0, parport); + temp |= (cyan_verify_copier (parport) != 0); + cyan_write_copier (CNLO, parport); + cyan_delay (0, parport); + temp |= (cyan_verify_copier (parport) != CNLO); + cyan_write_copier (CNHI, parport); + cyan_delay (0, parport); + temp |= (cyan_verify_copier (parport) != CNHI); + cyan_write_copier (RSLO, parport); + cyan_delay (0, parport); + temp |= (cyan_verify_copier (parport) != RSLO); + cyan_write_copier (RSHI, parport); + cyan_delay (0, parport); + temp |= (cyan_verify_copier (parport) != RSHI); + cyan_reset (parport); + + // if it's still okay after that, then try reading the first set of inputs + // with lines high and low + if (!temp) + { + fputs ("Passed\n" + "Input crosstalk test: ", + stdout); + fflush (stdout); + temp = cyan_read_copier (parport) & (INLO | INHI); + cyan_write_copier (255 - CNLO, parport); + cyan_delay (0, parport); + temp = (temp != (cyan_read_copier (parport) & (INLO | INHI))); + cyan_reset (parport); + } + + if (temp) + { + puts ("FAILED"); + fputs ("ERROR: Parallel port error\n" + "Possible causes: ROM copier not getting power (check or replace battery)\n" + " Short circuit or bad connection (on parallel port or board)\n" + " Cartridge not inserted properly (or not inserted at all)\n" + " Parallel port not configured correctly\n" + " Orange, grey or green wire(s) soldered to the wrong locations\n" + " Chips inserted backwards\n" + "NOTE: Don't forget the ROM copier needs to be turned on before starting!\n", + stderr); + exit (1); + } + else + puts ("Passed"); +} + + +static int +cyan_test_copier (int test, int speed, unsigned int parport) +{ + unsigned char *buffer1, *buffer2 = NULL; + int count = 1; + + fputs (INTRO_TEXT, stdout); + parport_print_info (); + + switch (test) + { + // reliability test -- note: this test may be required to run for 8 hours or more + case 1: + printf ("Reliability test mode selected, speed %d\n", speed); + cyan_test_parport (parport); + puts ("\n" + "Entering non-stop reliability test mode (press escape or q to exit, and turn\n" + "ROM copier off immediately afterwards)\n" + "\n" + "Copy process will continue indefinitely until an error is encountered, at\n" + "which point the program will terminate.\n" + "A large number of passes suggests that the copier is working reliably at the\n" + "selected speed\n"); + printf (" P %2d", + count); + fflush (stdout); + if (ucon64.frontend) + fputc ('\n', stdout); + buffer1 = cyan_read_rom (speed, parport, NULL); + if (!buffer1) // user abort + exit (0); + + // detect if ROM is all 0x00 or 0xff and print an error if so + cyan_calculate_rom_size (buffer1, 1); + + while (1) + { + clear_line (); // remove last gauge + printf (" Pass %2d OK\n", count); + count++; + + // verify checksum of first pass + if (count == 2) // check only in first iteration + if (cyan_checksum_rom (buffer1)) // verify checksum + puts ("\n" + "WARNING: Checksum of ROM does not appear to be correct.\n" + " This may be normal for this ROM, or it may indicate a bad copy\n"); + + printf (" P %2d", + count); + fflush (stdout); + if (ucon64.frontend) + fputc ('\n', stdout); + buffer2 = cyan_read_rom (speed, parport, buffer2); + if (!buffer2) + { + free (buffer1); + exit (0); + } + if (memcmp (buffer1, buffer2, 4 * MBYTE)) + { + // error + printf ("\n" + "\n" + "Error detected on pass number %d\n" + "\n", + count); + if (count == 2) + puts ("A failure this early suggests a critical fault, such as a misconfigured or\n" + "incompatible parallel port, extremely poor wiring, or power supply problems --\n" + "you may wish to replace the battery or try another power supply, and use\n" + "shorter cables.\n" + "Try lowering the speed and running this test again, as a too high speed can\n" + "often cause these symptoms.\n" + "Alternatively, it may have been a one-time glitch; re-run the test to be sure.\n" + "When (if?) you find a lower speed which works reliably, use that speed for\n" + "copying ROMs\n"); + else + puts ("The first couple of passes were successful. This indicates that you have a\n" + "minor intermittent problem; most likely power supply problems, bad wiring, or\n" + "some kind of one-time glitch.\n" + "You may wish to replace the battery or try another power supply, and use\n" + "shorter cables.\n" + "Make sure no electrical appliances turn on or off during the copy.\n" + "Re-run the test to be sure; it's recommended that you use a lower speed\n"); + free (buffer1); + free (buffer2); + exit (1); + } + } + + free (buffer1); + free (buffer2); + break; + // manual test + case 2: + cyan_reset (parport); + cyan_write_copier (CNHI, parport); + cyan_delay (0, parport); + cyan_write_copier (0, parport); + cyan_delay (0, parport); + + if (speed != DEFAULT_SPEED) + puts ("Ignoring specified speed; test bench mode does not require a speed setting"); + // print screen + puts ("Entering manual test bench mode\n" + "\n" + "Probe the board and verify that the counters are being clocked, and are\n" + "counting correctly. The upper counter should be one count ahead of the lower\n" + "counter, with both clocked at the same rate.\n" + "Inject logic levels into the multiplexers to verify that the data bits are\n" + "being read correctly:\n" + "*=high .=low, layout: L H L H L H L H (L=low multiplexer, H=high multiplexer)\n" + "NOTE: The signals in question are the chip native signals; D0 below corresponds\n" + "to D0 on the multiplexer, NOT D0 on the cartridge port. Likewise with the\n" + "address lines. The input lines are in counter order, left to right.\n" + "Press escape or q to exit; be sure to turn the ROM copier off immediately after\n" + "exiting, to reset the device.\n" + "\n" + "If the above didn't make any sense to you, press escape or q and turn the ROM\n" + "copier off immediately!\n" + "This test is designed for advanced users only\n"); + while (1) + { + const char *status[2] = {"* ", ". "}; + + fputc ('\r', stdout); + + cyan_write_copier (0, parport); + cyan_delay (1, parport); + count = cyan_read_copier (parport); + fputs (status[((count ^ INLO) >> 6) & 1], stdout); + fputs (status[((count ^ INHI) >> 4) & 1], stdout); + + cyan_write_copier (CNLO | CNHI, parport); + cyan_delay (1, parport); + count = cyan_read_copier (parport); + fputs (status[((count ^ INLO) >> 6) & 1], stdout); + fputs (status[((count ^ INHI) >> 4) & 1], stdout); + + cyan_write_copier (0, parport); + cyan_delay (1, parport); + count = cyan_read_copier (parport); + fputs (status[((count ^ INLO) >> 6) & 1], stdout); + fputs (status[((count ^ INHI) >> 4) & 1], stdout); + + cyan_write_copier (CNLO | CNHI, parport); + cyan_delay (1, parport); + count = cyan_read_copier (parport); + fputs (status[((count ^ INLO) >> 6) & 1], stdout); + fputs (status[((count ^ INHI) >> 4) & 1], stdout); + + cyan_write_copier (0, parport); + cyan_delay (1, parport); + count = cyan_read_copier (parport); + fputs (status[((count ^ INLO) >> 6) & 1], stdout); + fputs (status[((count ^ INHI) >> 4) & 1], stdout); + + cyan_write_copier (CNLO | CNHI, parport); + cyan_delay (1, parport); + count = cyan_read_copier (parport); + fputs (status[((count ^ INLO) >> 6) & 1], stdout); + fputs (status[((count ^ INHI) >> 4) & 1], stdout); + + cyan_write_copier (0, parport); + cyan_delay (1, parport); + count = cyan_read_copier (parport); + fputs (status[((count ^ INLO) >> 6) & 1], stdout); + fputs (status[((count ^ INHI) >> 4) & 1], stdout); + + cyan_write_copier (CNLO | CNHI, parport); + cyan_delay (1, parport); + count = cyan_read_copier (parport); + fputs (status[((count ^ INLO) >> 6) & 1], stdout); + fputs (status[((count ^ INHI) >> 4) & 1], stdout); + + cyan_write_copier (0, parport); + cyan_delay (1, parport); + fflush (stdout); + + if (check_exit ()) + { + cyan_reset (parport); + puts ("\nUser aborted test"); + exit (0); + } + } + break; + default: // cmc_test() should only pass a correct speed value + fputs ("INTERNAL ERROR: Invalid test number passed to cyan_test_copier()\n", stderr); + exit (1); + } + return 0; +} + + +static int +cyan_copy_rom (const char *filename, int speed, unsigned int parport) +/* + Copy a ROM file -- this assumes the filename is valid and the file does not + already exist, since it will blindly try to write (overwrite) the filename you + give it. + If the open failed due to an invalid filename or path, it prints an error. + Speed setting should be between 1-4, 3 is default, and this is verified. +*/ +{ + unsigned long romsize; + unsigned char *buffer; + FILE *f; + + fputs (INTRO_TEXT, stdout); + parport_print_info (); + + if (!strlen (filename)) + { + fputs ("ERROR: Filename not specified\n" + " You must specify a filename on the commandline, as follows:\n" + " ucon64 " OPTION_LONG_S "xcmc dump.bin\n", stderr); + exit (1); + } + + printf ("Speed %d selected\n", speed); + cyan_test_parport (parport); + printf ("Destination file: %s\n", filename); + + if ((f = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + fclose (f); + + puts ("NOTE: Dumping copier's full address space (file will be automatically trimmed\n" + " after dumping)\n" + "Press escape or q to abort\n"); + + buffer = cyan_read_rom (speed, parport, NULL); + if (!buffer) + { + remove (filename); + exit(0); + } + + fputc ('\n', stdout); + romsize = cyan_calculate_rom_size (buffer, 0); + + fputs ("Writing ROM to disk... ", stdout); + fflush (stdout); + if ((f = fopen (filename, "wb")) == NULL) + { + puts ("FAILED"); + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + free (buffer); + exit (1); + } + if (fwrite (buffer, 1, romsize, f) != romsize) + { + puts ("FAILED"); + fprintf (stderr, ucon64_msg[WRITE_ERROR], filename); + free (buffer); + fclose (f); + exit (1); + } + fclose (f); + printf ("%d kBytes OK\n" + "Verifying checksum... ", (int) (romsize / 1024)); + fflush (stdout); + + if (cyan_checksum_rom (buffer)) + { + puts ("FAILED\n" + "WARNING: Checksum of ROM does not appear to be correct.\n" + " This may be normal for this ROM, or it may indicate a bad copy.\n" + " Please verify the ROM, and consider running a copier test"); + } + else + puts ("OK"); + + puts ("Copy complete!\n" + "Don't forget to turn the ROM copier off, and never insert or remove a\n" + "cartridge with the power on"); + + free (buffer); + return 0; + +} + +#endif // USE_PARALLEL + + +/******************* + * uCON64 wrapping * + *******************/ + +const st_getopt2_t cmc_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Cyan's Megadrive ROM copier"/*"1999-2004 Cyan Helkaraxe"*/, + NULL + }, +#ifdef USE_PARALLEL + { + "xcmc", 0, 0, UCON64_XCMC, + NULL, "receive ROM from Cyan's Megadrive ROM copier; " OPTION_LONG_S "port=PORT", + &ucon64_wf[WF_OBJ_GEN_STOP_NO_ROM] + }, + { + "xcmct", 1, 0, UCON64_XCMCT, + "TEST", "run test TEST\n" + "TEST=1 burn-in reliability test (specify speed)\n" + "TEST=2 testbench mode (experts only)", + &ucon64_wf[WF_OBJ_GEN_STOP_NO_ROM] + }, + { + "xcmcm", 1, 0, UCON64_XCMCM, + "SPEED", "specify transfer speed\n" + "SPEED=1 slow (debug)\n" + "SPEED=2 medium\n" + "SPEED=3 fast (default)\n" // verify with value of DEFAULT_SPEED + "SPEED=4 full speed (risky)", + &ucon64_wf[WF_OBJ_GEN_SWITCH] + }, +#endif // USE_PARALLEL + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +#ifdef USE_PARALLEL + +int +cmc_read_rom (const char *filename, unsigned int parport, int speed) +{ +#if (defined __unix__ || defined __BEOS__) && !defined __MSDOS__ + init_conio (); +#endif + + if (speed < 1 || speed > 4) + speed = DEFAULT_SPEED; + cyan_copy_rom (filename, speed, parport); + +#if (defined __unix__ || defined __BEOS__) && !defined __MSDOS__ + deinit_conio (); +#endif + + return 0; +} + + +int +cmc_test (int test, unsigned int parport, int speed) +{ +#if (defined __unix__ || defined __BEOS__) && !defined __MSDOS__ + init_conio (); +#endif + + if (test < 1 || test > 2) + { + fputs ("ERROR: Choose a test between 1 and 2 (inclusive)\n", stderr); + exit (1); + } + if (speed < 1 || speed > 4) + speed = DEFAULT_SPEED; + cyan_test_copier (test, speed, parport); + +#if (defined __unix__ || defined __BEOS__) && !defined __MSDOS__ + deinit_conio (); +#endif + + return 0; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/cmc.h b/ucon64/2.0/src/backup/cmc.h new file mode 100644 index 0000000..23eed34 --- /dev/null +++ b/ucon64/2.0/src/backup/cmc.h @@ -0,0 +1,32 @@ +/* +cmc.h - Cyan's Megadrive ROM copier support for uCON64 + +Copyright (c) 1999-2004 Cyan Helkaraxe + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +// See cmc.c for important information +#ifndef CMC_H +#define CMC_H + +extern const st_getopt2_t cmc_usage[]; + +#ifdef USE_PARALLEL +extern int cmc_read_rom (const char *filename, unsigned int parport, int speed); +extern int cmc_test (int test, unsigned int parport, int speed); +#endif + +#endif diff --git a/ucon64/2.0/src/backup/dex.c b/ucon64/2.0/src/backup/dex.c new file mode 100644 index 0000000..fcfca41 --- /dev/null +++ b/ucon64/2.0/src/backup/dex.c @@ -0,0 +1,139 @@ +/* +dex.c - DexDrive support for uCON64 + +Copyright (c) 2002 NoisyB +Copyright (c) 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include "misc/misc.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "dex.h" +#include "psxpblib.h" +#include "misc/parallel.h" + + +const st_getopt2_t dex_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "DexDrive"/*"19XX InterAct http://www.dexdrive.de"*/, + NULL + }, + { + "xdex", 1, 0, UCON64_XDEX, + "N", "send/receive Block N to/from DexDrive; " OPTION_LONG_S "port=PORT\n" + "receives automatically when SRAM does not exist", + &ucon64_wf[WF_OBJ_ALL_DEFAULT_STOP_NO_ROM] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +#ifdef USE_PARALLEL + +#define CONPORT 1 +#define TAP 1 +#define DELAY 4 +#define FRAME_SIZE 128 +#define BLOCK_SIZE (64*FRAME_SIZE) + +static int print_data; + + +static unsigned char * +read_block (int block_num) +{ + return psx_memcard_read_block (print_data, CONPORT, TAP, DELAY, block_num); +} + + +static int +write_block (int block_num, unsigned char *data) +{ + return psx_memcard_write_block (print_data, CONPORT, TAP, DELAY, block_num, + data); +} + + +#if 0 +char * +read_frame (int frame, char *data) +{ + return psx_memcard_read_frame (print_data, CONPORT, TAP, DELAY, frame); +} + + +int +write_frame (int frame, char *data) +{ + return psx_memcard_write_frame (print_data, CONPORT, TAP, DELAY, frame, + data); +} +#endif + + +int +dex_read_block (const char *filename, int block_num, unsigned int parport) +{ + unsigned char *data; + + print_data = parport; + parport_print_info (); + + if ((data = read_block (block_num)) == NULL) + { + fprintf (stderr, ucon64_msg[PARPORT_ERROR]); + exit (1); + } + + ucon64_fwrite (data, 0, BLOCK_SIZE, filename, "wb"); + + return 0; +} + + +int +dex_write_block (const char *filename, int block_num, unsigned int parport) +{ + unsigned char data[BLOCK_SIZE]; + + print_data = parport; + parport_print_info (); + + ucon64_fread (data, 0, BLOCK_SIZE, filename); + + if (write_block (block_num, data) == -1) + { + fprintf (stderr, ucon64_msg[PARPORT_ERROR]); + exit (1); + } + + return 0; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/dex.h b/ucon64/2.0/src/backup/dex.h new file mode 100644 index 0000000..babf816 --- /dev/null +++ b/ucon64/2.0/src/backup/dex.h @@ -0,0 +1,34 @@ +/* +dex.h - DexDrive support for uCON64 + +Copyright (c) 2002 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef DEX_H +#define DEX_H + +extern const st_getopt2_t dex_usage[]; + +#define DEX_HEADER_START 0 +#define DEX_HEADER_LEN 0 + +#ifdef USE_PARALLEL +extern int dex_read_block (const char *filename, int block_num, unsigned int parport); +extern int dex_write_block (const char *filename, int block_num, unsigned int parport); +#endif // USE_PARALLEL + +#endif diff --git a/ucon64/2.0/src/backup/doctor64.c b/ucon64/2.0/src/backup/doctor64.c new file mode 100644 index 0000000..6cce152 --- /dev/null +++ b/ucon64/2.0/src/backup/doctor64.c @@ -0,0 +1,399 @@ +/* +doctor64.c - Bung Doctor V64 support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "misc/misc.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "doctor64.h" +#include "misc/parallel.h" + + +const st_getopt2_t doctor64_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Doctor V64"/*"19XX Bung Enterprises Ltd http://www.bung.com.hk"*/, + NULL + }, +#ifdef USE_PARALLEL + { + "xv64", 0, 0, UCON64_XV64, + NULL, "send/receive ROM to/from Doctor V64; " OPTION_LONG_S "port=PORT\n" + "receives automatically when ROM does not exist", + &ucon64_wf[WF_OBJ_N64_DEFAULT_STOP_NO_ROM] + }, +#endif + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +#ifdef USE_PARALLEL + +#define SYNC_MAX_CNT 8192 +#define SYNC_MAX_TRY 32 +#define SEND_MAX_WAIT 0x300000 +#define REC_HIGH_NIBBLE 0x80 +#define REC_LOW_NIBBLE 0x00 +#define REC_MAX_WAIT SEND_MAX_WAIT + + +static int +parport_write (char src[], int len, unsigned int parport) +{ + int maxwait, i; + + for (i = 0; i < len; i++) + { + maxwait = SEND_MAX_WAIT; + if ((inportb ((unsigned short) (parport + 2)) & 1) == 0) // check ~strobe + { + while (((inportb ((unsigned short) (parport + 2)) & 2) != 0) && maxwait--) + ; // wait for + if (maxwait <= 0) + return 1; // auto feed == 0 + outportb ((unsigned short) parport, src[i]); // write data + outportb ((unsigned short) (parport + 2), 5); // ~strobe = 1 + } + else + { + while (((inportb ((unsigned short) (parport + 2)) & 2) == 0) && maxwait--) + ; // wait for + if (maxwait <= 0) + return 1; // auto feed == 1 + outportb ((unsigned short) parport, src[i]); // write data + outportb ((unsigned short) (parport + 2), 4); // ~strobe = 0 + } + } + return 0; +} + + +static int +parport_read (char dest[], int len, unsigned int parport) +{ + int i, maxwait; + unsigned char c; + + for (i = 0; i < len; i++) + { + outportb ((unsigned short) parport, REC_HIGH_NIBBLE); + maxwait = REC_MAX_WAIT; + while (((inportb ((unsigned short) (parport + 1)) & 0x80) == 0) && maxwait--) + ; // wait for ~busy=1 + if (maxwait <= 0) + return len - i; + c = (inportb ((unsigned short) (parport + 1)) >> 3) & 0x0f; // ~ack, pe, slct, ~error + + outportb ((unsigned short) parport, REC_LOW_NIBBLE); + maxwait = REC_MAX_WAIT; + while (((inportb ((unsigned short) (parport + 1)) & 0x80) != 0) && maxwait--) + ; // wait for ~busy=0 + if (maxwait <= 0) + return len - i; + c |= (inportb ((unsigned short) (parport + 1)) << 1) & 0xf0; // ~ack, pe, slct, ~error + + dest[i] = c; + } + outportb ((unsigned short) parport, REC_HIGH_NIBBLE); + return 0; +} + + +int +syncHeader (unsigned int baseport) +{ + int i = 0; + + outportb ((unsigned short) baseport, 0); // data = 00000000 + outportb ((unsigned short) (baseport + 2), 4); // ~strobe=0 + while (i < SYNC_MAX_CNT) + { + if ((inportb ((unsigned short) (baseport + 2)) & 8) == 0) // wait for select=0 + { + outportb ((unsigned short) (baseport), 0xaa); // data = 10101010 + outportb ((unsigned short) (baseport + 2), 0); // ~strobe=0, ~init=0 + while (i < SYNC_MAX_CNT) + { + if ((inportb ((unsigned short) (baseport + 2)) & 8) != 0) // wait for select=1 + { + outportb ((unsigned short) (baseport + 2), 4); // ~strobe=0 + while (i < SYNC_MAX_CNT) + { + if ((inportb ((unsigned short) (baseport + 2)) & 8) == 0) // w for select=0 + { + outportb ((unsigned short) baseport, 0x55); // data = 01010101 + outportb ((unsigned short) (baseport + 2), 0); // ~strobe=0, ~init=0 + while (i < SYNC_MAX_CNT) + { + if ((inportb ((unsigned short) (baseport + 2)) & 8) != 0) // w select=1 + { + outportb ((unsigned short) (baseport + 2), 4); // ~strobe=0 + while (i < SYNC_MAX_CNT) + { + if ((inportb ((unsigned short) (baseport + 2)) & 8) == 0) // select=0 + return 0; + i++; + } + } + i++; + } + } + i++; + } + } + i++; + } + i++; + } + i++; + } + outportb ((unsigned short) (baseport + 2), 4); + return 1; +} + + +int +initCommunication (unsigned int port) +{ + int i; + for (i = 0; i < SYNC_MAX_TRY; i++) + { + if (syncHeader (port) == 0) + break; + } + if (i >= SYNC_MAX_TRY) + return -1; + return 0; +} + + +int +checkSync (unsigned int baseport) +{ + int i, j; + + for (i = 0; i < SYNC_MAX_CNT; i++) + { + if (((inportb ((unsigned short) (baseport + 2)) & 3) == 3) + || ((inportb ((unsigned short) (baseport + 2)) & 3) == 0)) + { + outportb ((unsigned short) baseport, 0); // ~strobe, auto feed + for (j = 0; j < SYNC_MAX_CNT; j++) + { + if ((inportb ((unsigned short) (baseport + 1)) & 0x80) == 0) // wait for ~busy=0 + { + return 0; + } + } + return 1; + } + } + return 1; +} + + +int +sendFilename (unsigned int baseport, char name[]) +{ + int i; + char *c, mname[12]; + + memset (mname, ' ', 11); + c = (strrchr (name, FILE_SEPARATOR)); + if (c == NULL) + { + c = name; + } + else + { + c++; + } + for (i = 0; i < 8 && *c != '.' && *c != '\0'; i++, c++) + mname[i] = toupper (*c); + c = strrchr (c, '.'); + if (c != NULL) + { + c++; + for (i = 8; i < 11 && *c != '\0'; i++, c++) + mname[i] = toupper (*c); + } + + return parport_write (mname, 11, baseport); +} + + +int +sendUploadHeader (unsigned int baseport, char name[], int len) +{ + char mname[12], lenbuffer[4]; + static char protocolId[] = "GD6R\1"; + + if (parport_write (protocolId, strlen (protocolId), baseport) != 0) + return 1; + + lenbuffer[0] = (char) len; + lenbuffer[1] = (char) (len >> 8); + lenbuffer[2] = (char) (len >> 16); + lenbuffer[3] = (char) (len >> 24); + if (parport_write (lenbuffer, 4, baseport) != 0) + return 1; + + memset (mname, ' ', 11); + if (sendFilename (baseport, name) != 0) + return 1; + return 0; +} + + +int +sendDownloadHeader (unsigned int baseport, int *len) +{ + char mname[12]; + static char protocolId[] = "GD6W"; + unsigned char recbuffer[15]; + + if (parport_write (protocolId, strlen (protocolId), baseport) != 0) + return 1; + memset (mname, ' ', 11); + if (parport_write (mname, 11, baseport) != 0) + return 1; + if (checkSync (baseport) != 0) + return 1; + + if (parport_read ((char *) recbuffer, 1, baseport) != 0) + return 1; + if (recbuffer[0] != 1) + return -1; + if (parport_read ((char *) recbuffer, 15, baseport) != 0) + return 1; + *len = (int) recbuffer[0] | + ((int) recbuffer[1] << 8) | + ((int) recbuffer[2] << 16) | + ((int) recbuffer[3] << 24); + return 0; +} + + +int +doctor64_read (const char *filename, unsigned int parport) +{ + char buf[MAXBUFSIZE]; + FILE *fh; + int size, inittime, bytesreceived = 0; + + parport_print_info (); + if (initCommunication (parport) == -1) + { + fprintf (stderr, ucon64_msg[PARPORT_ERROR]); + exit (1); + } + + inittime = time (0); + + if (sendDownloadHeader (parport, &size) != 0) + { + fprintf (stderr, ucon64_msg[PARPORT_ERROR]); + exit (1); + } + if (!(fh = fopen (filename, "wb"))) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + + for (;;) + { + if (parport_read (buf, sizeof buf, parport) != 0) + { + fclose (fh); + return 0; + } + bytesreceived += sizeof buf; + fwrite (buf, 1, sizeof buf, fh); + ucon64_gauge (inittime, bytesreceived, size); + } + sync (); + fclose (fh); + return 0; +} + + +int +doctor64_write (const char *filename, int start, int len, unsigned int parport) +{ + char buf[MAXBUFSIZE]; + FILE *fh; + unsigned int size, inittime, pos, bytessend = 0; + + parport_print_info (); + size = len - start; + if (initCommunication (parport) == -1) + { + fprintf (stderr, ucon64_msg[PARPORT_ERROR]); + exit (1); + } + inittime = time (0); + + strcpy (buf, filename); + if (sendUploadHeader (parport, buf, size) != 0) + { + fprintf (stderr, ucon64_msg[PARPORT_ERROR]); + exit (1); + } + + if (!(fh = fopen (filename, "rb"))) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + + printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + + for (;;) + { + if (!(pos = fread (buf, 1, sizeof buf, fh))) + break; + if (parport_write (buf, pos, parport) != 0) + break; + bytessend += sizeof buf; + ucon64_gauge (inittime, bytessend, size); + } + fclose (fh); + return 0; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/doctor64.h b/ucon64/2.0/src/backup/doctor64.h new file mode 100644 index 0000000..c05cb1e --- /dev/null +++ b/ucon64/2.0/src/backup/doctor64.h @@ -0,0 +1,31 @@ +/* +doctor64.h - Bung Doctor V64 support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef DOCTOR64_H +#define DOCTOR64_H + +extern const st_getopt2_t doctor64_usage[]; + +#ifdef USE_PARALLEL +extern int doctor64_read (const char *filename, unsigned int parport); +extern int doctor64_write (const char *filename, int start, int len, unsigned int parport); +#endif + +#endif diff --git a/ucon64/2.0/src/backup/doctor64jr.c b/ucon64/2.0/src/backup/doctor64jr.c new file mode 100644 index 0000000..0a3d7c4 --- /dev/null +++ b/ucon64/2.0/src/backup/doctor64jr.c @@ -0,0 +1,587 @@ +/* +doctor64jr.c - Bung Doctor V64 Junior support for uCON64 + +Copyright (c) 1999 - 2002 NoisyB +Copyright (c) 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* +drjr transfer protocol + + +DB25 pin name +p2~p9 pd[7:0] XXXXXXXXXXX ai XXXXX data XXXX +p1 nwrite ~~~~~~~~~|_____________________|~~ +p14 ndstb ~~~~~~~~~~~~~~~~~~~~~~~~~|_|~~~~~~ +p17 nastb ~~~~~~~~~~~~~|_|~~~~~~~~~~~~~~~~~~ + + +ai[]=0 r/w a[7..0] +ai[]=1 r/w a[15..8] +ai[]=2 r/w a[23..16] +ai[]=3 w a[28..24] +ai[]=3 r (rst,wdf,wcf,a[28..24]) +ai[]=4 r/w data +ai[]=5 w mode +ai[]=6 w en_1 +ai[]=7 w en_0 +*remark + a[8..1] support page count up + + ai[3]d7:0=N64 power off, 1=N64 power on + d6:0=no dram data written, 1=dram data written + d5:0=no data write in b4000000~b7ffffff, 1=some data written in b4000000~b7ffffff + + mode d0:0=dram read only and clear wdf, 1=dram write enable + d1:0=disable cartridge read and clear wcf flag, + 1=enable cartridge read(write b4000000~b7ffffff will switch off dram and cartridge will present at b0000000~b3ffffff) + + en_0=05 and en_1=0a is enable port control + + +mode:q0 0 1 0 1 +mode:q1 0 0 1 1 +b7ff ffff +b400 0000 dram read only dram r/w cartridge read cartridge read(* write this area will switch off dram) + +b3ff ffff +b000 0000 dram read only dram r/w dram read only dram r/w + + +eg:enable port control + +DB25 pin name +p2~p9 pd[7:0] XXXXXXXXXXX 07 XX 05 XXXX 06 XX 0a XXXXXXXXXXXX +p1 nwrite ~~~~~~~~~|_____________________________|~~~~~~~ +p14 ndstb ~~~~~~~~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~~ +p17 nastb ~~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~~~~~~~~ + en_0=05 en_1=0a + + +eg:write adr $b0123456, data $a55a,$1234.. + +DB25 pin name +p2~p9 pd[7:0] XXXXXXXXXXX 00 XX 56 XXXX 01 XX 34 XXXX 02 XX 12 XXXX 03 XX b0 XXXXXX 04 XX 5a XX a5 XX 34 XX 12 XXXXXXXXXXX +p1 nwrite ~~~~~~~~~|_______________________________________________________________________________________|~~~~~~~~~~ +p14 ndstb ~~~~~~~~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~~~|_|~~~|_|~~~|_|~~~|_|~~~~~~~~~~~ +p17 nastb ~~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~~~|_|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + set adr word low set adr word high wdata a55a wdata 1234 (after write adr=b012345a) + + +eg:read adr $b0123400~$b01235ff, 512 data + +DB25 pin name +p2~p9 pd[7:0] XXXXXXXXXXX 00 XX 00 XXXX 01 XX 34 XXXX 02 XX 12 XXXX 03 XX b0 XXXXXX 04 XX data0 XX data1 X ... X data510 XX data511 XXXXX +p1 nwrite ~~~~~~~~~|________________________________________________________________~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +p14 ndstb ~~~~~~~~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~~~~|_|~~~~~~|_|~~~ ~~~ ~~~~|_|~~~~~~~~|_|~~~~~~~~ +p17 nastb ~~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~~~|_|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + set adr word low set adr word high (after 512 read adr=b0123400) + + +eg:dram write protect, disable N64 access to cartridge and disable port control + +DB25 pin name +p2~p9 pd[7:0] XXXXXXXXXXX 05 XX 00 XXXX 07 XX 00 XXXX 06 XX 00 XXXXXXXXXXXX +p1 nwrite ~~~~~~~~~|________________________________________|~~~~~~~~~~ +p14 ndstb ~~~~~~~~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~~ +p17 nastb ~~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~|_|~~~~~~~~~~~~~~~~~~ + mode=00 en_0=00 en_1=00 + + +simple backup rountine for N64 + +void writePI(unsigned long addr, unsigned long value) +{ + do {} while (*(volatile unsigned long *) (0xa4600010) & 3); // check parallel interface not busy + addr &=0xbffffffc; + *(unsigned long *)(addr)=value; +} + +unsigned long readPI(unsigned long addr) +{ + do {} while (*(volatile unsigned long *) (0xa4600010) & 3); // check parallel interface not busy + addr &=0xbffffffc; + return *(unsigned long *)(addr); +} + +// MAIN -- START OF USER CODE +void mainproc(void *arg) { + u32 base_adr; + for (base_adr=0;base_adr<0x1000000;base_adr++){ // backup 128Mbits + writePI(0xb0000000+base_adr,readPI(0xb4000000 + base_adr)); // write data + } +} +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include "misc/misc.h" +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "misc/parallel.h" +#include "misc/file.h" +#include "ucon64.h" +#include "ucon64_misc.h" +#include "doctor64jr.h" + + +const st_getopt2_t doctor64jr_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Doctor V64 Junior"/*"19XX Bung Enterprises Ltd http://www.bung.com.hk"*/, + NULL + }, +#ifdef USE_PARALLEL + { + "xdjr", 0, 0, UCON64_XDJR, + NULL, "send ROM to Doctor V64 Junior; " OPTION_LONG_S "port=PORT", + &ucon64_wf[WF_OBJ_N64_DEFAULT_STOP_NO_ROM] + }, +#if 0 + { + "xdjrs", 0, 0, UCON64_XDJRS, + NULL, "send/receive SRAM to/from Doctor V64 Junior; " OPTION_LONG_S "port=PORT\n" + "receives automatically when SRAM does not exist", + &ucon64_wf[WF_OBJ_N64_DEFAULT_STOP_NO_ROM] + }, +#endif +#endif // USE_PARALLEL + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +#ifdef USE_PARALLEL + +#define BUFFERSIZE 32768 +//#define set_ai_write outportb (port_a, 5); // ninit=1, nwrite=0 +#define set_data_write outportb (port_a, 1); // ninit=0, nwrite=0 +#define set_data_read outportb (port_a, 0); // ninit=0, nwrite=1 +//#define set_normal outportb (port_a, 4); // ninit=1, nwrite=1 + +static unsigned short int port_8, port_9, port_a, port_b, port_c, + *buffer; +static int wv_mode; + + +static void +set_ai (unsigned char ai) +{ + outportb (port_a, 5); // ninit=1, nwrite=0 + outportb (port_b, ai); +} + + +static void +set_ai_data (unsigned char ai, unsigned char data) +{ + set_ai (ai); + set_data_write // ninit=0, nwrite=0 + outportb (port_c, data); +} + + +static void +init_port (int enable_write) +{ +#ifndef USE_PPDEV + outportb (port_9, 1); // clear EPP time flag +#endif + set_ai_data (6, 0x0a); + set_ai_data (7, 0x05); // 6==0x0a, 7==0x05 is pc_control mode +// set_ai (5); +// set_data_read +// enable_write = inportb (port_c); + set_ai_data (5, (unsigned char) enable_write); // d0=0 is write protect mode +} + + +static void +end_port (int enable_write) +{ + set_ai_data (5, (unsigned char) enable_write); // d0=0 is write protect mode + set_ai_data (7, 0); // release pc mode + set_ai_data (6, 0); // 6==0x0a, 7==0x05 is pc_control mode + outportb (port_a, 4); // ninit=1, nwrite=1 +} + + +static int +check_card (void) +{ + set_ai_data (3, 0x12); + set_ai_data (2, 0x34); + set_ai_data (1, 0x56); + set_ai_data (0, 0x78); + + set_ai (3); + set_data_read // ninit=0, nwrite=1 + if ((inportb (port_c) & 0x1f) != 0x12) + return 1; + + set_ai (2); + set_data_read + if (inportb (port_c) != 0x34) + return 1; + + set_ai (1); + set_data_read + if (inportb (port_c) != 0x56) + return 1; + + set_ai (0); + set_data_read + if (inportb (port_c) != 0x78) + return 1; + + return 0; +} + + +static int +write_32k (unsigned short int hi_word, unsigned short int lo_word) +{ + unsigned char unpass, pass1; + unsigned short int i, j, fix; + + set_ai_data (3, (unsigned char) (0x10 | (hi_word >> 8))); + set_ai_data (2, (unsigned char) hi_word); + for (i = 0; i < 0x40; i++) + { + unpass = 3; + while (unpass) + { + set_ai_data (1, (unsigned char) ((i << 1) | lo_word)); + set_ai_data (0, 0); + set_ai (4); // set address index=4 + set_data_write // ninit=0, nwrite=0 + fix = i << 8; + for (j = 0; j < 256; j++) + outportw (port_c, buffer[j + fix]); + set_data_read // ninit=0, nwrite=1 + if (wv_mode) + { + for (j = 0; j < 256; j++) + if (inportw (port_c) != buffer[j + fix]) + break; + } + else + { + pass1 = 1; + for (j = 0; j < 4; j++) + if (inportw (port_c) != buffer[j + fix]) + { + pass1 = 0; + break; + } + if (pass1) + { + set_ai_data (1, (unsigned char) ((i << 1) | lo_word | 1)); + set_ai_data (0, 0xf8); + set_ai (4); + set_data_read // ninit=0, nwrite=1 + for (j = 252; j < 256; j++) + if (inportw (port_c) != buffer[j + fix]) + break; + } + } + set_ai (0); + set_data_read // ninit=0, nwrite=1 + if (inportb (port_c) != 0) + { + unpass--; + outportb (port_a, 0x0b); // set all pin=0 for debug + set_ai_data (3, (unsigned char) (0x10 | (hi_word >> 8))); + set_ai_data (2, (unsigned char) hi_word); + if (unpass == 0) + return 1; + } + else + unpass = 0; + } + } + +/* + outportb (ai, 0); + printf ("\na[7..0]=%02x\n", inportb (data)); + outportb (ai, 1); + printf ("a[15..8]=%02x\n", inportb (data)); +*/ + return 0; +} + + +#if 0 // not used +static int +verify_32k (unsigned short int hi_word, unsigned short int lo_word) +{ + char unpass; + unsigned short int i, j, fix; + + set_ai_data (3, (unsigned char) (0x10 | (hi_word >> 8))); + set_ai_data (2, (unsigned char) hi_word); + for (i = 0; i < 0x40; i++) + { + unpass = 3; + while (unpass) + { + set_ai_data (1, (unsigned char) ((i << 1) | lo_word)); + set_ai_data (0, 0); + set_ai (4); + set_data_read // ninit=0, nwrite=1 + fix = i << 8; + for (j = 0; j < 256; j++) + { + if (inportw (port_c) != buffer[j + fix]) + { + outportb (port_a, 0x0b); // all pin=0 for debug + set_ai_data (3, (unsigned char) (0x10 | (hi_word >> 8))); + set_ai_data (2, (unsigned char) hi_word); + unpass--; + if (unpass == 0) + return 1; + else + break; + } + } + if (j == 256) + break; + } + } + +/* + outportb (ai,0); + printf ("\na[7..0]=%02x\n", inportb (data)); + outportb (ai, 1); + printf ("a[15..8]=%02x\n", inportb (data)); +*/ + return 0; +} + + +static void +gen_pat_32k (unsigned short int offset) +{ + int i; + + for (i = 0; i < 0x4000; i++) + buffer[i] = i + offset; +} + + +static unsigned short int +test_dram (void) +{ + int n_pages = 0, page; + + gen_pat_32k (0x0000); + write_32k (0, 0); + + gen_pat_32k (0x8000); + write_32k (0x100, 0); + + gen_pat_32k (0x0000); + if (verify_32k (0, 0) == 0) // find lower 128 Mbits + n_pages = 0x100; + gen_pat_32k (0x8000); + if (verify_32k (0x100, 0) == 0) // find upper 128 Mbits + n_pages = 0x200; + + printf ("Testing DRAM...\n"); + + for (page = 0; page < n_pages; page++) + { + gen_pat_32k ((unsigned short int) (page * 2)); + if (write_32k (page, 0)) + return 0; + else + { + fputc ('w', stdout); + fflush (stdout); + } + + gen_pat_32k ((unsigned short int) (page * 2 + 1)); + if (write_32k (page, 0x80)) + return 0; + else + { + fputc ('w', stdout); + fflush (stdout); + } + } + + fputc ('\n', stdout); + for (page = 0; page < n_pages; page++) + { + gen_pat_32k ((unsigned short int) (page * 2)); + if (verify_32k (page, 0)) + return 0; + else + { + fputc ('v', stdout); + fflush (stdout); + } + gen_pat_32k ((unsigned short int) (page * 2 + 1)); + if (verify_32k (page, 0x80)) + return 0; + else + { + fputc ('v', stdout); + fflush (stdout); + } + } + + return n_pages; +} +#endif + + +static unsigned long int +get_address (void) +{ + unsigned long int address; + + set_ai_data (6, 0x0a); // enable pc mode + set_ai_data (7, 0x05); // enable pc mode + + set_ai (3); + set_data_read // ninit=0, nwrite=1 + address = inportb (port_c) << 24; + + set_ai (2); + set_data_read + address |= inportb (port_c) << 16; + + set_ai (1); + set_data_read + address |= inportb (port_c) << 8; + + set_ai (0); + set_data_read + address |= inportb (port_c); + + return address; +} + + +int +doctor64jr_read (const char *filename, unsigned int parport) +{ + (void) filename; + (void) parport; + return fprintf (stderr, "ERROR: The function for dumping a cartridge is not yet implemented for the\n" + " Doctor V64 Junior\n"); +} + + +int +doctor64jr_write (const char *filename, unsigned int parport) +{ + unsigned int enable_write = 0, init_time, size, bytesread, bytessend = 0, + n_pages; + unsigned short int page; + FILE *file; + + parport_print_info (); + + port_8 = parport; + port_9 = parport + 1; + port_a = parport + 2; + port_b = parport + 3; + port_c = parport + 4; + + init_port (enable_write); + + if (check_card () != 0) + { + fprintf (stderr, "ERROR: No Doctor V64 Junior card present\n"); + end_port (enable_write); + exit (1); + } + + wv_mode = 0; + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned short int *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + size = fsizeof (filename); + printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + +#if 0 + if (dram_test) + { + dram_size = test_dram (); + if (dram_size) + printf ("\nDRAM size=%dMbits\n", (dram_size / 2)); + else + fprintf (stderr, "\nERROR: DRAM test failed\n"); + return 0; + } +#endif + + n_pages = (size + (64 * 1024 - 1)) / (64 * 1024); // "+ (64 * 1024 - 1)" to round up + init_time = time (0); + for (page = 0; page < n_pages; page++) + { + bytesread = fread ((unsigned char *) buffer, 1, BUFFERSIZE, file); + if (write_32k (page, 0)) + { + fprintf (stderr, "ERROR: Transfer failed at address 0x%8lx", get_address ()); + break; + } + + bytesread += fread ((unsigned char *) buffer, 1, BUFFERSIZE, file); + if (write_32k (page, 0x80)) + { + fprintf (stderr, "ERROR: Transfer failed at address 0x%8lx", get_address ()); + break; + } + + bytessend += bytesread; + ucon64_gauge (init_time, bytessend, size); + } + fputc ('\n', stdout); + + if (enable_write) // 1 or 3 + printf ("DRAM write protect disabled\n"); + if (enable_write & 2) // 3 + printf ("Run cartridge enabled\n"); + +// set_ai_data(5, enable_write); // d0=0 is write protect mode + end_port (enable_write); + + return 0; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/doctor64jr.h b/ucon64/2.0/src/backup/doctor64jr.h new file mode 100644 index 0000000..1444332 --- /dev/null +++ b/ucon64/2.0/src/backup/doctor64jr.h @@ -0,0 +1,31 @@ +/* +doctor64jr.h - Bung Doctor V64 Junior support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef DOCTOR64JR_H +#define DOCTOR64JR_H + +extern const st_getopt2_t doctor64jr_usage[]; + +#ifdef USE_PARALLEL +extern int doctor64jr_read (const char *filename, unsigned int parport); +extern int doctor64jr_write (const char *filename, unsigned int parport); +#endif + +#endif diff --git a/ucon64/2.0/src/backup/f2a.c b/ucon64/2.0/src/backup/f2a.c new file mode 100644 index 0000000..bd64734 --- /dev/null +++ b/ucon64/2.0/src/backup/f2a.c @@ -0,0 +1,1637 @@ +/* +f2a.c - Flash 2 Advance support for uCON64 + +Copyright (c) 2003 Ulrich Hecht +Copyright (c) 2003 - 2004 David Voswinkel +Copyright (c) 2004 NoisyB +Copyright (c) 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#if defined _WIN32 || defined __MSDOS__ +#include +#else +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include "misc/bswap.h" +#include "misc/misc.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "f2a.h" +#include "console/gba.h" +#ifdef USE_USB +#include +#include +#include +#include "misc/usb.h" +#endif +#ifdef USE_PARALLEL +#include "misc/parallel.h" +#endif +#include "misc/property.h" + + +const st_getopt2_t f2a_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Flash 2 Advance (Ultra)"/*"2003 Flash2Advance http://www.flash2advance.com"*/, + NULL + }, +#if defined USE_PARALLEL || defined USE_USB + { + "xf2a", 0, 0, UCON64_XF2A, + NULL, "send/receive ROM to/from Flash 2 Advance (Ultra); " OPTION_LONG_S "port=PORT\n" + "receives automatically (32 Mbits) when ROM does not exist", + &ucon64_wf[WF_OBJ_GBA_DEFAULT_STOP_NO_ROM] + }, + { + "xf2amulti", 1, 0, UCON64_XF2AMULTI, // send only + "SIZE", "send multiple ROMs to Flash 2 Advance (Ultra); specify a\n" + "loader in the configuration file; " OPTION_LONG_S "port=PORT", + &ucon64_wf[WF_OBJ_GBA_DEFAULT_STOP] + }, + { + "xf2ac", 1, 0, UCON64_XF2AC, + "N", "receive N Mbits of ROM from Flash 2 Advance (Ultra);\n" OPTION_LONG_S "port=PORT", + &ucon64_wf[WF_OBJ_GBA_STOP_NO_ROM] + }, + { + "xf2as", 0, 0, UCON64_XF2AS, + NULL, "send/receive SRAM to/from Flash 2 Advance (Ultra); " OPTION_LONG_S "port=PORT\n" + "receives automatically when SRAM does not exist", + &ucon64_wf[WF_OBJ_GBA_STOP_NO_ROM] + }, + { + "xf2ab", 1, 0, UCON64_XF2AB, + "BANK", "send/receive SRAM to/from Flash 2 Advance (Ultra) BANK\n" + "BANK should be a number >= 1; " OPTION_LONG_S "port=PORT\n" + "receives automatically when SRAM does not exist", + &ucon64_wf[WF_OBJ_GBA_STOP_NO_ROM] + }, +#endif + {NULL, 0, 0, 0, NULL, NULL, NULL} +}; + + +#ifdef USE_USB + +#define EZDEV "/proc/ezusb/dev0" +#define F2A_FIRM_SIZE 23053 +#define CMD_GETINF 0x05 // get info on the system status +#define CMD_MULTIBOOT1 0xff // boot up the GBA stage 1, no parameters +#define CMD_MULTIBOOT2 0 // boot up the GBA stage 2, f2a_sendmsg_t.size has to be set + +#define SENDMSG_SIZE 63 // (sizeof (f2a_sendmsg_t) - 1) + +typedef struct +{ + unsigned int command; // command to execute, see below + unsigned int size; // size of data block to read/write + unsigned int pad1[2]; + unsigned int magic; // magic number, see below + unsigned int pad2[3]; + unsigned int unknown; // no idea what this is for, seems to have to be 0xa for write, 0x7 for read + unsigned int address; // base address for read/write + unsigned int sizekb; // size of data block to read/write in kB + unsigned char pad3[5 * 4 /*-1*/]; + /* + For some reason the original software uses a 63 bytes structure for outgoing + messages, not 64 as it does for incoming messages, hence the "-1". It all + seems to work fine with 64 bytes, too, and I therefore suspect this to be a + bug in the original software. + */ + // we use SENDMSG_SIZE to solve the problem - dbjh +} /*__attribute__ ((packed))*/ f2a_sendmsg_t; + +typedef struct +{ + unsigned char data[64]; +} f2a_recvmsg_t; + +static int f2a_init_usb (void); +static int f2a_connect_usb (void); +static int f2a_info (f2a_recvmsg_t *rm); +static int f2a_boot_usb (const char *ilclient_fname); +static int f2a_read_usb (int address, int size, const char *filename); +static int f2a_write_usb (int n_files, char **files, int address); + +static usb_dev_handle *f2a_handle; +#endif // USE_USB + + +#ifdef USE_PARALLEL + +#define LOGO_ADDR 0x06000000 +#define EXEC_STUB 0x03002000 +#define ERASE_STUB 0x03000c00 +#define LOGO_SIZE 76800 +#define BOOT_SIZE 18432 + +#define FLIP 1 +#define HEAD 1 +#define EXEC 1 + +#define PP_CMD_WRITEROM 0x0a +#define PP_CMD_ERASE 0x0b +#define PP_HEAD_BOOT 0x01 + +typedef struct +{ + unsigned int pad1[3]; + unsigned int magic; + unsigned int command; + unsigned int address; + unsigned int sizekb; + unsigned int pad2; + unsigned int exec; + unsigned int exec_stub; + unsigned char pad3[984]; +} /*__attribute__ ((packed))*/ f2a_msg_cmd_t; // packed attribute is not necessary + +static int f2a_boot_par (const char *ilclient2_fname, const char *illogo_fname); +static int f2a_write_par (int n_files, char **files, unsigned int address); +static int f2a_read_par (unsigned int start, unsigned int size, + const char *filename); +//static int f2a_erase_par (unsigned int start, unsigned int size); +static int f2a_send_buffer_par (int cmd, int address, + int size, const unsigned char *resource, int head, + int flip, unsigned int exec, int mode); +static int f2a_send_cmd_par (int cmd, int address, int size); +static int f2a_exec_cmd_par (int cmd, int address, int size); +static int f2a_receive_data_par (int cmd, int address, int size, + const char *filename, int flip); +static int f2a_send_head_par (int cmd, int size); +static int f2a_send_raw_par (unsigned char *buffer, int len); +static int f2a_receive_raw_par (unsigned char *buffer, int len); +static int f2a_wait_par (); +static int parport_init (int port, int target_delay); +static int parport_init_delay (int target); +static void parport_out31 (unsigned char val); +static void parport_out91 (unsigned char val); +static void parport_nop (); + +static int f2a_pport; +#ifdef DEBUG +static int parport_debug = 1; +#else +static int parport_debug = 0; +#endif +static int parport_nop_cntr; +#endif // USE_PARALLEL + + +#if defined USE_PARALLEL || defined USE_USB + +#define LOADER_SIZE 32768 + +#define CMD_WRITEDATA 0x06 // write data to RAM/ROM(USB)/SRAM +#define CMD_READDATA 0x07 // read data from RAM/ROM(USB)/SRAM +#define MAGIC_NUMBER 0xa46e5b91 // needs to be properly set for almost all commands + +enum +{ + UPLOAD_FAILED = 0, + CANNOT_GET_FILE_SIZE, + UPLOAD_FILE +}; + +static time_t starttime = 0; +static const char *f2a_msg[] = { + "ERROR: Upload failed\n", + "ERROR: Can't determine size of file \"%s\"\n", + "Uploading %s, %d kB, padded to %d kB\n", + NULL +}; +#endif + + +#ifdef USE_USB + +int +exec (const char *program, int argc, ...) +/* + This function is needed in order to execute a program with the same + permissions as we have. In this way a suid root uCON64 executable can execute + programs that require root privileges. We cannot use system(), because it + might drop privileges. + argc should be the number of _arguments_ (excluding the program name itself). + DON'T move this function to misc.c. It's only necessary under Linux 2.5 (and + later) for the USB version of the F2A. It's hard to be more system dependent + than that. - dbjh +*/ +{ + va_list argptr; + int status; + + argc++; + va_start (argptr, argc); + if (fork () == 0) + { + int n, size; + char *argv[argc + 1], *arg; + + size = strlen (program) + 1; + if ((argv[0] = (char *) malloc (size)) == NULL) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], size); + exit (1); + } + strcpy (argv[0], program); + + for (n = 1; n < argc; n++) + { + arg = (char *) va_arg (argptr, char *); // get next argument + size = strlen (arg) + 1; + if ((argv[n] = (char *) malloc (size)) == NULL) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], size); + exit (1); + } + strcpy (argv[n], arg); + } + argv[n] = 0; + + setuid (geteuid ()); + setgid (getegid ()); + if (execv (argv[0], (char **) argv)) + { // error in info page: return value can be != 1 +// fprintf (stderr, "ERROR: %s\n", strerror (errno)); + exit (1); + } + } + wait (&status); + va_end (argptr); + + return status; +} + + +static int +f2a_init_usb (void) +{ + f2a_recvmsg_t rm; + char iclientu_fname[FILENAME_MAX]; + + if (sizeof (f2a_recvmsg_t) != 64) + { + fprintf (stderr, "ERROR: The size of f2a_recvmsg_t is not 64 bytes.\n" + " Please correct the source code or send a bug report\n"); + exit (1); + } + if (sizeof (f2a_sendmsg_t) != 64) + { + fprintf (stderr, "ERROR: The size of f2a_sendmsg_t is not 64 bytes.\n" + " Please correct the source code or send a bug report\n"); + exit (1); + } + + memset (&rm, 0, sizeof (rm)); + + if (f2a_connect_usb ()) + { + fprintf (stderr, "ERROR: Could not connect to F2A USB linker\n"); + exit (1); // fatal + } + f2a_info (&rm); + if (rm.data[0] == 0) + { + get_property_fname (ucon64.configfile, "iclientu", iclientu_fname, "iclientu.bin"); + if (f2a_boot_usb (iclientu_fname)) + { + fprintf (stderr, "ERROR: Booting GBA client binary was not successful\n"); + exit (1); // fatal + } + f2a_info (&rm); + } + return 0; +} + + +static int +f2a_connect_usb (void) +{ + int fp, result, firmware_loaded = 0; + unsigned char f2afirmware[F2A_FIRM_SIZE]; + char f2afirmware_fname[FILENAME_MAX]; + struct usb_bus *bus; + struct usb_device *dev, *f2adev = NULL; + + get_property_fname (ucon64.configfile, "f2afirmware", f2afirmware_fname, "f2afirm.hex"); + if (ucon64_fread (f2afirmware, 0, F2A_FIRM_SIZE, f2afirmware_fname) <= 0) + { + fprintf (stderr, "ERROR: Could not load F2A firmware (%s)\n", f2afirmware_fname); + exit (1); // fatal + } + + usb_init (); + usb_find_busses (); + usb_find_devices (); + for (bus = usb_busses; bus; bus = bus->next) // usb_busses is present in libusb + { + for (dev = bus->devices; dev; dev = dev->next) + { + if (dev->descriptor.idVendor == 0x547 && dev->descriptor.idProduct == 0x2131) + { + struct utsname info; + int version; + + if (uname (&info) == -1) + { + fputs ("ERROR: Could not determine version of the running kernel\n", stderr); + return -1; + } + + version = strtol (&info.release[0], NULL, 10) * 10 + + strtol (&info.release[2], NULL, 10); + // example contents of info.release: "2.4.18-14custom" + if (version >= 25) // Linux kernel 2.5 or later + { + // use fxload to upload the F2A firmware + char device_path[160]; + int exitstatus; + + snprintf (device_path, 160, "/proc/bus/usb/%s/%s", + bus->dirname, dev->filename); + exitstatus = exec ("/sbin/fxload", 7, "-D", device_path, "-I", + f2afirmware_fname, "-t", "an21", + ucon64.quiet < 0 ? "-vv" : "-v"); + if (WEXITSTATUS (exitstatus)) + { + char cmd[10 * 80]; + + snprintf (cmd, 10 * 80, ucon64.quiet < 0 ? + "/sbin/fxload -D %s -I %s -t an21 -vv" : + "/sbin/fxload -D %s -I %s -t an21 -v", + device_path, f2afirmware_fname); + fprintf (stderr, "ERROR: Could not upload EZUSB firmware using fxload. Command:\n" + " %s\n", cmd); + return -1; + } + } + else + { + int wrote, w; + + // Linux kernel version 2.4 or older (2.2.16 is supported by + // the EZUSB2131 driver). It is possible to use fxload under + // (later versions of?) Linux 2.4... + if ((fp = open (EZDEV, O_WRONLY)) == -1) + { + fprintf (stderr, "ERROR: Could not upload EZUSB firmware (opening " + EZDEV": %s)\n", strerror (errno)); + return -1; + } + + // The EZUSB2131 driver (version 1.0) only accepts one line of + // an Intel hex record file at a time... + for (wrote = 0; wrote < F2A_FIRM_SIZE; wrote += w) + { + if ((w = write (fp, f2afirmware + wrote, F2A_FIRM_SIZE - wrote)) == -1) + { + fprintf (stderr, "ERROR: Could not upload EZUSB firmware (writing " + EZDEV": %s)\n", strerror (errno)); + return -1; + } + if (ucon64.quiet < 0) + printf ("Wrote %d bytes (%d-%d of %d) to "EZDEV"\n", + w, wrote, wrote + w, F2A_FIRM_SIZE); + } + close (fp); + } + firmware_loaded = 1; + wait2 (2000); // give the EZUSB some time to renumerate + break; + } + } + if (firmware_loaded) + break; + } + + usb_find_devices (); + for (bus = usb_busses; bus; bus = bus->next) + { + for (dev = bus->devices; dev; dev = dev->next) + { + if (dev->descriptor.idVendor == 0x547 && dev->descriptor.idProduct == 0x1002) + { + f2adev = dev; + break; + } + } + if (f2adev) + break; + } + + if (f2adev == NULL) + { + fprintf (stderr, "ERROR: Could not find F2A attached to USB\n"); + return -1; + } + + f2a_handle = usbport_open (f2adev); + + result = usb_claim_interface (f2a_handle, 0x4); + if (result == -1) + { + fprintf (stderr, "ERROR: Could not claim USB interface\n" + " %s\n", usb_strerror ()); + return -1; + } + result = usb_claim_interface (f2a_handle, 0x83); + if (result == -1) + { + fprintf (stderr, "ERROR: Could not claim USB interface\n" + " %s\n", usb_strerror ()); + return -1; + } + + return 0; +} + + +static int +f2a_info (f2a_recvmsg_t *rm) +{ + f2a_sendmsg_t sm; + + memset (&sm, 0, sizeof (f2a_sendmsg_t)); + memset (rm, 0, sizeof (f2a_recvmsg_t)); + + sm.command = me2le_32 (CMD_GETINF); + + if (usbport_write (f2a_handle, (char *) &sm, SENDMSG_SIZE) == -1) + { + fprintf (stderr, "ERROR: Could not send info request\n"); + exit (1); + } + if (usbport_read (f2a_handle, (char *) rm, sizeof (f2a_recvmsg_t)) == -1) + { + fprintf (stderr, "ERROR: Did not receive info request\n"); + exit (1); + } + +#if 0 + { + unsigned int i; + for (i = 0; i < (sizeof (f2a_sendmsg_t) / 4); i++) + printf ("%-2x %08X\n", i, *(((unsigned int *) (&sm)) + i)); + + if (ucon64.quiet < 0) + { + printf ("info:"); + for (i = 0; i < (sizeof (f2a_sendmsg_t) / 4); i++) + printf (" %08X", *(((unsigned int *) (rm)) + i)); + fputc ('\n', stdout); + } + } +#endif + + return 0; +} + + +static int +f2a_boot_usb (const char *ilclient_fname) +{ + f2a_sendmsg_t sm; + unsigned int ack[16], i; + char ilclient[16 * 1024]; + + printf ("Booting GBA\n" + "Uploading iLinker client\n" + "Please turn OFF, then ON your GBA with SELECT and START held down\n"); + + if (ucon64_fread (ilclient, 0, 16 * 1024, ilclient_fname) <= 0) + { + fprintf (stderr, "ERROR: Could not load GBA client binary (%s)\n", ilclient_fname); + return -1; + } + + // boot the GBA + memset (&sm, 0, sizeof (f2a_sendmsg_t)); + sm.command = me2le_32 (CMD_MULTIBOOT1); + usbport_write (f2a_handle, (char *) &sm, SENDMSG_SIZE); + sm.command = me2le_32 (CMD_MULTIBOOT2); + sm.size = me2le_32 (16 * 1024); + usbport_write (f2a_handle, (char *) &sm, SENDMSG_SIZE); + + // send the multiboot image + if (usbport_write (f2a_handle, ilclient, 16 * 1024) == -1) + { + fprintf (stderr, f2a_msg[UPLOAD_FAILED]); + return -1; + } + + if (usbport_read (f2a_handle, (char *) ack, 16 * 4) == -1) + return -1; + + if (ucon64.quiet < 0) + { + printf ("post-boot:"); + for (i = 0; i < 16; i++) + printf (" %08X", ack[i]); + fputc ('\n', stdout); + } + + return 0; +} + + +static int +f2a_read_usb (int address, int size, const char *filename) +{ + FILE *file; + int i; + f2a_sendmsg_t sm; + char buffer[1024]; + + memset (&sm, 0, sizeof (f2a_sendmsg_t)); + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + //exit (1); for now, return, although registering usbport_close() is better + return -1; + } + + sm.command = me2le_32 (CMD_READDATA); + sm.magic = me2le_32 (MAGIC_NUMBER); + sm.unknown = me2le_32 (7); + sm.address = me2le_32 (address); + sm.size = me2le_32 (size); + sm.sizekb = me2le_32 (size / 1024); + if (usbport_write (f2a_handle, (char *) &sm, SENDMSG_SIZE) == -1) + return -1; + + for (i = 0; i < size; i += 1024) + { + if (usbport_read (f2a_handle, buffer, 1024) == -1) + { + fclose (file); + return -1; + } + if (!fwrite (buffer, 1024, 1, file)) // note order of arguments + { + fprintf (stderr, ucon64_msg[WRITE_ERROR], filename); + fclose (file); + return -1; // see comment for fopen() call + } + ucon64_gauge (starttime, i + 1024, size); + } + fclose (file); + return 0; +} + + +static int +f2a_write_usb (int n_files, char **files, int address) +{ + f2a_sendmsg_t sm; + int i, j, fsize, size, n, is_sram_data = address >= 0xe000000 ? 1 : 0; + char buffer[1024], loader_fname[FILENAME_MAX]; + unsigned char loader[LOADER_SIZE]; + FILE *file; + + // initialize command buffer + memset (&sm, 0, sizeof (f2a_sendmsg_t)); + sm.command = me2le_32 (CMD_WRITEDATA); + sm.magic = me2le_32 (MAGIC_NUMBER); + sm.unknown = me2le_32 (is_sram_data ? 0x06 : 0x0a); // SRAM => 0x06, ROM => 0x0a + + if (n_files > 1 && !is_sram_data) + { + printf ("Uploading multiloader\n"); + get_property_fname (ucon64.configfile, "gbaloader", loader_fname, "loader.bin"); + if (ucon64_fread (loader, 0, LOADER_SIZE, loader_fname) <= 0) + { + fprintf (stderr, "ERROR: Could not load loader binary (%s)\n", loader_fname); + return -1; + } +#if 0 // just use a correct loader file - dbjh + ((int *) loader)[0] = me2be_32 (0x2e0000ea); // start address +#endif + memcpy (loader + 4, gba_logodata, GBA_LOGODATA_LEN); // + 4 for start address + + sm.size = me2le_32 (LOADER_SIZE); + sm.address = me2le_32 (address); + sm.sizekb = me2le_32 (LOADER_SIZE / 1024); + + if (usbport_write (f2a_handle, (char *) &sm, SENDMSG_SIZE) == -1) + return -1; + + if (usbport_write (f2a_handle, (char *) loader, LOADER_SIZE) == -1) + { + fprintf (stderr, f2a_msg[UPLOAD_FAILED]); + return -1; + } + address += LOADER_SIZE; + } + for (j = 0; j < n_files; j++) + { + if ((fsize = fsizeof (files[j])) == -1) + { + fprintf (stderr, f2a_msg[CANNOT_GET_FILE_SIZE], files[j]); + return -1; + } + // Round up to 32 kB. FIXME: This has to be 128 kB for Turbo carts + size = fsize; + if (size & (32768 - 1)) + size += 32768; + size &= ~(32768 - 1); + printf (f2a_msg[UPLOAD_FILE], files[j], fsize / 1024, size / 1024); + + if ((file = fopen (files[j], "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], files[j]); + //exit (1); for now, return, although registering usbport_close() is better + return -1; + } + clearerr (file); + + sm.size = me2le_32 (size); + sm.address = me2le_32 (address); + sm.sizekb = me2le_32 (size / 1024); + + if (usbport_write (f2a_handle, (char *) &sm, SENDMSG_SIZE) == -1) + return -1; + + for (i = 0; i < size; i += 1024) + { + //printf ("writing chunk %d\n", i); + n = fread (buffer, 1, 1024, file); + memset (buffer + n, 0, 1024 - n); + if (ferror (file)) + { + fputc ('\n', stderr); + fprintf (stderr, ucon64_msg[READ_ERROR], files[j]); + fclose (file); + return -1; // see comment for fopen() call + } + if (usbport_write (f2a_handle, buffer, 1024) == -1) + return -1; + ucon64_gauge (starttime, i + 1024, size); + } + fputc ('\n', stdout); // start new gauge on new line + + fclose (file); + address += fsize; + } + + return 0; +} + +#endif // USE_USB + + +#ifdef USE_PARALLEL + +static int +f2a_init_par (int parport, int parport_delay) +{ + char iclientp_fname[FILENAME_MAX], ilogo_fname[FILENAME_MAX]; + + if (parport_init (parport, parport_delay)) + { + fprintf (stderr, "ERROR: Could not connect to F2A parport linker\n"); + exit (1); // fatal + } + + get_property_fname (ucon64.configfile, "iclientp", iclientp_fname, "iclientp.bin"); + get_property_fname (ucon64.configfile, "ilogo", ilogo_fname, ""); // "ilogo.bin" + if (f2a_boot_par (iclientp_fname, ilogo_fname)) + { + fprintf (stderr, "ERROR: Booting GBA client binary was not successful\n"); + exit (1); // fatal + } + return 0; +} + + +int +parport_init (int port, int target_delay) +{ + f2a_pport = port; + parport_nop_cntr = parport_init_delay (target_delay); + + parport_print_info (); + +#ifndef USE_PPDEV + outportb ((unsigned short) (f2a_pport + PARPORT_STATUS), 0x01); // clear EPP time flag +#endif + + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x04); + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01); + outportb ((unsigned short) (f2a_pport + PARPORT_DATA), 0x04); + + parport_out91 (0x47); + parport_out31 (0x02); + parport_out91 (0x12); + parport_out31 (0x01); + parport_out91 (0x34); + parport_out31 (0x00); + parport_out91 (0x56); + + // not parport_out31 (0x02), because extra write to control register + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x03); + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01); + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01); + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01); + outportb ((unsigned short) (f2a_pport + PARPORT_DATA), 0x02); + + // not parport_out91 (0x00), because no write to data register + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x09); + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01); + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x00); + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x00); + + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x02); + inportb ((unsigned short) (f2a_pport + PARPORT_STATUS)); + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x06); + inportb ((unsigned short) (f2a_pport + PARPORT_STATUS)); + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x00); + + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01); + outportb ((unsigned short) (f2a_pport + PARPORT_EADDRESS), 0x04); + outportb ((unsigned short) (f2a_pport + PARPORT_EDATA), 0x07); + + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01); + outportb ((unsigned short) (f2a_pport + PARPORT_EADDRESS), 0x02); + outportb ((unsigned short) (f2a_pport + PARPORT_EDATA), 0x12); + + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01); + outportb ((unsigned short) (f2a_pport + PARPORT_EADDRESS), 0x01); + outportb ((unsigned short) (f2a_pport + PARPORT_EDATA), 0x34); + + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01); + outportb ((unsigned short) (f2a_pport + PARPORT_EADDRESS), 0x00); + outportb ((unsigned short) (f2a_pport + PARPORT_EDATA), 0x56); + + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01); + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01); + outportb ((unsigned short) (f2a_pport + PARPORT_EADDRESS), 0x02); + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x00); + inportb ((unsigned short) (f2a_pport + PARPORT_EDATA)); + + return 0; +} + + +static int +parport_init_delay (int n_micros) +/* + We only have millisecond accuracy on DOS, while we have to determine the + correct initial loop counter value for a number of microseconds. Luckily, the + function of time against the initial loop counter value is linear (provided + that the initial counter value is large enough), so we can just divide the + found loop value by 1000. Of course, in reality we don't get millisecond + accuracy... + TODO: Find the equivalent of gettimeofday() for MinGW and Visual C++ +*/ +{ +#define N_CHECKS 10 +#define N_HITSMAX 10 +#if defined _WIN32 || defined __MSDOS__ + struct timeb t0, t1; +#else + struct timeval t0, t1; +#endif + int n_ticks = 0, n, n_hits = 0, loop = 10000, loop_sum = 0; + volatile int m; // volatile is necessary for Visual C++... + + printf ("Determining delay loop value for %d microseconds...", n_micros); + fflush (stdout); + while (n_hits < N_HITSMAX) + { + n_ticks = 0; + for (n = 0; n < N_CHECKS; n++) + { + m = loop; +#if defined _WIN32 || defined __MSDOS__ + ftime (&t0); +#else + gettimeofday (&t0, NULL); +#endif + while (m--) + ; +#if defined _WIN32 || defined __MSDOS__ + ftime (&t1); + n_ticks += (t1.time * 1000 + t1.millitm) - (t0.time * 1000 + t0.millitm); +#else + gettimeofday (&t1, NULL); + n_ticks += (t1.tv_sec * 1000000 + t1.tv_usec) - (t0.tv_sec * 1000000 + t0.tv_usec); +#endif + } + n_ticks /= N_CHECKS; + +#ifndef DJGPP + if (n_ticks - n_micros == 0) // we are aiming at microsecond accuracy... +#else // DJGPP's runtime system is quite inaccurate under Windows XP + n = n_ticks - n_micros; + if (n < 0) + n = -n; + if (n <= 1) // allow a deviation of 1 ms? +#endif + { + n_hits++; + loop_sum += loop; + loop -= loop >> 3; // force "variation" in hope of better accuracy + continue; + } + + if (n_ticks == 0) + loop <<= 1; + else + loop = (int) (n_micros / ((float) n_ticks / loop)); + } + +#if defined _WIN32 || defined __MSDOS__ + n = loop_sum / (1000 * N_HITSMAX); // divide by 1000 +#else + n = loop_sum / N_HITSMAX; // we summed N_HITSMAX loop values +#endif + printf ("done (%d)\n", n); + return n; +} + + +static void +parport_nop () +{ + volatile int i = parport_nop_cntr; // volatile is necessary for Visual C++... + while (i--) + ; +} + + +static void +parport_out31 (unsigned char val) +{ + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x03); + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01); + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01); + outportb ((unsigned short) (f2a_pport + PARPORT_DATA), val); +} + + +static void +parport_out91 (unsigned char val) +{ + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x09); + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x01); + outportb ((unsigned short) (f2a_pport + PARPORT_DATA), val); +} + + +int +f2a_boot_par (const char *iclientp_fname, const char *ilogo_fname) +{ + unsigned char recv[4], iclientp[BOOT_SIZE]; + + printf ("Booting GBA\n" + "Please turn OFF, then ON your GBA with SELECT and START held down\n"); + + if (f2a_send_head_par (PP_HEAD_BOOT, 1)) + return -1; + if (f2a_receive_raw_par (recv, 4)) + return -1; + + if (ilogo_fname[0] != 0) + { + unsigned char ilogo[LOGO_SIZE]; + + printf ("Uploading iLinker logo\n"); + if (ucon64_fread (ilogo, 0, LOGO_SIZE, ilogo_fname) <= 0) + { + fprintf (stderr, "ERROR: Could not load logo file (%s)\n", ilogo_fname); + return -1; + } + if (f2a_send_buffer_par (CMD_WRITEDATA, LOGO_ADDR, LOGO_SIZE, ilogo, + 0, 0, 0, 0)) + { + fprintf (stderr, f2a_msg[UPLOAD_FAILED]); + return -1; + } + } + + printf ("Uploading iLinker client\n"); + if (ucon64_fread (iclientp, 0, BOOT_SIZE, iclientp_fname) <= 0) + { + fprintf (stderr, "ERROR: Could not load GBA client binary (%s)\n", iclientp_fname); + return -1; + } + if (f2a_send_buffer_par (CMD_WRITEDATA, EXEC_STUB, BOOT_SIZE, iclientp, + HEAD, FLIP, EXEC, 0)) + { + fprintf (stderr, f2a_msg[UPLOAD_FAILED]); + return -1; + } + return 0; +} + + +int +f2a_write_par (int n_files, char **files, unsigned int address) +{ + int j, fsize, size, is_sram_data = address >= 0xe000000 ? 1 : 0; + char loader_fname[FILENAME_MAX]; + unsigned char loader[LOADER_SIZE]; + + if (n_files > 1 && !is_sram_data) + { + printf ("Uploading multiloader\n"); + get_property_fname (ucon64.configfile, "gbaloader", loader_fname, "loader.bin"); + if (ucon64_fread (loader, 0, LOADER_SIZE, loader_fname) <= 0) + { + fprintf (stderr, "ERROR: Could not load loader binary (%s)\n", loader_fname); + return -1; + } +#if 0 // just use a correct loader file - dbjh + ((int *) loader)[0] = me2le_32 (0x2e0000ea); // start address +#endif + if (f2a_send_buffer_par (PP_CMD_WRITEROM, address, LOADER_SIZE, loader, + HEAD, FLIP, 0, 0)) + { + fprintf (stderr, f2a_msg[UPLOAD_FAILED]); + return -1; + } + address += LOADER_SIZE; + } + for (j = 0; j < n_files; j++) + { + if ((fsize = fsizeof (files[j])) == -1) + { + fprintf (stderr, f2a_msg[CANNOT_GET_FILE_SIZE], files[j]); + return -1; + } + size = fsize; + if (size & (32768 - 1)) + size += 32768; + size &= ~(32768 - 1); + printf (f2a_msg[UPLOAD_FILE], files[j], fsize / 1024, size / 1024); + if (f2a_send_buffer_par (PP_CMD_WRITEROM, address, size, + (unsigned char *) files[j], HEAD, FLIP, 0, 1)) + { + fprintf (stderr, f2a_msg[UPLOAD_FAILED]); + return -1; + } + + address += size; + } + return 0; +} + + +int +f2a_erase_par (unsigned int start, unsigned int size) +{ + int end, address; + + f2a_exec_cmd_par (CMD_READDATA, ERASE_STUB, 1024); + end = start + (size); + + printf ("Erase cart start=0x%08x end=0x%08x\n", start, end); + for (address = start; address < end; address += 0x40000) + f2a_send_cmd_par (PP_CMD_ERASE, address, 1024); + return 0; +} + + +int +f2a_read_par (unsigned int start, unsigned int size, const char *filename) +{ + f2a_exec_cmd_par (CMD_READDATA, ERASE_STUB, 1024); + printf ("Reading from cart start=0x%08x size=0x%08x\n", start, size); + f2a_receive_data_par (CMD_READDATA, start, size, filename, FLIP); + return 0; +} + + +#if 0 +typedef struct +{ + unsigned char header[16]; + unsigned char command; + unsigned char unknown; + unsigned int size; + unsigned char pad[58]; +} __attribute__ ((packed)) f2a_msg_head_t; +#endif + +static int +f2a_send_head_par (int cmd, int size) +{ + unsigned char trans[] = { 0xa, 0x8, 0xe, 0xc, 0x2, 0x0, 0x6, 0x4 }, + msg_header[80] = { 0x49, 0x2d, 0x4c, 0x69, 0x6e, 0x6b, 0x65, 0x72, + 0x2e, 0x31, 0x30, 0x30, 0x00, 0x00, 0x01, 0xe8 }; +// f2a_msg_head_t msg_header; // Don't use structs with misaligned + unsigned short int s; // members for data streams (we don't + // want compiler-specific stuff) +// memcpy (&msg_head, header, 16); // .head + msg_header[16] = cmd; // .command + s = size / 1024; + msg_header[17] = // .unknown + (trans[((s & 255) / 32)] << 4) | (((1023 - (s & 1023)) / 256) & 0x0f); +// msg_header.unknown = 0x82; + msg_header[18] = (unsigned char) s; // .size + msg_header[19] = (unsigned char) (s >> 8); + memset (&msg_header[20], 0, 80 - 20); + + if (f2a_send_raw_par (msg_header, 80)) + return -1; + return 0; +} + + +static int +f2a_exec_cmd_par (int cmd, int address, int size) +{ + unsigned char *buffer; + f2a_msg_cmd_t msg_cmd; + + memset (&msg_cmd, 0, sizeof (f2a_msg_cmd_t)); + msg_cmd.magic = me2be_32 (MAGIC_NUMBER); + msg_cmd.command = me2be_32 (cmd); + msg_cmd.address = me2be_32 (address); + msg_cmd.sizekb = me2be_32 (size / 1024); + + msg_cmd.exec_stub = me2be_32 (EXEC_STUB); + msg_cmd.exec = me2be_32 (0x08); + f2a_send_head_par (CMD_READDATA, size); + f2a_wait_par (); + + if (parport_debug) + fprintf (stderr, + "sending msg_cmd cmd='0x%08x' address='0x%08x' size='0x%08x' %d bytes\n", + msg_cmd.command, msg_cmd.address, msg_cmd.sizekb, + (int) sizeof (f2a_msg_cmd_t)); + + + f2a_send_raw_par ((unsigned char *) &msg_cmd, sizeof (f2a_msg_cmd_t)); +// f2a_wait_par (); + if ((buffer = (unsigned char *) malloc (size)) == NULL) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], size); + exit (1); // not return, caller doesn't handle it + } + f2a_receive_raw_par (buffer, size); + free (buffer); + + return 0; +} + + +static int +f2a_receive_data_par (int cmd, int address, int size, const char *filename, int flip) +{ + unsigned char buffer[1024], recv[4]; //, *mbuffer; + int i, j; + f2a_msg_cmd_t msg_cmd; + FILE *file; + + memset (&msg_cmd, 0, sizeof (f2a_msg_cmd_t)); + msg_cmd.magic = me2be_32 (MAGIC_NUMBER); + msg_cmd.command = me2be_32 (cmd); + msg_cmd.address = me2be_32 (address); + msg_cmd.sizekb = me2be_32 (size / 1024); + + if (f2a_send_head_par (CMD_READDATA, size)) + return -1; + + if (f2a_receive_raw_par (recv, 4)) + return -1; + + if (parport_debug) + fprintf (stderr, + "sending msg_cmd cmd='0x%08x' address='0x%08x' size='0x%08x' %d bytes\n", + msg_cmd.command, msg_cmd.address, msg_cmd.sizekb, + (int) sizeof (f2a_msg_cmd_t)); + + f2a_send_raw_par ((unsigned char *) &msg_cmd, sizeof (f2a_msg_cmd_t)); + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + //exit (1); return, because the other code does it too... + return -1; + } + +#if 0 + if ((mbuffer = (unsigned char *) malloc (size)) == NULL) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], size); + //exit (1); see comment for fopen() call + return -1; + } + f2a_receive_raw_par (mbuffer, size); + if (flip) + for (j = 0; j < size / 4; j++) + ((int *) mbuffer)[j] = bswap_32 (((int *) mbuffer)[j]); + + if (!fwrite (mbuffer, size, 1, file)) // note order of arguments + { + fprintf (stderr, ucon64_msg[WRITE_ERROR], filename); + fclose (file); + free (mbuffer); + return -1; // see comment for fopen() call + } + free (mbuffer); +#else + for (i = 0; i < size; i += 1024) + { + f2a_receive_raw_par (buffer, 1024); + if (flip) + for (j = 0; j < 256; j++) + ((int *) buffer)[j] = bswap_32 (((int *) buffer)[j]); + + if (!fwrite (buffer, 1024, 1, file)) // note order of arguments + { + fprintf (stderr, ucon64_msg[WRITE_ERROR], filename); + fclose (file); + return -1; // see comment for fopen() call + } + + if (parport_debug) + fprintf (stderr, "reading chunk %d of %d\n", (int) (i / 1024) + 1, + (int) (size / 1024)); + else + ucon64_gauge (starttime, i + 1024, size); + } + if (!parport_debug) + fputc ('\n', stdout); + fclose (file); +#endif + + return 0; +} + + +static int +f2a_send_cmd_par (int cmd, int address, int size) +{ + unsigned char recv[4]; + f2a_msg_cmd_t msg_cmd; + + memset (&msg_cmd, 0, sizeof (f2a_msg_cmd_t)); + msg_cmd.magic = me2be_32 (MAGIC_NUMBER); + msg_cmd.command = me2be_32 (cmd); + msg_cmd.address = me2be_32 (address); + msg_cmd.sizekb = me2be_32 (size / 1024); + + if (f2a_send_head_par (CMD_WRITEDATA, size)) + return -1; + + if (f2a_receive_raw_par (recv, 4)) + return -1; + + if (parport_debug) + fprintf (stderr, + "parport_send_cmd cmd='0x%08x' address='0x%08x' size='0x%08x' %d bytes\n", + msg_cmd.command, msg_cmd.address, msg_cmd.sizekb, + (int) sizeof (f2a_msg_cmd_t)); + + if (f2a_send_raw_par ((unsigned char *) &msg_cmd, sizeof (f2a_msg_cmd_t))) + return -1; + return 0; +} + + +static int +f2a_send_buffer_par (int cmd, int address, int size, const unsigned char *resource, + int head, int flip, unsigned int exec, int mode) +{ + unsigned char recv[4], buffer[1024]; + int i, j; + f2a_msg_cmd_t msg_cmd; + FILE *file = NULL; + + memset (&msg_cmd, 0, sizeof (f2a_msg_cmd_t)); + msg_cmd.magic = me2be_32 (MAGIC_NUMBER); + msg_cmd.command = me2be_32 (cmd); + msg_cmd.address = me2be_32 (address); + msg_cmd.sizekb = me2be_32 (size / 1024); + if (exec) + { + msg_cmd.exec_stub = me2be_32 (EXEC_STUB); + msg_cmd.exec = me2be_32 (0x08); + } + if (f2a_send_head_par (CMD_WRITEDATA, size)) + return -1; + if (f2a_receive_raw_par (recv, 4)) + return -1; + + if (parport_debug) + fprintf (stderr, + "parport_send_buffer cmd='0x%08x' address='0x%08x' size='0x%08x' %d bytes\n", + msg_cmd.command, msg_cmd.address, msg_cmd.sizekb, + (int) sizeof (f2a_msg_cmd_t)); + + if (f2a_send_raw_par ((unsigned char *) &msg_cmd, sizeof (f2a_msg_cmd_t))) + return -1; + + if (mode == 1) + { + if ((file = fopen ((char *) resource, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], (char *) resource); + //exit (1); return, because the other code does it too... + return -1; + } + clearerr (file); + } + + for (i = 0; i < size; i += 1024) + { + if (mode == 1) + { + j = fread (buffer, 1, 1024, file); + memset (buffer + j, 0, 1024 - j); + if (ferror (file)) + { + fputc ('\n', stderr); + fprintf (stderr, ucon64_msg[READ_ERROR], (char *) resource); + fclose (file); + return -1; + } + } + else + memcpy (buffer, resource, 1024); + + if (flip) + for (j = 0; j < 256; j++) + ((int *) buffer)[j] = bswap_32 (((int *) buffer)[j]); + + if (!i && head) + for (j = 1; j < GBA_LOGODATA_LEN / 4 + 1; j++) // + 1 for start address + ((int *) buffer)[j] = bswap_32 (((int *) gba_logodata)[j - 1]); + + if (parport_debug) + fprintf (stderr, "sending chunk %d of %d\n", (int) (i / 1024) + 1, + (int) (size / 1024)); + else + ucon64_gauge (starttime, i + 1024, size); + f2a_send_raw_par (buffer, 1024); + if (mode == 0) + resource += 1024; + } + if (!parport_debug) + fputc ('\n', stdout); // start new gauge on new line + + if (mode == 1) + fclose (file); + + return 0; +} + + +#ifdef DEBUG +static void +parport_dump_byte (unsigned char byte) +{ + char i; + + for (i = 7; i >= 0; i--) + { + if ((byte >> i) & 1) + fprintf (stderr, "1"); + else + fprintf (stderr, "0"); + } + fputc ('\n', stderr); +} +#endif + + +static int +f2a_receive_raw_par (unsigned char *buffer, int len) +{ + int err, i; + unsigned char *ptr, nibble; + + ptr = buffer; + if (parport_debug) + fprintf (stderr, "\nreceive:\n%04x: ", 0); + + *ptr = 0; + for (err = 0, i = 0; i < len * 2; i++) + { + nibble = 0; + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x04); + parport_nop (); + while (inportb ((unsigned short) (f2a_pport + PARPORT_STATUS)) & PARPORT_IBUSY) + ; + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x05); + nibble = inportb ((unsigned short) (f2a_pport + PARPORT_STATUS)); + while (!(inportb ((unsigned short) (f2a_pport + PARPORT_STATUS)) & PARPORT_IBUSY)) + ; + if (i % 2) + { + *ptr |= (nibble >> 3) & 0x0f; + if (parport_debug) + { + fprintf (stderr, "%02x ", (unsigned char) *ptr); + if (!(((i / 2) + 1) % 32) && i && (i / 2) < len - 1) + fprintf (stderr, "\n%04x: ", (i / 2) + 1); + } + *ptr = 0; + ptr++; + } + else + *ptr |= ((nibble >> 3) & 0xf) << 4; + } + if (parport_debug) + fputc ('\n', stderr); + + return err; +} + + +static int +f2a_send_raw_par (unsigned char *buffer, int len) +{ + int timeout, i; + unsigned char *pc; + + pc = buffer; + if (parport_debug) + fprintf (stderr, "\nsend:\n%04x: ", 0); + for (i = 0; i < len; i++) + { + timeout = 2000; + if (parport_debug) + { + fprintf (stderr, "%02x ", (unsigned char) *pc); + if (!((i + 1) % 32) && i && i < len - 1) + fprintf (stderr, "\n%04x: ", i + 1); + } + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x04); + parport_nop (); + while ((inportb ((unsigned short) (f2a_pport + PARPORT_STATUS)) & PARPORT_IBUSY) && + (timeout--) > 0) + wait2 (1); + outportb ((unsigned short) (f2a_pport + PARPORT_DATA), *pc); + parport_nop (); + while ((inportb ((unsigned short) (f2a_pport + PARPORT_STATUS)) & PARPORT_IBUSY) && + (timeout--) > 0) + wait2 (1); + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x05); + parport_nop (); + while ((!(inportb ((unsigned short) (f2a_pport + PARPORT_STATUS)) & PARPORT_IBUSY)) && + (timeout--) > 0) + wait2 (1); + pc++; + if (timeout < 0) + { + fprintf (stderr, "\nERROR: Time-out\n"); + return -1; + } + } + if (parport_debug) + fputc ('\n', stderr); + + return 0; +} + + +static int +f2a_wait_par (void) +{ + int stat; + + while (1) + { + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x04); + parport_nop (); + stat = inportb ((unsigned short) (f2a_pport + PARPORT_STATUS)); + if (stat & PARPORT_IBUSY) + break; + outportb ((unsigned short) (f2a_pport + PARPORT_CONTROL), 0x05); + parport_nop (); + inportb ((unsigned short) (f2a_pport + PARPORT_STATUS)); + } + return 0; +} +#endif // USE_PARALLEL + + +#if defined USE_PARALLEL || defined USE_USB +int +f2a_read_rom (const char *filename, int size) +{ + int offset = 0; + + starttime = time (NULL); +#ifdef USE_USB + if (ucon64.usbport) + { + f2a_init_usb (); + f2a_read_usb (0x8000000 + offset * MBIT, size * MBIT, filename); + usbport_close (f2a_handle); + } +#endif +#if defined USE_PARALLEL && defined USE_USB + else +#endif +#ifdef USE_PARALLEL + { + f2a_init_par (ucon64.parport, 10); + f2a_read_par (0x08000000 + offset * MBIT, size * MBIT, filename); + } +#endif + return 0; +} + + +int +f2a_write_rom (const char *filename, int size) +{ + int offset = 0, n, n_files, n_files_max = 0, fsize, totalsize = LOADER_SIZE; + char **files = NULL, *file_mem[1]; + struct stat fstate; + + if (filename) // -xf2a + { + files = file_mem; + files[0] = (char *) filename; + n_files = 1; + } + else // -xf2amulti=SIZE + { + n_files = 0; + for (n = 1; n < ucon64.argc; n++) + { + if (access (ucon64.argv[n], F_OK)) + continue; // "file" does not exist (option) + stat (ucon64.argv[n], &fstate); + if (!S_ISREG (fstate.st_mode)) + continue; + + if (n_files == n_files_max) + { + n_files_max += 20; // allocate mem for 20 extra pointers + if ((files = (char **) realloc (files, n_files_max * 4)) == NULL) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], n_files_max * 4); + exit (1); + } + } + + fsize = fsizeof (ucon64.argv[n]); + if (totalsize + fsize > size) + { + printf ("WARNING: The sum of the sizes of the files is larger than the specified flash\n" + " card size (%d Mbit). Skipping files, starting with\n" + " %s\n", + size / MBIT, ucon64.argv[n]); + break; + } + totalsize += fsize; + + files[n_files] = ucon64.argv[n]; + n_files++; + } + if (n_files == 0) + return -1; + } + + starttime = time (NULL); +#ifdef USE_USB + if (ucon64.usbport) + { + f2a_init_usb (); + f2a_write_usb (n_files, files, 0x8000000 + offset * MBIT); + usbport_close (f2a_handle); + } +#endif +#if defined USE_PARALLEL && defined USE_USB + else +#endif +#ifdef USE_PARALLEL + { + f2a_init_par (ucon64.parport, 10); + //f2a_erase_par (0x08000000, size * MBIT); + f2a_write_par (n_files, files, 0x8000000 + offset * MBIT); + } +#endif + + if (!filename) + free (files); + + return 0; +} + + +int +f2a_read_sram (const char *filename, int bank) +{ + int size; + + if (bank == UCON64_UNKNOWN) + { + bank = 1; + size = 256 * 1024; + } + else + { + if (bank < 1) + { + fprintf (stderr, "ERROR: Bank must be a number larger than or equal to 1\n"); + exit (1); + } + size = 64 * 1024; + } + bank--; + + starttime = time (NULL); +#ifdef USE_USB + if (ucon64.usbport) + { + f2a_init_usb (); + f2a_read_usb (0xe000000 + bank * 64 * 1024, size, filename); + usbport_close (f2a_handle); + } +#endif +#if defined USE_PARALLEL && defined USE_USB + else +#endif +#ifdef USE_PARALLEL + { + f2a_init_par (ucon64.parport, 10); + f2a_read_par (0xe000000 + bank * 64 * 1024, size, filename); + } +#endif + return 0; +} + + +int +f2a_write_sram (const char *filename, int bank) +{ + char *files[1] = { (char *) filename }; + + // define one bank as a 64 kilobyte unit + if (bank == UCON64_UNKNOWN) + bank = 1; + else + if (bank < 1) + { + fprintf (stderr, "ERROR: Bank must be a number larger than or equal to 1\n"); + exit (1); + } + bank--; + + starttime = time (NULL); +#ifdef USE_USB + if (ucon64.usbport) + { + f2a_init_usb (); + f2a_write_usb (1, files, 0xe000000 + bank * 64 * 1024); + usbport_close (f2a_handle); + } +#endif +#if defined USE_PARALLEL && defined USE_USB + else +#endif +#ifdef USE_PARALLEL + { + f2a_init_par (ucon64.parport, 10); + //f2a_erase_par (0xe000000, size * MBIT); + f2a_write_par (1, files, 0xe000000 + bank * 64 * 1024); + } +#endif + return 0; +} + +#endif // defined USE_PARALLEL || defined USE_USB diff --git a/ucon64/2.0/src/backup/f2a.h b/ucon64/2.0/src/backup/f2a.h new file mode 100644 index 0000000..13cafd6 --- /dev/null +++ b/ucon64/2.0/src/backup/f2a.h @@ -0,0 +1,34 @@ +/* +f2a.h - Flash 2 Advance support for uCON64 + +Copyright (c) 2003 Ulrich Hecht +Copyright (c) 2004 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef F2A_H +#define F2A_H + +extern const st_getopt2_t f2a_usage[]; + +#if defined USE_PARALLEL || defined USE_USB +extern int f2a_read_rom (const char *filename, int size); +extern int f2a_write_rom (const char *filename, int size); +extern int f2a_read_sram (const char *filename, int bank); +extern int f2a_write_sram (const char *filename, int bank); +#endif + +#endif diff --git a/ucon64/2.0/src/backup/fal.c b/ucon64/2.0/src/backup/fal.c new file mode 100644 index 0000000..1fded18 --- /dev/null +++ b/ucon64/2.0/src/backup/fal.c @@ -0,0 +1,1671 @@ +/* +fal.c - Flash Linker Advance support for uCON64 + +Copyright (c) 2001 Jeff Frohwein +Copyright (c) 2001 NoisyB +Copyright (c) 2001 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include "misc/misc.h" +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "fal.h" +#include "misc/parallel.h" +#include "console/gba.h" + + +const st_getopt2_t fal_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Flash Advance Linker"/*"2001 Visoly http://www.visoly.com"*/, + NULL + }, +#ifdef USE_PARALLEL + { + "xfal", 0, 0, UCON64_XFAL, + NULL, "send/receive ROM to/from Flash Advance Linker; " OPTION_LONG_S "port=PORT\n" + "receives automatically (32 Mbits) when ROM does not exist", + &ucon64_wf[WF_OBJ_GBA_DEFAULT_STOP_NO_ROM] + }, + { + "xfalmulti", 1, 0, UCON64_XFALMULTI, // send only + "SIZE", "send multiple ROMs to Flash Advance Linker (makes temporary\n" + "multi-game file truncated to SIZE Mbit); specify a loader in\n" + "the configuration file; " OPTION_LONG_S "port=PORT", + &ucon64_wf[WF_OBJ_GBA_DEFAULT_STOP] + }, + { + "xfalc", 1, 0, UCON64_XFALC, + "N", "receive N Mbits of ROM from Flash Advance Linker; " OPTION_LONG_S "port=PORT\n" + "N can be 8, 16, 32, 64, 128 or 256", + &ucon64_wf[WF_OBJ_GBA_STOP_NO_ROM] + }, + { + "xfals", 0, 0, UCON64_XFALS, + NULL, "send/receive SRAM to/from Flash Advance Linker; " OPTION_LONG_S "port=PORT\n" + "receives automatically when SRAM does not exist", + &ucon64_wf[WF_OBJ_GBA_STOP_NO_ROM] + }, + { + "xfalb", 1, 0, UCON64_XFALB, + "BANK", "send/receive SRAM to/from Flash Advance Linker BANK\n" + "BANK can be 1, 2, 3 or 4; " OPTION_LONG_S "port=PORT\n" + "receives automatically when SRAM does not exist", + &ucon64_wf[WF_OBJ_GBA_STOP_NO_ROM] + }, + { + "xfalm", 0, 0, UCON64_XFALM, + NULL, "try to enable EPP mode, default is SPP mode", + &ucon64_wf[WF_OBJ_GBA_SWITCH] + }, +#endif // USE_PARALLEL + {NULL, 0, 0, 0, NULL, NULL, NULL} +}; + + +#ifdef USE_PARALLEL + +/********************************************************/ +/* Flash Linker Advance */ +/* by Jeff Frohwein, 2001-Jun-28 */ +/* Compiled with DJGPP & linux */ +/********************************************************/ +// V1.0 - 01/06/28 - Original release +// V1.1 - 01/06/29 - Add -w option to slow down I/O transfer for some. +// V1.11 - 01/06/30 - Set ECP chipsets to proper parallel port mode. +// V1.2 - 01/07/23 - Fixed programming bug for Visoly carts. +// - Only the first block was getting erased. +// - -v option now appears on help (-h) menu. +// - -v & -s options now work properly after fixing a bug. +// V1.3 - 01/07/24 - Added support for Visoly turbo carts. +// V1.4 - 01/07/27 - Added support for longer filenames. +// - Fixed bug where files the size of the cart overwrite +// the first 16 half-words of the cart. Thnx goes to +// Richard W for the code fix. Thanks Richard! +// Fixed random lockup bug when programming Turbo carts. +// Added -n option. Header is now repaired by default. +// V1.5 - 01/09/25 - Added error retries/checking for older Visoly carts. +// - Odd length files no longer give a verify error on last byte+1 location. +// V1.6 - 01/11/11 - Made IRQ 7 instead of 5 and DMA 3 instead of 1 default +// - linux values. (Thanks to Massimiliano Marsiglietti.) +// - Added -D & -I to allow linux to change IRQ & DMA defaults. +// - Added LPT3 support. +// - Added error checking for space between switch & parameters. +// - Added -2 options for faster operation for some EPP ports. +// V1.7 - 01/11/13 - Added -b option to backup game save SRAM or game save Flash. +// - Added -r option to restore game save SRAM. (No flash support.) +// V1.71 - 01/11/23 - Fixed bug introduced in v1.7 where -d option printed out twice. +// V1.72 - 01/12/12 - Force 0x96 at location 0xb2 in header since it's required. + +// To compile source on linux: +// cc -o fl fl.c -O2 +// You must have root access to run this under linux. +// +// NOTE: This file is filled with cr+lf line terminators. This may +// lead to unhelpful and weird error messages with gcc for linux. +// Strip out the cr characters to prevent this problem. The following +// unix command line will work for that: +// tr -d \\r < dosfile > unixfile +// On some unix distributions you can also use the following command: +// dos2unix +// (Thanks to Massimiliano Marsiglietti for locating this problem.) + +// RAM Detect notes for dev. (Just ignore!) +//----------------------------------------- +// To detect backup type. +// 1. First check for eeprom. +// 2. Read byte from 0xe000000. +// 3. Write new byte to 0xe000000. +// 4. If diff, write back data & exit with SRAM detect flag. +// 5. If no diff get device Manuf ID for flash. +// 6. If no Manuf ID detected then report no cart backup available. + +#define outpb(p, v) outportb((unsigned short) (p), (unsigned char) (v)); iodelay() +#define inpb(p) inportb((unsigned short) (p)) +#define outpw(p, v) outportw((unsigned short) (p), (unsigned char) (v)); iodelay() +#define inpw(p) inportw((unsigned short) (p)) + +//#define HEADER_LENGTH 0xc0 +//#define OUTBUFLEN 256 // Must be a multiple of 2! (ex:64,128,256...) + +#define INTEL28F_BLOCKERASE 0x20 +#define INTEL28F_CLEARSR 0x50 +#define INTEL28F_CONFIRM 0xD0 +#define INTEL28F_QUIRY 0x98 +#define INTEL28F_READARRAY 0xff +#define INTEL28F_READSR 0x70 +#define INTEL28F_RIC 0x90 +#define INTEL28F_WRTOBUF 0xe8 + +#define SHARP28F_BLOCKERASE 0x20 +#define SHARP28F_CONFIRM 0xD0 +#define SHARP28F_READARRAY 0xff +#define SHARP28F_WORDWRITE 0x10 + +#define u8 unsigned char +#define u16 unsigned short int +#define u32 unsigned int +#define u64 unsigned long long int +// u64 is needed to compile cartlib.c without warnings on Linux/x86_64 +#define CONST_U8 const unsigned char + + +// ***Global Variables *** + +//int WaitDelay,WaitNCDelay; +unsigned SPPDataPort; +unsigned SPPStatPort; +unsigned SPPCtrlPort; +unsigned EPPAddrPort; +unsigned EPPDataPort; +unsigned ECPRegECR; + +// prototypes +void WriteFlash (int addr, int data); +int ReadFlash (int addr); +void iodelay (void); +int PPReadWord (void); +void PPWriteWord (int i); +void SetCartAddr (int addr); +void l4021d0 (int i); +void l40226c (void); + +#define FLINKER 1 + +#include "cartlib.c" + + +static int debug, verbose, DataSize16, Device, EPPMode, RepairHeader, + VisolyTurbo, WaitDelay, FileHeader[0xc0], HeaderBad, Complement = 0; + + +void +iodelay (void) +{ + volatile int i; + for (i = 0; i < WaitDelay; i++) + { + i++; + i--; + } +} + + +void +ProgramExit (int code) +{ + exit (code); +} + + +#if 0 +void +usage (char *name) +{ + char _small[255]; + char smaller[255]; + int i = 0; + + strcpy (_small, name); + +#if 0 + if (strchr (name, '.') != NULL) + _small[strlen (_small) - 4] = 0; /* remove trailing file type */ +#endif + + while ((_small[strlen (_small) - i] != 0x2f) && /* loop until we find a / */ + ((strlen (_small) - i) > 0)) + i++; + + if ((strlen (_small) - i) == 0) + i++; + + strcpy (smaller, (char *) (&_small[strlen (_small) - i + 1])); + + fprintf (stderr, "GBA FLinker v1.72 by Jeff F.\n"); + fprintf (stderr, "Usage: %s [options]\n", smaller); + + fprintf (stderr, + "\t-2 Use 16bit EPP data path for faster operation (default=8bit)\n"); + fprintf (stderr, + "\t-b o s file Backup game save SRAM or Flash to file\n"); + fprintf (stderr, "\t (o = Bank Number [1-4])\n"); + fprintf (stderr, "\t (s=1 - Backup 32K bytes to file.)\n"); + fprintf (stderr, "\t (s=2 - Backup 64K bytes to file.)\n"); + fprintf (stderr, "\t (s=3 - Backup 128K bytes to file.)\n"); + fprintf (stderr, "\t (s=4 - Backup 256K bytes to file.)\n"); + fprintf (stderr, + "\t-c n Specify chip size in mbits (8,16,32,64,128,256) (default=32)\n"); + fprintf (stderr, + "\t-d n Dump 256 bytes of ROM to screen (default: n=0)\n"); + fprintf (stderr, "\t-h This help screen\n"); + fprintf (stderr, + "\t-l n Specify the parallel port to use (default is 1 = LPT1)\n"); +// fprintf (stderr, "\t-m\tSet Standard Parallel Port (SPP) mode (default = EPP)\n"); + fprintf (stderr, + "\t-n Do not repair incorrect header (default = repair header)\n"); + fprintf (stderr, "\t-p file Program flash cart with file\n"); + fprintf (stderr, + "\t-r o file Restore game save SRAM from file (No save flash support)\n"); + fprintf (stderr, "\t (o = Bank Number [1-4])\n"); + fprintf (stderr, + "\t-s file Save the cart into a file (Use -c to specify size)\n"); + fprintf (stderr, "\t-v file Verify flash cart with file\n"); + fprintf (stderr, "\t-w n Add delay to make transfer more reliable\n"); +} +#endif + + +void +InitPort (int port) +{ + // uCON64 comment: see the comment in fal_main() where port is initialised + SPPDataPort = port; + SPPStatPort = SPPDataPort + 1; + SPPCtrlPort = SPPDataPort + 2; + EPPAddrPort = SPPDataPort + 3; + EPPDataPort = SPPDataPort + 4; + ECPRegECR = SPPDataPort + 0x402; +} + + +void +l4020a4 (int reg) +{ + outpb (SPPCtrlPort, 1); + outpb (SPPDataPort, reg); + outpb (SPPCtrlPort, 9); + outpb (SPPCtrlPort, 1); +} + + +void +SPPWriteByte (int i) // l4020dc +{ + outpb (SPPDataPort, i); + outpb (SPPCtrlPort, 3); + outpb (SPPCtrlPort, 1); +} + + +void +l402108 (int reg, int adr) +{ + l4020a4 (reg); + SPPWriteByte (adr); +} + + +int +SPPReadByte (void) // 402124 +{ + int v; + + outpb (SPPCtrlPort, 0); + outpb (SPPCtrlPort, 2); + v = ((inpb (SPPStatPort)) >> 3) & 0xf; + outpb (SPPCtrlPort, 6); + v += (((inpb (SPPStatPort)) << 1) & 0xf0); + outpb (SPPCtrlPort, 0); + + return v; +} + + +void +l402188 (int reg) +{ + outpb (SPPCtrlPort, 1); + outpb (EPPAddrPort, reg); +} + + +void +l4021a8 (int reg, int adr) +{ + l402188 (reg); + outpb (SPPCtrlPort, 1); + outpb (EPPDataPort, adr); +} + + +void +l4021d0 (int i) +{ + outpb (SPPCtrlPort, 1); + + if (EPPMode) + l402188 (i); + else + l4020a4 (i); +} + + +void +l402200 (int reg, int adr) +{ + if (EPPMode) + l4021a8 (reg, adr); + else + l402108 (reg, adr); +} + + +void +l402234 (void) +{ + if (EPPMode) + l402200 (4, 0x83); + else + l402200 (4, 0xc3); +} + + +void +l40226c (void) +{ + if (EPPMode) + l402200 (4, 7); + else + l402200 (4, 0x47); +} + + +void +PPWriteByte (int i) // 4022d0 +{ + if (EPPMode) + { + outpb (EPPDataPort, i); + } + else + SPPWriteByte (i); +} + + +void +PPWriteWord (int i) // 4022d0 +{ + if (EPPMode) + { + if (DataSize16) + { + outpw (EPPDataPort, i); + } + else + { + outpb (EPPDataPort, i); + outpb (EPPDataPort, (i >> 8)); + } + } + else + { + SPPWriteByte (i); + SPPWriteByte (i >> 8); + } +} + + +int +PPReadByte (void) // 40234c +{ + int v; + + if (EPPMode) + v = inpb (EPPDataPort); + else + v = SPPReadByte (); + + return v; +} + + +int +PPReadWord (void) // 402368 // ReadFlash +{ + int v = 0; + + if (EPPMode) + { + if (DataSize16) + v = inpw (EPPDataPort); + else + { + v = inpb (EPPDataPort); + v += (inpb (EPPDataPort) << 8); + } + } + else + { + v = SPPReadByte (); //402124 + v += (SPPReadByte () << 8); + } + return v; +} + + +void +SetCartAddr (int addr) // 4023cc +{ + l402200 (2, addr >> 16); + l402200 (1, addr >> 8); + l402200 (0, addr); +} + + +void +WriteFlash (int addr, int data) // 402414 +{ + SetCartAddr (addr); + l4021d0 (3); + PPWriteWord (data); +} + + +int +ReadFlash (int addr) +{ + SetCartAddr (addr); + l4021d0 (3); + outpb (SPPCtrlPort, 0); + return PPReadWord (); +} + + +void +l402684 (void) +{ +#ifndef USE_PPDEV + outpb (SPPStatPort, 1); // clear EPP time flag +#endif + l40226c (); +} + + +int +LookForLinker (void) // 4026a8 +{ + l402684 (); + l402200 (2, 0x12); + l402200 (1, 0x34); + l402200 (0, 0x56); + l4021d0 (2); + outpb (SPPCtrlPort, 0); + if (PPReadByte () != 0x12) // 40234c + return 0; + + l4021d0 (1); + outpb (SPPCtrlPort, 0); + if (PPReadByte () != 0x34) + return 0; + + l4021d0 (0); + outpb (SPPCtrlPort, 0); + if (PPReadByte () != 0x56) + return 0; + + outpb (SPPCtrlPort, 4); + return 1; +} + + +void +LinkerInit (void) // 4027c4 +{ + int linker_found = 0; + + /* + uCON64 comment: + Accessing I/O ports with addresses higher than 0x3ff causes an access + violation under Windows XP (NT/2000) for _Windows_ executables without the + use of an appropriate I/O port driver. UserPort is an example of an + *inappropriate* I/O port driver, because it enables access to I/O ports up + to 0x3ff. For some (ridiculous) reason, DOS executables are allowed to + _access_ at least the ECP register this code uses. That doesn't mean it + will result in the expected behaviour like enabling EPP. + */ + if (EPPMode) + { + /* + Writing to the ECP register seems to have no effect on my PC (which + supports ECP, used appropriate BIOS setting). Tested under Windows XP + with Windows executables (Cygwin, VC++ and MinGW) - dbjh + */ +#ifndef USE_PPDEV + outpb (ECPRegECR, 4); // set EPP mode for ECP chipsets +#endif + if (LookForLinker ()) + { + // Linker found using EPP mode. + linker_found = 1; + puts ("Linker found. EPP found"); + + if (SPPDataPort == 0x3bc) + return; + outpb (SPPCtrlPort, 4); + } + } + if (!linker_found) + { + // Look for linker in SPP mode. +#ifndef USE_PPDEV + if (EPPMode) + outpb (ECPRegECR, 0); // set SPP mode for ECP chipsets +#endif + + EPPMode = 0; + if (LookForLinker ()) + puts ("Linker found. EPP not found or not enabled - SPP used"); + else + { + fputs ("ERROR: Flash Advance Linker not found or not turned on\n", + stderr); + ProgramExit (1); + } + } +} + + +int +ReadStatusRegister (int addr) // 402dd8 +{ + int v; + WriteFlash (addr, INTEL28F_READSR); + outpb (SPPCtrlPort, 0); + v = PPReadWord (); // & 0xff; + v = PPReadWord (); // & 0xff; + return v; +} + + +// StartOffSet: 1 = 0, 2 = 64k, 3 = 128k, 4 = 192k +// Size: 1 = 32k, 2 = 64k, 3 = 128k, 4 = 256k +void +BackupSRAM (FILE *fp, int StartOS, int Size) // 4046f4 +{ + int j, k, v; + int m; + int n = 1 << (Size - 1); + int size = n * 32 * 1024, bytesread = 0; + time_t starttime; + + printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + + starttime = time (NULL); + for (m = ((StartOS - 1) << 1); m < (((StartOS - 1) << 1) + n); m++) + { + if ((m & 1) == 0) + { + SetVisolyBackupRWMode (m >> 1); + l402234 (); + } + + // Backup a 32k byte chunk + for (j = 0; j < 0x80; j++) + { + l402200 (0, 0); + l402200 (1, j + (0x80 * (m & 1))); + l402200 (2, 0); + l4021d0 (3); + outpb (SPPCtrlPort, 0); + + for (k = 0; k < 0x100; k++) + { + v = PPReadByte (); + fputc (v, fp); + } + bytesread += 256; + if ((bytesread & 0x1fff) == 0) // call ucon64_gauge() after receiving 8 kB + ucon64_gauge (starttime, bytesread, size); + } + } +} + + +// StartOffSet: 1 = 0, 2 = 64k, 3 = 128k, 4 = 192k +void +RestoreSRAM (FILE *fp, int StartOS) +{ + int i; + int j, k; + int m = ((StartOS - 1) << 1); + int byteswritten = 0; + time_t starttime; + + printf ("Send: %d Bytes (%.4f Mb)\n\n", ucon64.file_size, + (float) ucon64.file_size / MBIT); + + starttime = time (NULL); + i = fgetc (fp); + while (!feof (fp)) + { + if ((m & 1) == 0) + { + SetVisolyBackupRWMode (m >> 1); + l402234 (); + } + + // Restore a 32k byte chunk + for (j = 0; j < 0x80; j++) + { + l402200 (0, 0); + l402200 (1, j + (0x80 * (m & 1))); + l402200 (2, 0); + l4021d0 (3); + outpb (SPPCtrlPort, 0); + + for (k = 0; k < 0x100; k++) + { + PPWriteByte (i); + i = fgetc (fp); + } + byteswritten += 256; + if ((byteswritten & 0x1fff) == 0) // call ucon64_gauge() after sending 8 kB + ucon64_gauge (starttime, byteswritten, ucon64.file_size); + } + m++; + } +} + + +void +BackupROM (FILE *fp, int SizekW) +{ + u16 valw; + u32 i, j; + int size = SizekW << 1, bytesread = 0; + time_t starttime; + + printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + + WriteFlash (0, INTEL28F_READARRAY); // Set flash (intel 28F640J3A) Read Mode + + starttime = time (NULL); + for (i = 0; i < (u32) (SizekW >> 8); i++) + { + SetCartAddr (i << 8); // Set cart base addr to 0 + l4021d0 (3); + + outpb (SPPCtrlPort, 0); + + for (j = 0; j < 256; j++) + { + valw = PPReadWord (); + fputc (valw & 0xff, fp); + fputc (valw >> 8, fp); + } + bytesread += 256 << 1; // 256 words + if ((bytesread & 0xffff) == 0) // call ucon64_gauge() after receiving 64 kB + ucon64_gauge (starttime, bytesread, size); + } +} + + +void +dump (u8 BaseAdr) +{ +// unsigned char low, high; + int i; + u8 First = 1; + u16 v; + u8 val1, val2; + u8 Display[17] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + WriteFlash (0, INTEL28F_READARRAY); // Set flash (intel 28F640J3A) Read Mode + + SetCartAddr (BaseAdr << 7); // Set cart base addr to read + l4021d0 (3); + + outpb (SPPCtrlPort, 0); + + for (i = 0; i < 128; i++) + { + if (First == 1) + { + if (i * 2 < 256) + fputc ('0', stdout); + if (i * 2 < 16) + fputc ('0', stdout); + printf ("%hx - ", (i * 2)); + First = 0; + } + + v = PPReadWord (); + val2 = v >> 8; + val1 = v & 255; + + if ((val1 > 31) & (val1 < 127)) + Display[(i & 7) * 2] = val1; + else + Display[(i & 7) * 2] = 46; + + if (val1 < 16) + fputc ('0', stdout); + printf ("%hx ", val1); + + if ((val2 > 31) & (val2 < 127)) + + Display[(i & 7) * 2 + 1] = val2; + else + Display[(i & 7) * 2 + 1] = 46; + + if (val2 < 16) + fputc ('0', stdout); + printf ("%hx ", val2); + + if ((i & 7) == 7) + { + First = 1; + printf (" %3s\n", (&Display[0])); + } + } +} + + +void +CheckForFC (void) +{ + LinkerInit (); + + SetVisolyFlashRWMode (); + Device = CartTypeDetect (); + VisolyTurbo = 0; + + printf ("Device ID = 0x%x: ", Device); + + switch (Device) + { + case 0x16: + fputs ("FA 32M (i28F320J3A)", stdout); + break; + case 0x17: + fputs ("FA 64M (i28F640J3A)", stdout); + break; + case 0x18: + fputs ("FA 128M (i28F128J3A)", stdout); + break; + case 0x2e: + fputs ("Standard ROM", stdout); + break; + case 0x96: + fputs ("Turbo FA 64M (2 x i28F320J3A)", stdout); + VisolyTurbo = 1; + break; + case 0x97: + fputs ("Turbo FA 128M (2 x i28F640J3A)", stdout); + VisolyTurbo = 1; + break; + case 0x98: + fputs ("Turbo FA 256M (2 x i28F128J3A", stdout); + VisolyTurbo = 1; + break; + case 0xdc: + fputs ("Hudson", stdout); + break; + case 0xe2: + fputs ("Nintendo Flash Card (LH28F320BJE)", stdout); + break; + default: + fputs ("Unknown", stdout); + break; + } + fputc ('\n', stdout); +} + + +int +GetFileByte (FILE *fp) +{ + static int FilePos = 0; + int i = 0; + + if (RepairHeader) + { + // Set file pointer just past header + if (FilePos == 0) + for (i = 0; i < 0xa0; i++) + (void) fgetc (fp); + + if (FilePos < 0xa0) + { + if ((HeaderBad) && (FilePos > 3)) + i = gba_logodata[FilePos - 4]; // GoodHeader[FilePos]; + else + i = FileHeader[FilePos]; + } + else if ((FilePos == 0xb2) || (FilePos == 0xbd)) + { + if (FilePos == 0xb2) + i = 0x96; // Required + else + i = Complement; + (void) fgetc (fp); // Discard file value + } + else + i = fgetc (fp); + } + else + i = fgetc (fp); + + FilePos++; + return i; +} + + +int +GetFileSize2 (FILE *fp) +/* + The name "GetFileSize" conflicts with a Windows function. + For some odd reason Jeff Frohwein returned the file size minus 1. I (dbjh) + guess that's a bug. I disabled the (terribly inefficient) file size code + because uCON64 already determined the file size at this point. +*/ +{ + int FileSize; + + if (RepairHeader) + { + int i; + int j; + + FileSize = 0; + while (!feof (fp) && (FileSize < 0xc0)) + FileHeader[FileSize++] = fgetc (fp); + + if (feof (fp)) + { + fputs ("ERROR: File must be 192 bytes or larger\n", stderr); + ProgramExit (1); + } + + HeaderBad = 0; + i = 4; + while (i < 0xa0) //9c) + { + if (FileHeader[i] != gba_logodata[i - 4]) // GoodHeader[i] + HeaderBad = 1; + i++; + } + if (HeaderBad) + puts ("NOTE: Fixing logo area"); + + Complement = 0; + FileHeader[0xb2] = 0x96; // Required + for (j = 0xa0; j < 0xbd; j++) + Complement += FileHeader[j]; + Complement = (0 - (0x19 + Complement)) & 0xff; + //printf("[Complement = 0x%x]", (int)Complement); + //printf("[HeaderComp = 0x%x]", (int)FileHeader[0xbd]); + if (FileHeader[0xbd] != Complement) + puts ("NOTE: Fixing complement check"); + + rewind (fp); + } + else + rewind (fp); + + return ucon64.file_size; +} + + +// Program older (non-Turbo) Visoly flash card +// (Single flash chip) +void +ProgramNonTurboIntelFlash (FILE *fp) +{ + int i, j, k; + int addr = 0; + int FileSize; + int Ready = 0; + int Timeout; + time_t starttime; + + // Get file size + FileSize = GetFileSize2 (fp); + + puts ("Erasing Visoly non-turbo flash card..."); + + // Erase as many 128k blocks as are required + Ready = EraseNonTurboFABlocks (0, ((FileSize - 1) >> 17) + 1); + + clear_line (); // remove "erase gauge" + if (Ready) + { + printf ("Send: %d Bytes (%.4f Mb)\n\n", FileSize, (float) FileSize / MBIT); + + //403018 + starttime = time (NULL); + j = GetFileByte (fp); + + while (!feof (fp)) + { + Ready = 0; + Timeout = 0x4000; + + while ((Ready == 0) && (Timeout != 0)) + { + WriteFlash (addr, INTEL28F_WRTOBUF); + outpb (SPPCtrlPort, 0); + Ready = PPReadWord () & 0x80; + + Timeout--; + } + + if (Ready) + { + WriteFlash (addr, 15); // Write 15+1 16bit words + + SetCartAddr (addr); // Set cart base addr to 0 + l4021d0 (3); + + for (i = 0; i < 16; i++) + { + k = j; + if (j != EOF) + j = GetFileByte (fp); + k += (j << 8); + PPWriteWord (k); + + if (j != EOF) + j = GetFileByte (fp); + } + + addr += 16; + if ((addr & 0x3fff) == 0) // call ucon64_gauge() after sending 32 kB + ucon64_gauge (starttime, addr << 1, FileSize); + + PPWriteWord (INTEL28F_CONFIRM); // Comfirm block write + + Ready = 0; + Timeout = 0x4000; + + while ((Ready == 0) && (Timeout != 0)) + { + WriteFlash (0, INTEL28F_READSR); + outpb (SPPCtrlPort, 0); + i = PPReadWord () & 0xff; + Ready = i & 0x80; + + Timeout--; + } + + if (Ready) + { + if (i & 0x7f) + { + // One or more status register error bits are set + outpb (SPPCtrlPort, 1); + WriteFlash (0, INTEL28F_CLEARSR); + Ready = 0; + break; + } + } + else + { + outpb (SPPCtrlPort, 1); + WriteFlash (0, INTEL28F_CLEARSR); + break; + } + } + else + break; + } + + clear_line (); // remove last gauge + ucon64_gauge (starttime, addr << 1, FileSize); // make gauge reach 100% when size % 32 k != 0 + WriteFlash (0, INTEL28F_READARRAY); + outpb (SPPCtrlPort, 0); + + if (Ready) + ; + else + fputs ("\nERROR: Flash card write failed\n", stderr); + } + else + fputs ("\nERROR: Flash card erase failed\n", stderr); +} + + +// Program newer (Turbo) Visoly flash card +// (Dual chip / Interleave) +void +ProgramTurboIntelFlash (FILE *fp) +{ + int i, j = 0; + int k; //z; + int addr = 0; + int done1, done2; + int FileSize; + int Timeout; + int Ready; //= 0; + time_t starttime; + + // Get file size + FileSize = GetFileSize2 (fp); + + puts ("Erasing Visoly turbo flash card..."); + + // Erase as many 256k blocks as are required + Ready = EraseTurboFABlocks (0, ((FileSize - 1) >> 18) + 1); + + clear_line (); // remove "erase gauge" + if (Ready) + { + printf ("Send: %d Bytes (%.4f Mb)\n\n", FileSize, (float) FileSize / MBIT); + + //403018 + starttime = time (NULL); + j = GetFileByte (fp); + + while (!feof (fp)) + { + done1 = 0; + done2 = 0; + Ready = 0; + Timeout = 0x4000; + + while ((!Ready) && (Timeout != 0)) + { + if (done1 == 0) + WriteFlash (addr + 0, INTEL28F_WRTOBUF); + if (done2 == 0) + WriteFlash (addr + 1, INTEL28F_WRTOBUF); + + SetCartAddr (addr); // Set cart base addr + l4021d0 (3); + outpb (SPPCtrlPort, 0); + + done1 = PPReadWord () & 0x80; + done2 = PPReadWord () & 0x80; + Ready = ((done1 + done2) == 0x100); + + Timeout--; + } + + if (Ready) + { + WriteFlash (addr, 15); // Write 15+1 16bit words + PPWriteWord (15); + + SetCartAddr (addr); // Set cart base addr + l4021d0 (3); + + for (i = 0; i < 32; i++) + { + k = j; + if (j != EOF) + j = GetFileByte (fp); + k += (j << 8); + PPWriteWord (k); + + if (j != EOF) + j = GetFileByte (fp); + } + addr += 32; + if ((addr & 0x3fff) == 0) // call ucon64_gauge() after sending 32 kB + ucon64_gauge (starttime, addr << 1, FileSize); + PPWriteWord (INTEL28F_CONFIRM); // Comfirm block write + PPWriteWord (INTEL28F_CONFIRM); // Comfirm block write + + Ready = 0; + Timeout = 0x4000; + k = 0; + + while (((k & 0x8080) != 0x8080) && (Timeout != 0)) + { + outpb (SPPCtrlPort, 0); + k = PPReadWord () & 0xff; + k += ((PPReadWord () & 0xff) << 8); + Ready = (k == 0x8080); + + Timeout--; + } + + if (!Ready) + break; + } + else + break; + } + + clear_line (); // remove last gauge + ucon64_gauge (starttime, addr << 1, FileSize); // make gauge reach 100% when size % 32 k != 0 + WriteFlash (0, INTEL28F_READARRAY); + outpb (SPPCtrlPort, 0); + WriteFlash (1, INTEL28F_READARRAY); + outpb (SPPCtrlPort, 0); + + if (Ready) + ; + else + { + WriteFlash (0, INTEL28F_CLEARSR); + PPWriteWord (INTEL28F_CLEARSR); + fputs ("\nERROR: Flash card write failed\n", stderr); + } + } + else + fputs ("\nERROR: Flash card erase failed\n", stderr); +} + + +// Program official Nintendo flash card +void +ProgramSharpFlash (FILE *fp) +{ + int i, j; + int k = 0; + int addr = 0; + int FileSize; + int Ready; + time_t starttime; + + // Get file size + FileSize = GetFileSize2 (fp); + + puts ("Erasing Nintendo flash card..."); + + // Erase as many 64k blocks as are required + Ready = EraseNintendoFlashBlocks (0, ((FileSize - 1) >> 16) + 1); + + clear_line (); // remove "erase gauge" + if (Ready) + { + printf ("Send: %d Bytes (%.4f Mb)\n\n", FileSize, (float) FileSize / MBIT); + + starttime = time (NULL); + j = GetFileByte (fp); + + while (!feof (fp)) + { + if (j != EOF) + k = GetFileByte (fp); + + i = ((k & 0xff) << 8) + (j & 0xff); + + while ((ReadStatusRegister (0) & 0x80) == 0) + ; + + WriteFlash (addr, SHARP28F_WORDWRITE); + WriteFlash (addr, i); + addr += 1; + + j = GetFileByte (fp); + if ((addr & 0x3fff) == 0) // call ucon64_gauge() after sending 32 kB + ucon64_gauge (starttime, addr << 1, FileSize); + } + + clear_line (); // remove last gauge + ucon64_gauge (starttime, addr << 1, FileSize); // make gauge reach 100% when size % 32 k != 0 + WriteFlash (0, INTEL28F_READARRAY); + outpb (SPPCtrlPort, 0); + } + else + fputs ("\nERROR: Flash card erase failed\n", stderr); +} + + +#if 0 // not used +void +VerifyFlash (FILE *fp) +{ + int addr = 0; + int CompareFail = 0; + int k = 0; + int i, j, m, n; + + WriteFlash (0, INTEL28F_READARRAY); // Set flash (intel 28F640J3A) Read Mode + + j = 0; + while (!feof (fp)) + { + j = fgetc (fp); + if (j != EOF) + { + if ((addr & 0x1ff) == 0) + { + SetCartAddr (addr >> 1); // Set cart base addr to read + l4021d0 (3); + + outpb (SPPCtrlPort, 0); + } + + k = fgetc (fp); + + i = PPReadWord (); + m = i & 0xff; + n = i >> 8; + + if (m != j) + { + printf ("Address %x - Cartridge %x: File %hx\n", addr, m, j); + CompareFail = 1; + } + if ((n != k) && (k != EOF)) + { + printf ("Address %x - Cartridge %x: File %hx\n", addr + 1, n, + k); + CompareFail = 1; + } + addr += 2; + } + } + + // Correct verify length if at EOF + if (k == EOF) + addr--; + + if (CompareFail == 0) + printf ("%d bytes compared OK\n", addr); +} +#endif + + +void +SpaceCheck (char c) +{ + if (c != 0) + { + fputs ("ERROR: Space required between option and parameter\n", stderr); + ProgramExit (1); + } +} + + +int +fal_main (int argc, char **argv) +{ + int arg, i; + u8 Base = 0; + FILE *fp; + char fname[128], fname2[128]; + int OptB = 0; + int OptD = 0; + int OptP = 0; + int OptR = 0; + int OptS = 0; + int OptV = 0; + int OptZ = 0; + int port = 0x378; + int ChipSize = 32; + int BackupMemOffset = 0; + int BackupMemSize = 0; + + if (argc < 2) + { +// usage (argv[0]); + ProgramExit (1); + } + + debug = 0; + verbose = 1; + EPPMode = 0; // uCON64 comment: use the most compatible setting as default + DataSize16 = 0; + WaitDelay = 0; + VisolyTurbo = 0; + RepairHeader = 1; + + for (arg = 1; arg < argc; arg++) + { + if (argv[arg][0] != '-') + { +// usage (argv[0]); + ProgramExit (1); + } + + switch (argv[arg][1]) + { + case 'b': + SpaceCheck (argv[arg][2]); + BackupMemOffset = *(char *) argv[++arg] - 0x30; + SpaceCheck (argv[arg][1]); + BackupMemSize = *(char *) argv[++arg] - 0x30; + + if ((BackupMemSize < 1) || (BackupMemSize > 4) || + (BackupMemOffset < 1) || (BackupMemOffset > 4)) + { + fputs ("ERROR: -b parameter values must be between 1-4\n", stderr); + ProgramExit (1); + } + SpaceCheck (argv[arg][1]); + strcpy (fname, argv[++arg]); + OptB = 1; + break; + case '2': + // 16-bit EPP support enable (doesn't work with + // "Turbo FA 128M (2 x i28F640J3A)" - dbjh) + DataSize16 = 1; + break; + case 'c': + // Set cart size + SpaceCheck (argv[arg][2]); + ChipSize = (u16) (atoi (argv[++arg])); + if ((ChipSize != 8) && + (ChipSize != 16) && + (ChipSize != 32) && + (ChipSize != 64) && (ChipSize != 128) && (ChipSize != 256)) + { + fputs ("ERROR: Chip size must be 8,16,32,64,128 or 256\n", stderr); + ProgramExit (1); + } + break; + case 'd': + // Dump 256 bytes to screen + SpaceCheck (argv[arg][2]); + if (argv[++arg] != NULL) + Base = (u8) (atoi (argv[arg])); + printf ("Base address: %hx\n", Base * 256); + OptD = 1; + break; + case 's': + // Backup flash cart + SpaceCheck (argv[arg][2]); + strcpy (fname, argv[++arg]); + OptS = 1; + break; + case 'p': + // Program flash cart + SpaceCheck (argv[arg][2]); + strcpy (fname, argv[++arg]); + OptP = 1; + break; + case 'r': + SpaceCheck (argv[arg][2]); + BackupMemOffset = *(char *) argv[++arg] - 0x30; + if ((BackupMemOffset < 1) || (BackupMemOffset > 4)) + { + fputs ("ERROR: -r parameter value must be between 1-4\n", stderr); + ProgramExit (1); + } + SpaceCheck (argv[arg][1]); + strcpy (fname, argv[++arg]); + OptR = 1; + break; + case 'v': + // Verify flash cart + SpaceCheck (argv[arg][2]); + strcpy (fname2, argv[++arg]); + OptV = 1; + break; + case 'l': + SpaceCheck (argv[arg][2]); + i = atoi (argv[++arg]); + /* + uCON64 comment: we want to support non-standard parallel port + addresses too. So, instead of passing a number from 1 to 4, we pass + the address itself + */ + port = i; + break; + case 'm': + // uCON64 comment: See comment in LinkerInit(). Note that we reverse + // the meaning compared to the original code. + EPPMode = 1; // Set EPP mode + break; + case 'n': + // Don't repair header + RepairHeader = 0; + break; + case 'w': + SpaceCheck (argv[arg][2]); + WaitDelay = atoi (argv[++arg]); + break; + case 'z': + OptZ = 1; + break; + default: +// usage (argv[0]); + ProgramExit (1); + } + } + + InitPort (port); + + CheckForFC (); + + if (OptB) + { + //DumpSRAM (); + if ((fp = fopen (fname, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], fname); + ProgramExit (1); + } + + BackupSRAM (fp, BackupMemOffset, BackupMemSize); + fputc ('\n', stdout); + fclose (fp); + } + + if (OptD) + dump (Base); + + if ((OptP) && ((Device == 0) || (Device == 0x2e) || (Device == 0xff))) + { + fputs ("ERROR: Device type not recognized as programmable\n", stderr); + ProgramExit (1); + } + + if (OptR) + { + if ((fp = fopen (fname, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], fname); + ProgramExit (1); + } + + RestoreSRAM (fp, BackupMemOffset); + fputc ('\n', stdout); + fclose (fp); + } + + if (OptP) + { + if ((fp = fopen (fname, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], fname); + ProgramExit (1); + } + + if (Device == 0xe2) + ProgramSharpFlash (fp); + else + { + if (VisolyTurbo) + ProgramTurboIntelFlash (fp); + else + ProgramNonTurboIntelFlash (fp); + } + fputc ('\n', stdout); + fclose (fp); + } + + if (OptS) + { + if ((fp = fopen (fname, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], fname); + ProgramExit (1); + } + + BackupROM (fp, ChipSize << 16); + fputc ('\n', stdout); + fclose (fp); + } + +#if 0 + if (OptV) + { + if ((fp = fopen (fname2, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], fname2); + ProgramExit (1); + } + + VerifyFlash (fp); + fclose (fp); + } +#endif + + ProgramExit (0); + exit (0); +} + + +/* + It will save you some work if you don't fully integrate the code above with + uCON64's code, because it is a project separate from the uCON64 project. +*/ +int fal_argc = 0; +char *fal_argv[128]; + +void +fal_args (unsigned int parport) +{ + char parport_str[80]; + + parport_print_info (); + + fal_argv[fal_argc++] = "fl"; + fal_argv[fal_argc++] = "-l"; + sprintf (parport_str, "%d", parport); // don't use %x, as Jeff Frohwein uses atoi() + fal_argv[fal_argc++] = parport_str; + + if (ucon64.parport_mode == UCON64_EPP) + fal_argv[fal_argc++] = "-m"; +} + + +int +fal_read_rom (const char *filename, unsigned int parport, int size) +{ + char size_str[80]; + + fal_args (parport); + + fal_argv[fal_argc++] = "-c"; + if (size != 8 && size != 16 && size != 32 && size != 64 && size != 128 && + size != 256) + { + fputs ("ERROR: Invalid argument for -xfalc=n\n" + " n can be 8, 16, 32, 64, 128 or 256\n", stderr); + exit (1); + } + sprintf (size_str, "%d", size); + fal_argv[fal_argc++] = size_str; + fal_argv[fal_argc++] = "-s"; + fal_argv[fal_argc++] = (char *) filename; // this is safe (FAL code + // doesn't modify argv) + if (!fal_main (fal_argc, fal_argv)) + return 0; + + return -1; +} + + +int +fal_write_rom (const char *filename, unsigned int parport) +{ + fal_args (parport); + + fal_argv[fal_argc++] = "-p"; + fal_argv[fal_argc++] = (char *) filename; + + if (!fal_main (fal_argc, fal_argv)) + return 0; + + return -1; +} + + +int +fal_read_sram (const char *filename, unsigned int parport, int bank) +{ + char bank_str[2]; + + fal_args (parport); + + fal_argv[fal_argc++] = "-b"; + if (bank == UCON64_UNKNOWN) + { + fal_argv[fal_argc++] = "1"; + fal_argv[fal_argc++] = "4"; // 256 kB + } + else + { + if (bank < 1 || bank > 4) + { + fputs ("ERROR: Bank must be 1, 2, 3 or 4\n", stderr); + exit (1); + } + bank_str[0] = '0' + bank; + bank_str[1] = 0; // terminate string + fal_argv[fal_argc++] = bank_str; + fal_argv[fal_argc++] = "2"; // 64 kB + } + fal_argv[fal_argc++] = (char *) filename; + + if (!fal_main (fal_argc, fal_argv)) + return 0; + + return -1; +} + + +int +fal_write_sram (const char *filename, unsigned int parport, int bank) +{ + char bank_str[2]; + + fal_args (parport); + + fal_argv[fal_argc++] = "-r"; + if (bank == UCON64_UNKNOWN) + fal_argv[fal_argc++] = "1"; + else + { + if (bank < 1 || bank > 4) + { + fputs ("ERROR: Bank must be 1, 2, 3 or 4\n", stderr); + exit (1); + } + bank_str[0] = '0' + bank; + bank_str[1] = 0; // terminate string + fal_argv[fal_argc++] = bank_str; + } + fal_argv[fal_argc++] = (char *) filename; + + if (!fal_main (fal_argc, fal_argv)) + return 0; + + return -1; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/fal.h b/ucon64/2.0/src/backup/fal.h new file mode 100644 index 0000000..4bea7d9 --- /dev/null +++ b/ucon64/2.0/src/backup/fal.h @@ -0,0 +1,35 @@ +/* +fal.h - Flash Linker Advance support for uCON64 + +Copyright (c) 2001 Jeff Frohwein +Copyright (c) 2001 NoisyB +Copyright (c) 2001 - 2002 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef FAL_H +#define FAL_H + +extern const st_getopt2_t fal_usage[]; + +#ifdef USE_PARALLEL +extern int fal_read_rom (const char *filename, unsigned int parport, int size); +extern int fal_write_rom (const char *filename, unsigned int parport); +extern int fal_read_sram (const char *filename, unsigned int parport, int bank); +extern int fal_write_sram (const char *filename, unsigned int parport, int bank); +#endif + +#endif diff --git a/ucon64/2.0/src/backup/ffe.c b/ucon64/2.0/src/backup/ffe.c new file mode 100644 index 0000000..c71f075 --- /dev/null +++ b/ucon64/2.0/src/backup/ffe.c @@ -0,0 +1,316 @@ +/* +ffe.c - General Front Far East copier routines for uCON64 + +Copyright (c) 2002 - 2004 dbjh +Copyright (c) 2003 JohnDie + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include "misc/misc.h" // kbhit(), getch() +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "ffe.h" +#include "misc/parallel.h" + + +#ifdef USE_PARALLEL + +#define N_TRY_MAX 65536 // # times to test if copier ready + + +static void ffe_sendb (unsigned char byte); +static unsigned char ffe_wait_while_busy (void); + +static int ffe_port; + + +void +ffe_init_io (unsigned int port) +/* + - sets global `ffe_port'. Then the send/receive functions don't need to pass `port' all + the way to ffe_sendb()/ffe_receiveb(). + - calls init_conio(). Necessary for kbhit() and DOS-like behaviour of getch(). +*/ +{ + ffe_port = port; +#if 0 // we want to support non-standard parallel port addresses + if (ffe_port != 0x3bc && ffe_port != 0x378 && ffe_port != 0x278) + { + fprintf (stderr, "ERROR: PORT must be 0x3bc, 0x378 or 0x278\n"); + exit (1); + } +#endif + +#if (defined __unix__ || defined __BEOS__) && !defined __MSDOS__ + init_conio (); +#endif + + parport_print_info (); +} + + +void +ffe_deinit_io (void) +{ +#if (defined __unix__ || defined __BEOS__) && !defined __MSDOS__ + deinit_conio (); +#endif +} + + +void +ffe_send_block (unsigned short address, unsigned char *buffer, int len) +{ + int n; + unsigned char checksum = 0x81; + + ffe_send_command (0, address, (unsigned short) len); + for (n = 0; n < len; n++) + { + ffe_sendb (buffer[n]); + checksum ^= buffer[n]; + } + ffe_sendb (checksum); +} + + +void +ffe_send_block2 (unsigned short address, unsigned char *buffer, int len) +{ + int n; + unsigned char checksum = 0x81; + + ffe_send_command (2, address, (unsigned short) len); + for (n = 0; n < len; n++) + { + ffe_sendb (buffer[n]); + checksum ^= buffer[n]; + } + ffe_sendb (checksum); +} + + +void +ffe_send_command0 (unsigned short address, unsigned char byte) +// command 0 for 1 byte +{ + ffe_send_command (0, address, 1); + ffe_sendb (byte); + ffe_sendb ((unsigned char) (0x81 ^ byte)); +} + + +unsigned char +ffe_send_command1 (unsigned short address) +// command 1 for 1 byte +{ + unsigned char byte; + + ffe_send_command (1, address, 1); + byte = ffe_receiveb (); + if ((0x81 ^ byte) != ffe_receiveb ()) + puts ("received data is corrupt"); + + return byte; +} + + +void +ffe_send_command (unsigned char command_code, unsigned short a, unsigned short l) +{ + ffe_sendb (0xd5); + ffe_sendb (0xaa); + ffe_sendb (0x96); + ffe_sendb (command_code); + ffe_sendb ((unsigned char) a); // low byte + ffe_sendb ((unsigned char) (a >> 8)); // high byte + ffe_sendb ((unsigned char) l); // low byte + ffe_sendb ((unsigned char) (l >> 8)); // high byte + ffe_sendb ((unsigned char) (0x81 ^ command_code ^ a ^ (a >> 8) ^ l ^ (l >> 8))); // check sum +} + + +void +ffe_sendb (unsigned char byte) +{ + ffe_wait_for_ready (); + outportb ((unsigned short) (ffe_port + PARPORT_DATA), byte); + outportb ((unsigned short) (ffe_port + PARPORT_CONTROL), + (unsigned char) (inportb ((unsigned short) // invert strobe + (ffe_port + PARPORT_CONTROL)) ^ PARPORT_STROBE)); + ffe_wait_for_ready (); // necessary if followed by ffe_receiveb() +} + + +void +ffe_receive_block (unsigned short address, unsigned char *buffer, int len) +{ + volatile int n; + int n_try = 0; + unsigned char checksum1, checksum2; + + do + { + checksum1 = 0x81; + ffe_send_command (1, address, (unsigned short) len); + for (n = 0; n < len; n++) + { + buffer[n] = ffe_receiveb (); + checksum1 ^= buffer[n]; + } + checksum2 = ffe_receiveb (); + + for (n = 0; n < 65536; n++) // a delay is necessary here + ; + + n_try++; + } + while ((checksum1 != checksum2) && (n_try < N_TRY_MAX)); + + if (checksum1 != checksum2) + puts ("\nreceived data is corrupt"); +} + + +void +ffe_receive_block2 (unsigned short address, unsigned char *buffer, int len) +{ + volatile int n; + int n_try = 0; + unsigned char checksum1, checksum2; + + do + { + checksum1 = 0x81; + ffe_send_command (3, address, (unsigned short) len); + for (n = 0; n < len; n++) + { + buffer[n] = ffe_receiveb (); + checksum1 ^= buffer[n]; + } + checksum2 = ffe_receiveb (); + + for (n = 0; n < 65536; n++) // a delay is necessary here + ; + + n_try++; + } + while ((checksum1 != checksum2) && (n_try < N_TRY_MAX)); + + if (checksum1 != checksum2) + puts ("\nreceived data is corrupt"); +} + + +unsigned char +ffe_receiveb (void) +{ + unsigned char byte; + + byte = (unsigned char) ((ffe_wait_while_busy () & PARPORT_INPUT_MASK) >> 3); // receive low nibble + outportb ((unsigned short) (ffe_port + PARPORT_CONTROL), + (unsigned char) (inportb ((unsigned short) // invert strobe + (ffe_port + PARPORT_CONTROL)) ^ PARPORT_STROBE)); + byte |= (unsigned char) ((ffe_wait_while_busy () & PARPORT_INPUT_MASK) << 1); // receive high nibble + outportb ((unsigned short) (ffe_port + PARPORT_CONTROL), + (unsigned char) (inportb ((unsigned short) // invert strobe + (ffe_port + PARPORT_CONTROL)) ^ PARPORT_STROBE)); + + return byte; +} + + +unsigned char +ffe_wait_while_busy (void) +{ + unsigned char input; + int n_try = 0; + + do + { + input = inportb ((unsigned short) (ffe_port + PARPORT_STATUS)); + n_try++; + } + while (input & PARPORT_IBUSY && n_try < N_TRY_MAX); + +#if 0 +/* + VGS doesn't check for this, and it seems to happen quite regularly, so it + is currently commented out +*/ + if (n_try >= N_TRY_MAX) + { + fputs ("ERROR: The copier is not ready\n" // yes, "ready" :-) + " Turn it off for a few seconds then turn it on and try again\n", + stderr); + exit (1); + } +#endif + + // read port again to let data settle down and to delay a little bit - JohnDie + return inportb ((unsigned short) (ffe_port + PARPORT_STATUS)); +} + + +void +ffe_wait_for_ready (void) +{ + unsigned char input; + int n_try = 0; + + do + { + input = inportb ((unsigned short) (ffe_port + PARPORT_STATUS)); + n_try++; + } + while (!(input & PARPORT_IBUSY) && n_try < N_TRY_MAX); + +#if 0 + if (n_try >= N_TRY_MAX) + { + fputs ("ERROR: The copier is not ready\n" + " Turn it off for a few seconds then turn it on and try again\n", + stderr); + exit (1); + } +#endif +} + + +void +ffe_checkabort (int status) +{ + if (((!ucon64.frontend) ? kbhit () : 0) && getch () == 'q') + { +// ffe_send_command (5, 0, 0); // VGS: when sending/receiving a SNES ROM + puts ("\nProgram aborted"); + exit (status); + } +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/ffe.h b/ucon64/2.0/src/backup/ffe.h new file mode 100644 index 0000000..e861cbb --- /dev/null +++ b/ucon64/2.0/src/backup/ffe.h @@ -0,0 +1,80 @@ +/* +ffe.h - General Front Far East copier routines for uCON64 + +Copyright (c) 2002 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef FFE_H +#define FFE_H + +/* + 0 - Low byte of 8 kB page count + SMD: 16 kB page count + 1 - High byte of 8 kB page count + SMD: File ID code 0 (3, not: high byte of 16 kB page count) + Magic Griffin: Emulation mode select, first byte? + 2 - Emulation mode select (SWC/SMC/Magic Griffin, second byte?) + Bit 7 6 5 4 3 2 1 0 + x : 1 = Run in mode 0 (JMP $8000) (higher precedence than bit 1) + x : 0 = Last file of the ROM dump (multi-file) + : 1 = Multi-file (there is another file to follow) + x : SWC & SMC: + 0 = SRAM mapping mode 1 (LoROM) + 1 = mode 2 (HiROM) + x : SWC & SMC: + 0 = DRAM mapping mode 20 (LoROM) + 1 = mode 21 (HiROM) + x x : SWC & SMC (SRAM size): + 00 = 256 kb, 01 = 64 kb, 10 = 16 kb, 11 = no SRAM + x : SWC & SMC: + 0 = Run in mode 3 + 1 = Run in mode 2 (JMP RESET) + x : 0 = Disable, 1 = Enable external cartridge memory + image at bank 20-5F,A0-DF in system mode 2/3) + 3-7 - 0, reserved + 8 - File ID code 1 (0xaa) + 9 - File ID code 2 (0xbb) + 10 - File type; check this byte only if ID 1 & 2 match + 1 : Super Magic Card saver data + 2 : Magic Griffin program (PC-Engine) + 3 : Magic Griffin SRAM data + 4 : SNES program + 5 : SWC & SMC password, SRAM data + 6 : Mega Drive program + 7 : SMD SRAM data + 8 : SWC & SMC saver data + 11-511 - 0, reserved +*/ + +#ifdef USE_PARALLEL + +extern void ffe_init_io (unsigned int port); +extern void ffe_deinit_io (void); +extern void ffe_send_block (unsigned short address, unsigned char *buffer, int len); +extern void ffe_send_block2 (unsigned short address, unsigned char *buffer, int len); +extern void ffe_send_command0 (unsigned short address, unsigned char byte); +extern unsigned char ffe_send_command1 (unsigned short address); +extern void ffe_send_command (unsigned char command_code, unsigned short a, unsigned short l); +extern void ffe_receive_block (unsigned short address, unsigned char *buffer, int len); +extern void ffe_receive_block2 (unsigned short address, unsigned char *buffer, int len); +extern unsigned char ffe_receiveb (void); +extern void ffe_wait_for_ready (void); +extern void ffe_checkabort (int status); + +#endif // USE_PARALLEL + +#endif // FFE_H diff --git a/ucon64/2.0/src/backup/fig.c b/ucon64/2.0/src/backup/fig.c new file mode 100644 index 0000000..dcfb082 --- /dev/null +++ b/ucon64/2.0/src/backup/fig.c @@ -0,0 +1,739 @@ +/* +fig.c - Super PRO Fighter support for uCON64 + +Copyright (c) 1999 - 2002 NoisyB +Copyright (c) 2001 - 2004 dbjh +Copyright (c) 2003 - 2004 JohnDie + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include "misc/misc.h" +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "misc/parallel.h" +#include "misc/file.h" +#include "ucon64.h" +#include "ucon64_misc.h" +#include "ffe.h" +#include "fig.h" +#include "console/snes.h" // for snes_get_snes_hirom() + + +const st_getopt2_t fig_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Super Pro Fighter (Q/Q+)/Pro Fighter X (Turbo 2)/Double Pro Fighter (X Turbo)" + /*"1993/1994/19XX China Coach Limited/CCL http://www.ccltw.com.tw"*/, + NULL + }, +#ifdef USE_PARALLEL + { + "xfig", 0, 0, UCON64_XFIG, + NULL, "send/receive ROM to/from *Pro Fighter*/FIG; " OPTION_LONG_S "port=PORT\n" + "receives automatically when ROM does not exist", + &ucon64_wf[WF_OBJ_SNES_DEFAULT_STOP_NO_SPLIT_NO_ROM] + }, + { + "xfigs", 0, 0, UCON64_XFIGS, + NULL, "send/receive SRAM to/from *Pro Fighter*/FIG; " OPTION_LONG_S "port=PORT\n" + "receives automatically when SRAM does not exist", + &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM] + }, + { + "xfigc", 0, 0, UCON64_XFIGC, NULL, + "send/receive SRAM to/from cartridge in *Pro Fighter*/FIG;\n" OPTION_LONG_S "port=PORT\n" + "receives automatically when SRAM does not exist", +// "Press q to abort; ^C might cause invalid state of backup unit" + &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM] + }, +#endif + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +#ifdef USE_PARALLEL + +#define BUFFERSIZE 8192 // don't change, only 8192 works! + + +static int receive_rom_info (unsigned char *buffer); +static int get_rom_size (unsigned char *info_block); +static int check1 (unsigned char *info_block, int index); +static int check2 (unsigned char *info_block, int index, unsigned char value); +static int check3 (unsigned char *info_block, int index1, int index2, int size); +static void handle_swc_header (unsigned char *header); + +static int hirom; + + +#if BUFFERSIZE < 512 +#error receive_rom_info() and fig_read_sram() expect BUFFERSIZE to be at least \ + 512 bytes. +#endif +int +receive_rom_info (unsigned char *buffer) +/* + - returns size of ROM in Mb (128 kB) units + - sets global `hirom' +*/ +{ + int n, size; + volatile int m; + unsigned char byte; + + ffe_send_command0 (0xe00c, 0); + + if (UCON64_ISSET (ucon64.snes_hirom)) + hirom = ucon64.snes_hirom ? 1 : 0; + else + { + ffe_send_command (5, 3, 0); + byte = ffe_send_command1 (0xbfd5); + if ((byte & 1 && byte != 0x23) || byte == 0x3a) // & 1 => 0x21, 0x31, 0x35 + hirom = 1; + } + + for (n = 0; n < (int) FIG_HEADER_LEN; n++) + { + for (m = 0; m < 65536; m++) // a delay is necessary here + ; + ffe_send_command (5, (unsigned short) (0x200 + n), 0); + buffer[n] = ffe_send_command1 (0xa0a0); + } + + size = get_rom_size (buffer); + if (hirom) + size <<= 1; + + return size; +} + + +int +get_rom_size (unsigned char *info_block) +// returns size of ROM in Mb units +{ + if (check1 (info_block, 0)) + return 0; + if (check2 (info_block, 0x10, 0x84)) + return 0; + if (check3 (info_block, 0, 0x20, 0x20)) + return 2; + if (check3 (info_block, 0, 0x40, 0x20)) + return 4; + if (check3 (info_block, 0x40, 0x60, 0x20)) + return 6; + if (check3 (info_block, 0, 0x80, 0x10)) + return 8; + if (check1 (info_block, 0x80)) + return 8; + if (check3 (info_block, 0x80, 0x90, 0x10)) + return 8; + if (check2 (info_block, 0x80, 0xa0)) + return 8; + if (check3 (info_block, 0x80, 0xa0, 0x20)) + return 0xa; + if (check1 (info_block, 0xc0)) + return 0xc; + if (check2 (info_block, 0xc0, 0xb0)) + return 0xc; + if (check3 (info_block, 0x80, 0xc0, 0x20)) + return 0xc; + if (check3 (info_block, 0x100, 0, 0x10)) + return 0x10; + if (check2 (info_block, 0x100, 0xc0)) + return 0x10; + if (check3 (info_block, 0x100, 0x120, 0x10)) + return 0x12; + if (check3 (info_block, 0x100, 0x140, 0x10)) + return 0x14; + if (check2 (info_block, 0x140, 0xd0)) + return 0x14; + if (check3 (info_block, 0x100, 0x180, 0x10)) + return 0x18; + if (check2 (info_block, 0x180, 0xe0)) + return 0x18; + if (check3 (info_block, 0x180, 0x1c0, 0x10)) + return 0x1c; + if (check3 (info_block, 0x1f0, 0x1f0, 0x10)) + return 0x20; + + return 0; +} + + +int +check1 (unsigned char *info_block, int index) +{ + int n; + + for (n = 0; n < 16; n++) + if (info_block[n + index] != info_block[index]) + return 0; + + return 1; +} + + +int +check2 (unsigned char *info_block, int index, unsigned char value) +{ + int n; + + for (n = 0; n < 4; n++) + if (info_block[n + index] != value) + return 0; + + return 1; +} + + +int +check3 (unsigned char *info_block, int index1, int index2, int size) +{ + int n; + + for (n = 0; n < size; n++) + if (info_block[n + index1] != info_block[n + index2]) + return 0; + + return 1; +} + + +void +handle_swc_header (unsigned char *header) +{ + if ((header[2] & 0x10) == 0x10) + { // HiROM + header[3] |= 0x80; + + if ((header[2] & 0x0c) == 0x0c) // 0 Kbit SRAM + { + header[4] = 0x77; + header[5] = 0x83; + } + else if (((header[2] & 0x0c) == 0x08) || // 16 *or* 64 Kbit SRAM + ((header[2] & 0x0c) == 0x04)) + { + header[4] = 0xdd; + header[5] = 0x82; + } + else // 256 Kbit SRAM + { + header[4] = 0xdd; + header[5] = 0x02; + } + } + else + { // LoROM + header[3] &= 0x7f; + + if ((header[2] & 0x0c) == 0x0c) // 0 Kbit SRAM + { + header[4] = 0x77; + header[5] = 0x83; + } + else if (((header[2] & 0x0c) == 0x08) || // 16 *or* 64 Kbit SRAM + ((header[2] & 0x0c) == 0x04)) + { + header[4] = 0x00; + header[5] = 0x80; + } + else // 256 Kbit SRAM + { + header[4] = 0x00; + header[5] = 0x00; + } + } +} + + +int +fig_read_rom (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer; + int n, size, blocksleft, bytesreceived = 0; + unsigned short address1, address2; + time_t starttime; + st_rominfo_t rominfo; + + ffe_init_io (parport); + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + size = receive_rom_info (buffer); + if (size == 0) + { + fprintf (stderr, "ERROR: There is no cartridge present in the Pro Fighter\n"); + fclose (file); + remove (filename); + exit (1); + } + blocksleft = size * 16; // 1 Mb (128 kB) unit == 16 8 kB units + printf ("Receive: %d Bytes (%.4f Mb)\n", size * MBIT, (float) size); + size *= MBIT; // size in bytes for ucon64_gauge() below + + ffe_send_command (5, 0, 0); + ffe_send_command0 (0xe00c, 0); + ffe_send_command0 (0xe003, 0); +// byte = ffe_send_command1 (0xbfd8); + + memset (buffer, 0, FIG_HEADER_LEN); + fwrite (buffer, 1, FIG_HEADER_LEN, file); // write temporary empty header + + if (hirom) + blocksleft >>= 1; + + printf ("Press q to abort\n\n"); // print here, NOT before first FIG I/O, + // because if we get here q works ;-) + address1 = 0x300; // address1 = 0x100, address2 = 0 should + address2 = 0x200; // also work + starttime = time (NULL); + while (blocksleft > 0) + { + if (hirom) + for (n = 0; n < 4; n++) + { + ffe_send_command (5, address1, 0); + ffe_receive_block (0x2000, buffer, BUFFERSIZE); + address1++; + fwrite (buffer, 1, BUFFERSIZE, file); + + bytesreceived += BUFFERSIZE; + ucon64_gauge (starttime, bytesreceived, size); + ffe_checkabort (2); + } + + for (n = 0; n < 4; n++) + { + ffe_send_command (5, address2, 0); + ffe_receive_block (0xa000, buffer, BUFFERSIZE); + blocksleft--; + address2++; + fwrite (buffer, 1, BUFFERSIZE, file); + + bytesreceived += BUFFERSIZE; + ucon64_gauge (starttime, bytesreceived, size); + ffe_checkabort (2); + } + } + ffe_send_command (5, 0, 0); + + // Create a correct header. We can't obtain the header from the Pro Fighter + // unless a (the same) cartridge was just dumped to diskette... + ucon64.rom = filename; + ucon64.file_size = size + FIG_HEADER_LEN; + // override everything we know for sure + ucon64.console = UCON64_SNES; + ucon64.buheader_len = FIG_HEADER_LEN; + ucon64.split = 0; + ucon64.snes_hirom = hirom ? SNES_HIROM : 0; + ucon64.interleaved = 0; + memset (&rominfo, 0, sizeof (st_rominfo_t)); + + fflush (file); + snes_init (&rominfo); + memset (buffer, 0, FIG_HEADER_LEN); + snes_set_fig_header (&rominfo, (st_fig_header_t *) buffer); + fseek (file, 0, SEEK_SET); + fwrite (buffer, 1, FIG_HEADER_LEN, file); // write correct header + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + + +int +fig_write_rom (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer; + int bytesread = 0, bytessend, totalblocks, blocksdone = 0, blocksleft, fsize, + n, emu_mode_select; + unsigned short address1, address2; + time_t starttime; + + ffe_init_io (parport); + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + fsize = fsizeof (filename); + printf ("Send: %d Bytes (%.4f Mb)\n", fsize, (float) fsize / MBIT); + + ffe_send_command0 (0xc008, 0); + fread (buffer, 1, FIG_HEADER_LEN, file); + + if (snes_get_file_type () == SWC) + handle_swc_header (buffer); + emu_mode_select = buffer[2]; // this byte is needed later + + ffe_send_command (5, 0, 0); + ffe_send_block (0x400, buffer, FIG_HEADER_LEN); // send header + bytessend = FIG_HEADER_LEN; + + hirom = snes_get_snes_hirom (); + if (hirom) + ffe_send_command0 (0xe00f, 0); // seems to enable HiROM mode, + // value doesn't seem to matter + printf ("Press q to abort\n\n"); // print here, NOT before first FIG I/O, + // because if we get here q works ;-) + totalblocks = (fsize - FIG_HEADER_LEN + BUFFERSIZE - 1) / BUFFERSIZE; // round up + blocksleft = totalblocks; + address1 = 0x300; + address2 = 0x200; + starttime = time (NULL); + while (blocksleft > 0) + { + if (hirom) + for (n = 0; n < 4; n++) + { + bytesread = fread (buffer, 1, BUFFERSIZE, file); + ffe_send_command0 ((unsigned short) 0xc010, (unsigned char) (blocksdone >> 9)); + ffe_send_command (5, address1, 0); + ffe_send_block (0x0000, buffer, bytesread); + address1++; + blocksleft--; + blocksdone++; + + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, fsize); + ffe_checkabort (2); + } + + for (n = 0; n < 4; n++) + { + bytesread = fread (buffer, 1, BUFFERSIZE, file); + ffe_send_command0 ((unsigned short) 0xc010, (unsigned char) (blocksdone >> 9)); + ffe_send_command (5, address2, 0); + ffe_send_block (0x8000, buffer, bytesread); + address2++; + blocksleft--; + blocksdone++; + + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, fsize); + ffe_checkabort (2); + } + } + + if (blocksdone > 0x200) // ROM dump > 512 8 kB blocks (=32 Mb (=4 MB)) + ffe_send_command0 (0xc010, 2); + + ffe_send_command (5, 0, 0); + ffe_send_command (6, (unsigned short) (1 | (emu_mode_select << 8)), 0); + + ffe_wait_for_ready (); + outportb ((unsigned short) (parport + PARPORT_DATA), 0); + outportb ((unsigned short) (parport + PARPORT_CONTROL), + (unsigned char) (inportb ((unsigned short) // invert strobe + (parport + PARPORT_CONTROL)) ^ PARPORT_STROBE)); + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + + +int +fig_read_sram (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer; + int blocksleft, bytesreceived = 0; + unsigned short address; + time_t starttime; + + ffe_init_io (parport); + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + printf ("Receive: %d Bytes\n", 32 * 1024); + memset (buffer, 0, FIG_HEADER_LEN); +#if 0 // Not needed for FIG, as size is always 4 blocks + buffer[0] = 4; // 32 kB == 4*8 kB, "size_high" is already 0 +#endif + fwrite (buffer, 1, FIG_HEADER_LEN, file); + + ffe_send_command (5, 0, 0); + ffe_send_command0 (0xe00d, 0); + ffe_send_command0 (0xc008, 0); + + printf ("Press q to abort\n\n"); // print here, NOT before first FIG I/O, + // because if we get here q works ;-) + blocksleft = 4; // SRAM is 4*8 kB + address = 0x100; + starttime = time (NULL); + while (blocksleft > 0) + { + ffe_send_command (5, address, 0); + ffe_receive_block (0x2000, buffer, BUFFERSIZE); + blocksleft--; + address++; + fwrite (buffer, 1, BUFFERSIZE, file); + + bytesreceived += BUFFERSIZE; + ucon64_gauge (starttime, bytesreceived, 32 * 1024); + ffe_checkabort (2); + } + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + + +int +fig_write_sram (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer; + int bytesread, bytessend = 0, size; + unsigned short address; + time_t starttime; + + ffe_init_io (parport); + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + size = fsizeof (filename) - FIG_HEADER_LEN; // FIG SRAM is 4*8 kB, emu SRAM often not + printf ("Send: %d Bytes\n", size); + fseek (file, FIG_HEADER_LEN, SEEK_SET); // skip the header + + ffe_send_command (5, 0, 0); + ffe_send_command0 (0xe00d, 0); + ffe_send_command0 (0xc008, 0); + + printf ("Press q to abort\n\n"); // print here, NOT before first FIG I/O, + // because if we get here q works ;-) + address = 0x100; + starttime = time (NULL); + while ((bytesread = fread (buffer, 1, BUFFERSIZE, file))) + { + ffe_send_command (5, address, 0); + ffe_send_block (0x2000, buffer, bytesread); + address++; + + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, size); + ffe_checkabort (2); + } + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + + +int +fig_read_cart_sram (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer, byte; + int bytesreceived = 0, size; + unsigned short address; + time_t starttime; + + ffe_init_io (parport); + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + size = receive_rom_info (buffer); + if (size == 0) + { + fprintf (stderr, "ERROR: There is no cartridge present in the Pro Fighter\n"); + fclose (file); + remove (filename); + exit (1); + } + + ffe_send_command (5, 3, 0); // detect cartridge SRAM size because + ffe_send_command0 (0xe00c, 0); // we don't want to read too few data + byte = ffe_send_command1 (0xbfd8); + + size = MAX ((byte ? 1 << (byte + 10) : 0), 32 * 1024); + printf ("Receive: %d Bytes\n", size); + + memset (buffer, 0, FIG_HEADER_LEN); +#if 0 // Not needed for FIG, as size is always 4 blocks + buffer[0] = 4; // 32 kB == 4*8 kB, "size_high" is already 0 +#endif + fwrite (buffer, 1, FIG_HEADER_LEN, file); + + ffe_send_command (5, 0, 0); + ffe_send_command0 (0xe00c, 0); +// ffe_send_command0 (0xc008, 0); + + printf ("Press q to abort\n\n"); // print here, NOT before first FIG I/O, + // because if we get here q works ;-) + address = hirom ? 0x2c3 : 0x1c0; + + starttime = time (NULL); + while (bytesreceived < size) + { + ffe_send_command (5, address, 0); + ffe_receive_block ((unsigned short) (hirom ? 0x6000 : 0x2000), buffer, BUFFERSIZE); + fwrite (buffer, 1, BUFFERSIZE, file); + address += hirom ? 4 : 1; + + bytesreceived += BUFFERSIZE; + ucon64_gauge (starttime, bytesreceived, size); + ffe_checkabort (2); + } + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + + +int +fig_write_cart_sram (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer, byte; + int bytesread, bytessend = 0, size; + unsigned short address; + time_t starttime; + + ffe_init_io (parport); + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + size = receive_rom_info (buffer); + if (size == 0) + { + fprintf (stderr, "ERROR: There is no cartridge present in the Pro Fighter\n"); + fclose (file); + remove (filename); + exit (1); + } + + ffe_send_command (5, 3, 0); // detect cartridge SRAM size because we don't + ffe_send_command0 (0xe00c, 0); // want to write more data than necessary + byte = ffe_send_command1 (0xbfd8); + + size = fsizeof (filename) - FIG_HEADER_LEN; // FIG SRAM is 4*8 kB, emu SRAM often not + size = MIN ((byte ? 1 << (byte + 10) : 0), size); + + printf ("Send: %d Bytes\n", size); + fseek (file, FIG_HEADER_LEN, SEEK_SET); // skip the header + + ffe_send_command (5, 0, 0); + ffe_send_command0 (0xe00c, 0); +// ffe_send_command0 (0xc008, 0); + + printf ("Press q to abort\n\n"); // print here, NOT before first FIG I/O, + // because if we get here q works ;-) + address = hirom ? 0x2c3 : 0x1c0; + + starttime = time (NULL); + while ((bytessend < size) && (bytesread = fread (buffer, 1, MIN (size, BUFFERSIZE), file))) + { + ffe_send_command (5, address, 0); + ffe_send_block ((unsigned short) (hirom ? 0x6000 : 0x2000), buffer, bytesread); + address += hirom ? 4 : 1; + + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, size); + ffe_checkabort (2); + } + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/fig.h b/ucon64/2.0/src/backup/fig.h new file mode 100644 index 0000000..ab36e1d --- /dev/null +++ b/ucon64/2.0/src/backup/fig.h @@ -0,0 +1,100 @@ +/* +fig.h - Super PRO Fighter support for uCON64 + +Copyright (c) 1999 - 2002 NoisyB +Copyright (c) 2001 - 2003 dbjh +Copyright (c) 2003 JohnDie + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef FIG_H +#define FIG_H + +extern const st_getopt2_t fig_usage[]; + +#ifdef USE_PARALLEL +#endif // USE_PARALLEL + +/* +Super Pro Fighter (FIG) Header Format +Last edited: 19.06.2002 + +Offset | Content +-------------+------------------------------------ +$0000 | Lo-Byte of 8K-Block# +-------------+------------------------------------ +$0001 | Hi-Byte of 8K-Block# +-------------+------------------------------------ +$0002 | $00 = Last File + | $40 = More Files Present +-------------+------------------------------------ +$0003 | $00 = LoROM + | $80 = HiROM +-------------+------------------------------------ +$0004-$0005 | $77 $83 = No SRAM (LoROM) + | $00 $80 = 16 KBit (LoROM) + | $00 $80 = 64 KBit (LoROM) + | $00 $00 = 256 KBit (LoROM) + | $47 $83 = No SRAM (LoROM) (DSP) + | $11 $02 = 256 KBit (LoROM) (SFX) + | $77 $83 = No SRAM (HiROM) + | $DD $82 = 16 KBit (HiROM) + | $DD $82 = 64 KBit (HiROM) + | $DD $02 = 256 KBit (HiROM) + | $F7 $83 = No SRAM (HiROM) (DSP) + | $FD $82 = 16 KBit (HiROM) (DSP) +-------------+------------------------------------ +$0006-$01FF | Reserved (=$00) + + + +NOTE 1: The Super Pro Fighter does not distinguish between 16 KBit SRAM + and 64 KBit SRAM. + +NOTE 2: When splitting files, the SPF writes all relevant header fields + to all files. So each file has the same header with exception of + the last one, because it has $0002 set to $00 to indicate that it + is the last file. +*/ +typedef struct st_fig_header +{ +/* + Don't create fields that are larger than one byte! For example size_low and size_high + could be combined in one unsigned short int. However, this gives problems with little + endian vs. big endian machines (e.g. writing the header to disk). +*/ + unsigned char size_low; + unsigned char size_high; + unsigned char multi; + unsigned char hirom; + unsigned char emulation1; + unsigned char emulation2; + unsigned char pad[506]; +} st_fig_header_t; + +#define FIG_HEADER_START 0 +#define FIG_HEADER_LEN (sizeof (st_fig_header_t)) + +#ifdef USE_PARALLEL +extern int fig_read_rom (const char *filename, unsigned int parport); +extern int fig_write_rom (const char *filename, unsigned int parport); +extern int fig_read_sram (const char *filename, unsigned int parport); +extern int fig_write_sram (const char *filename, unsigned int parport); +extern int fig_read_cart_sram (const char *filename, unsigned int parport); +extern int fig_write_cart_sram (const char *filename, unsigned int parport); +#endif + +#endif // FIG_H diff --git a/ucon64/2.0/src/backup/gbx.c b/ucon64/2.0/src/backup/gbx.c new file mode 100644 index 0000000..a578db7 --- /dev/null +++ b/ucon64/2.0/src/backup/gbx.c @@ -0,0 +1,1991 @@ +/* +gbx.c - Game Boy Xchanger/GBDoctor support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2001 - 2004 dbjh +Based on gbt15.c - Copyright (c) Bung Enterprises + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/******************************************************************** +* use parallel EPP/SPP port to r/w game boy cartridge * +* * +* ai[]=0 w a[7..0] * +* ai[]=1 w a[15..8] * +* ai[]=2 w control d7=rs,d6=spp,d1=xwe_en,d0=cs_en * +* ai[]=3 r/w data * +* * +* MBC1 * +* R/W A000~BFFF RAM SWITCHING BANK(256Kbit) 4 BANKS OF 8Kbyte * +* R 4000~7FFF ROM SWITCHING BANK(4Mbit) 32 BANKS OF 128Kbit * +* W 2000~3FFF SET ROM BANK (5 BIT) * +* R 0000~3FFF FIX ROM BANK 0 * +* W 4000~5FFF SET RAM BANK (2 BIT) * +* W 0000~1FFF SET 0A ENABLE RAM BANK * +* * +* MBC2 * +* R/W A000~BFFF 512 X 4 BIT RAM * +* R 4000~7FFF ROM SWITCHING BANK(2Mbit) 16 BANKS OF 128Kbit * +* W 2100 SET ROM BANK (4 BIT) * +* R 0000~3FFF FIX ROM BANK 0 * +* W 0000 SET 0A ENABLE RAM BANK * +* * +* MBC5 * +* R/W A000~BFFF RAM SWITCHING BANK(1Mbit) 16 BANKS OF 64 Kbit * +* R 4000~7FFF ROM SWITCHING BANK(64Mbit) 512 BANKS OF 128Kbit * +* W 3000~3FFF SET ROM BANK1(BANK Q8) TOTAL 9 BIT * +* W 2000~2FFF SET ROM BANK0(BANK Q7~Q0) * +* R 0000~3FFF FIX ROM BANK 0 * +* W 4000~7FFF SET RAM BANK (4 BIT) * +* W 0000~1FFF SET 0A ENABLE RAM BANK * +* * +********************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include "misc/misc.h" +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "misc/parallel.h" +#include "misc/file.h" +#include "ucon64.h" +#include "ucon64_misc.h" +#include "gbx.h" +#include "console/gb.h" // gb_logodata, rocket_logodata + + +const st_getopt2_t gbx_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Game Boy Xchanger/GBDoctor"/*"19XX Bung Enterprises Ltd http://www.bung.com.hk"*/, + NULL + }, +#ifdef USE_PARALLEL + { + "xgbx", 0, 0, UCON64_XGBX, + NULL, "send/receive ROM to/from GB Xchanger; " OPTION_LONG_S "port=PORT\n" + "receives automatically when ROM does not exist", + &ucon64_wf[WF_OBJ_GB_DEFAULT_STOP_NO_ROM] + }, + { + "xgbxs", 0, 0, UCON64_XGBXS, + NULL, "send/receive SRAM to/from GB Xchanger; " OPTION_LONG_S "port=PORT\n" + "receives automatically when SRAM does not exist", + &ucon64_wf[WF_OBJ_GB_STOP_NO_ROM] + }, + { + "xgbxb", 1, 0, UCON64_XGBXB, + "BANK", "send/receive 64 kbits SRAM to/from GB Xchanger BANK\n" + "BANK can be a number from 0 to 15; " OPTION_LONG_S "port=PORT\n" + "receives automatically when ROM does not exist", + &ucon64_wf[WF_OBJ_GB_STOP_NO_ROM] + }, + { + "xgbxm", 0, 0, UCON64_XGBXM, + NULL, "try to enable EPP mode, default is SPP mode", + &ucon64_wf[WF_OBJ_GB_SWITCH] + }, +#endif + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +#ifdef USE_PARALLEL + +//#define set_ai_write outportb (port_a, 5); // ninit=1, nwrite=0 +#define set_data_read outportb (port_a, 0); // ninit=0, nastb=1, nib_sel=0, ndstb=1, nwrite=1 +#define set_data_write outportb (port_a, 1); // ninit=0, nastb=1, nib_sel=0, ndstb=1, nwrite=0 +//#define set_normal outportb (port_a, 4); // ninit=1, nwrite=1 + +typedef enum { UNKNOWN_MBC, BUNG, ROM, MBC1, MBC2, MBC3, MBC5, CAMERA, ROCKET } mbc_t; +typedef enum { UNKNOWN_EEPROM, WINBOND, MX, INTEL } eeprom_t; + +static unsigned short int port_8, port_9, port_a, port_b, port_c, rocket_game_no; +static unsigned char buffer[32768]; +static mbc_t mbc_type; +static eeprom_t eeprom_type; +static parport_mode_t port_mode; + + +static volatile void +delay_us (int n_us) +// Hmmm, usleep() can be used under UNIX, but default under DOS is delay() which +// waits for a number of milliseconds... Better use the same code on all platforms. +{ + volatile int n; + int n_max; + + // it's probably best to use the original strange loop values + if (n_us == 10) + n_max = 0x2000; + else if (n_us == 100) + n_max = 0x10000; + else if (n_us == 20000) + n_max = 0xfffff; + else + n_max = n_us * 1000; + + for (n = 0; n < n_max; n++) + ; +} + + +static void +spp_set_ai (unsigned char ai) +{ + set_data_write +// outportb (port_a, 1); // nastb=1,nib_sel=0,ndstb=1,nwrite=0 + outportb (port_8, ai); // put ai at data bus + outportb (port_a, 9); // nastb=0,nib_sel=0,ndstb=1,nwrite=0 + outportb (port_a, 1); // nastb=1,nib_sel=0,ndstb=1,nwrite=0 + // nastb ~~~~|___|~~~~ +} + + +static void +spp_write_data (unsigned char data) +{ +// outportb (port_a, 1); // nastb=1,nib_sel=0,ndstb=1,nwrite=0 + outportb (port_8, data); // put data at data bus + outportb (port_a, 3); // nastb=1,nib_sel=0,ndstb=0,nwrite=0 + outportb (port_a, 1); // nastb=1,nib_sel=0,ndstb=1,nwrite=0 + // ndstb ~~~~|___|~~~~ +} + + +static void +spp_set_ai_data (unsigned char ai, unsigned char data) +{ + spp_set_ai (ai); + spp_write_data (data); +} + + +static unsigned char +spp_read_data (void) +{ + unsigned char byte; + + set_data_read + outportb (port_a, 2); // nastb=1,nib_sel=0,ndstb=0,nwrite=1 + byte = (inportb (port_9) >> 3) & 0x0f; + outportb (port_a, 6); // nastb=1,nib_sel=1,ndstb=0,nwrite=1 + byte |= (inportb (port_9) << 1) & 0xf0; + outportb (port_a, 0); // nastb=1,nib_sel=0,ndstb=1,nwrite=1 + // nibble_sel ___|~~~ and ndstb ~~~~|___|~~~~ + + return byte; +} + + +static void +epp_set_ai (unsigned char ai) +{ + set_data_write + outportb (port_b, ai); +} + + +static void +epp_set_ai_data (unsigned char ai, unsigned char data) +{ + epp_set_ai (ai); + set_data_write + outportb (port_c, data); +} + + +static void +set_ai (unsigned char ai) +{ + set_data_write + if (port_mode == UCON64_SPP) + spp_set_ai (ai); + else + epp_set_ai (ai); +} + + +static void +set_ai_data (unsigned char ai, unsigned char data) +{ + if (port_mode == UCON64_SPP) + spp_set_ai_data (ai, data); // SPP mode + else + epp_set_ai_data (ai, data); // EPP mode +} + + +static void +write_data (unsigned char data) +{ + if (port_mode == UCON64_SPP) + spp_write_data (data); // SPP write data + else + outportb (port_c, data); // EPP write data +} + + +static unsigned char +read_data (void) +{ + if (port_mode == UCON64_SPP) + return spp_read_data (); // SPP read data + else + return inportb (port_c); // EPP read data +} + + +static void +init_port (void) +{ +#ifndef USE_PPDEV + outportb (port_9, 1); // clear EPP time flag +#endif + set_ai_data ((unsigned char) 2, 0); // rst=0, wei=0(dis.), rdi=0(dis.) + set_ai_data ((unsigned char) 2, 0x80); // rst=1, wei=0(dis.), rdi=0(dis.) +} + + +static void +end_port (void) +{ + set_ai_data ((unsigned char) 2, 0); // rst=0, wei=0(dis.), rdi=0(dis.) + outportb (port_a, 4); // ninit=1, nwrite=1 +} + + +static void +set_adr (unsigned int adr) +{ + set_ai_data ((unsigned char) 0, (unsigned char) adr); // a[7..0] + set_ai_data ((unsigned char) 1, (unsigned char) (adr >> 8)); // a[15..8] + set_ai (3); + set_data_read // ninit=0, nwrite=1 +} + + +static void +set_adr_long (unsigned int adr, int ignore_xh) // real address +{ + unsigned char xh, h, m, l; + + set_ai_data ((unsigned char) 2, 0x80); // disable wr/rd inc. + l = (unsigned char) adr; // a7-a0 + m = (unsigned char) (adr >> 8) & 0x3f; // a13-a8 + h = (unsigned char) (adr >> 14) & 0xff; // a21-a13 + if (h) + m |= 0x40; // > bank 0 + + if (!ignore_xh) + { + xh = (unsigned char) (adr >> 22) & 0x7; // max. 256Mbit + if (xh) + m |= 0x40; // > bank 0 + set_adr (0x3000); // write 3000:xh + set_data_write + write_data (xh); // set ROM bank extend value + } + + set_adr (0x2000); // write 2000:h + set_data_write + write_data (h); // set ROM bank value + set_ai_data ((unsigned char) 1, m); // a[15..8] + set_ai_data ((unsigned char) 0, l); // a[7..0] +} + + +static void +set_bank (unsigned int adr, unsigned char bank) +{ + set_ai_data ((unsigned char) 2, 0x80); // disable inc + set_ai_data ((unsigned char) 0, (unsigned char) adr); // a[7..0] + set_ai_data ((unsigned char) 1, (unsigned char) ((adr >> 8) & 0x7f)); // a[15..8] + set_ai_data ((unsigned char) 3, bank); // write bank no + set_data_read // ninit=0, nwrite=1 +} + + +static void +out_byte_eeprom (unsigned char data) +{ + set_ai_data ((unsigned char) 2, 0x82); // wei enable + set_ai (3); // default write mode +// set_data_read // ninit=0, nwrite=1 + set_data_write + write_data (data); // out data + set_ai_data ((unsigned char) 2, 0x80); // wei disable + set_ai (3); // default write mode +} + + +static void +out_byte (unsigned char data) +{ + set_ai (3); +// set_data_read // ninit=0, nwrite=1 + set_data_write + write_data (data); // out data +} + + +static void +out_data (unsigned char h, unsigned char m, unsigned char l, unsigned char data) +{ + // ai[]=2 w control d7=rs,d1=xwe_en,d0=cs_en + h = ((h << 2) | (m >> 6)) & 0x1f; // maximum bank is 1f + if (h) + m = (m & 0x3f) | 0x40; // > bank 0 + else + m = m & 0x3f; // bank 0 + + set_adr (0x2000); // write 2000:h + set_data_write + write_data (h); // set ROM bank value + set_ai_data ((unsigned char) 1, m); // a[15..8] + set_ai_data ((unsigned char) 0, l); // a[7..0] + out_byte_eeprom (data); // write data to EEPROM +} + + +static void +out_adr2_data (unsigned int adr, unsigned char data) // address shift 1 bit +{ + set_adr_long (adr << 1, 1); // adr x 2 + out_byte_eeprom (data); // write data to EEPROM +} + + +static void +out_adr_data (unsigned int adr, unsigned char data) // real address +{ + set_adr_long (adr, 0); + out_byte_eeprom (data); // write data to EEPROM +} + + +static void +out_adr_data_32k (unsigned int adr, unsigned char data) +{ + set_adr (adr); + out_byte_eeprom (data); // write data to EEPROM +} + + +static unsigned char +read_byte (void) +{ + set_ai (3); // default write mode + set_data_read // ninit=0, nwrite=1 + return read_data (); +} + + +static void +enable_protection (void) +{ +// set_bank (0x2000,0); // set bank 0 + out_data (0, 0x55, 0x55, 0xaa); // adr2,adr1,adr0,data 05555:aa + out_data (0, 0x2a, 0xaa, 0x55); + out_data (0, 0x55, 0x55, 0xa0); +} + + +/* +static void +disable_protection (void) +{ + out_data (0, 0x55, 0x55, 0xaa); // adr2,adr1,adr0,data 05555:aa + out_data (0, 0x2a, 0xaa, 0x55); + out_data (0, 0x55, 0x55, 0x80); + out_data (0, 0x55, 0x55, 0xaa); + out_data (0, 0x2a, 0xaa, 0x55); + out_data (0, 0x55, 0x55, 0x20); + delay_us (20000); +} +*/ + + +#if 0 +static int +data_polling_data (unsigned char last_data) +{ + unsigned int timeout = 0; + + while (timeout++ < 0x07ffffff) + if (((read_byte () ^ last_data) & 0x80) == 0) + return 0; + return 1; // ready to exit the while loop +} +#endif + + +static int +data_polling (void) +{ + unsigned char predata, currdata; + unsigned int timeout = 0; + + predata = read_byte () & 0x40; + while (timeout++ < 0x07ffffff) + { + currdata = read_byte () & 0x40; + if (predata == currdata) + return 0; + predata = currdata; + } + return 1; +} + + +static void +reset_to_read (void) // return to read mode +{ + out_adr2_data (0x5555, 0xaa); // 5555:aa adr2,adr1,adr0,data + out_adr2_data (0x2aaa, 0x55); // 2aaa:55 + out_adr2_data (0x5555, 0xf0); // 5555:f0 +} + + +/* +static void +read_status_reg_cmd (void) +{ + out_adr2_data (0x5555, 0xaa); // 5555:aa adr2,adr1,adr0,data + out_adr2_data (0x2aaa, 0x55); // 2aaa:55 + out_adr2_data (0x5555, 0x70); // 5555:70 +} +*/ + + +static int +wait_status (void) +{ + unsigned temp = read_byte (); // read first status byte + + while ((temp & 0xfc) != 0x80) + { + if ((temp & 0x20) == 0x20 && port_mode == UCON64_SPP) + { + fputs ("\nERROR: Erase failed\n", stderr); + return -1; + } + if ((temp & 0x10) == 0x10) + { + fputs ("\nERROR: Programming failed\n", stderr); + return -2; + } + temp = read_data (); + } +// reset_to_read (); + return 0; +} + + +static int +mx_erase (void) +{ + int retval; + + out_adr2_data (0x5555, 0xaa); // 5555:aa adr2,adr1,adr0,data + out_adr2_data (0x2aaa, 0x55); // 2aaa:55 + out_adr2_data (0x5555, 0x80); // 5555:80 + out_adr2_data (0x5555, 0xaa); // 5555:aa + out_adr2_data (0x2aaa, 0x55); // 2aaa:55 + out_adr2_data (0x5555, 0x10); // 5555:10 + + delay_us (100); +// read_status_reg_cmd (); // send read status reg. cmd + retval = wait_status () ? -1 : 0; + reset_to_read (); + return retval; +} + + +/* +static int +win_erase (void) +{ + out_data (0, 0x55, 0x55, 0xaa); // adr2,adr1,adr0,data 05555:aa + out_data (0, 0x2a, 0xaa, 0x55); + out_data (0, 0x55, 0x55, 0x80); + out_data (0, 0x55, 0x55, 0xaa); + out_data (0, 0x2a, 0xaa, 0x55); + out_data (0, 0x55, 0x55, 0x10); + + delay_us (20000); + if (data_polling ()) + { + fputs ("ERROR: Erase failed\n", stderr); + return -1; + } + else + return 0; +} +*/ + + +static unsigned char +intel_read_status (void) +{ + out_adr_data (0, 0x70); // read status command + return read_byte (); +} + + +static int +intel_check_status (void) +{ + unsigned int time_out = 0x8000; + + while (!(intel_read_status () & 0x80)) + { + time_out--; + if (time_out == 0) + { + fprintf (stderr, "\nERROR: Intel read status time out\n" + " Status = 0x%02x\n", intel_read_status ()); + out_adr_data (0, 0x50); // clear status register + return -1; + } + } + return 0; +} + + +static int +intel_block_erase (unsigned int block) +{ + unsigned int time_out = 0x8000; + + while ((intel_read_status ()) != 0x80) + { + time_out--; + if (time_out == 0) + { + fprintf (stderr, "\nERROR: Intel block erase time out\n" + " Status = 0x%02x\n", intel_read_status ()); + return -1; + } + } + out_adr_data (block, 0x20); // block erase + out_adr_data (block, 0xd0); // write confirm + time_out = 0x8000; + while (!(intel_read_status () & 0x80)) + { + time_out--; + if (time_out == 0) + { + fprintf (stderr, "\nERROR: Intel block erase time out at 0x%x\n" + " Status = 0x%02x\n", block, intel_read_status ()); + out_adr_data (block, 0x50); // clear status register + fprintf (stderr, " Status = 0x%02x\n", intel_read_status ()); + return -1; + } + } + + if ((intel_read_status ()) == 0x80) + return 0; + else + { + fprintf (stderr, "\nERROR: Intel block erase error at 0x%x\n" + " Status = 0x%02x\n", block, intel_read_status ()); + out_adr_data (block, 0x50); // clear status register + fprintf (stderr, " Status = 0x%02x\n", intel_read_status ()); + out_adr_data (0x0000, 0xff); // read array + return -1; + } +} + + +/* +static int +intel_erase (void) +{ + unsigned int block; + for (block = 0; block < 64; block++) + if (intel_block_erase (block * 0x20000)) + return -1; + return 0; +} + + +static int +erase (void) +{ + if (eeprom_type == WINBOND) + return win_erase (); + else if (eeprom_type == MX) + return mx_erase (); + else if (eeprom_type == INTEL) + return intel_erase (); + + fputs ("ERROR: Unknown EEPROM type\n", stderr); + return -1; +} +*/ + + +static int +check_eeprom (void) +{ + int i; + + // check 4M flash + out_adr_data_32k (0x5555, 0xaa); // software product ID entry + out_adr_data_32k (0x2aaa, 0x55); + out_adr_data_32k (0x5555, 0x80); + out_adr_data_32k (0x5555, 0xaa); + out_adr_data_32k (0x2aaa, 0x55); + out_adr_data_32k (0x5555, 0x60); + + delay_us (10); + set_adr (0); // adr2,adr1,adr0 + if (read_byte () == 0xda) // manufacturer code + { + set_adr (1); // adr2,adr1,adr0 + if (read_byte () == 0x46) // device code + { + out_adr_data_32k (0x5555, 0xaa); // software product ID exit + out_adr_data_32k (0x2aaa, 0x55); // adr2,adr1,adr0,data + out_adr_data_32k (0x5555, 0xf0); // adr2,adr1,adr0,data + eeprom_type = WINBOND; // Winbond 4 Mbit flash + return 0; + } + } + + // check 16M flash + out_adr2_data (0x5555, 0xaa); // 5555:aa software product ID entry + out_adr2_data (0x2aaa, 0x55); // 2aaa:55 adr2,adr1,adr0,data + out_adr2_data (0x5555, 0x90); // 5555:90 adr2,adr1,adr0,data + + set_adr (0); // adr2,adr1,adr0 + if (read_byte () == 0xc2) // manufacturer code + { + set_adr (2); // adr2,adr1,adr0 + if (read_byte () == 0xf1) // device code + { + reset_to_read (); // reset to read mode + eeprom_type = MX; // MX 16 Mbit flash + return 0; + } + } + + // check 64M flash + reset_to_read (); + init_port (); + out_adr_data (0x0000, 0x98); // read query + for (i = 0; i < 128; i += 2) + { + set_adr (i); // adr2,adr1,adr0 + buffer[i / 2] = read_byte (); + } +// dumper (stdout, buffer, 64, 0, DUMPER_HEX); + + if (buffer[0] == 0x89 && buffer[1] == 0x15 + && buffer[0x10] == 'Q' && buffer[0x11] == 'R' && buffer[0x12] == 'Y') + { + out_adr_data (0x0000, 0xff); // read array + eeprom_type = INTEL; // Intel 64 Mbit flash + return 0; + } + +// eeprom_type = UNKNOWN_EEPROM; + return 1; +} + + +static void +check_mbc (void) +// determine memory bank controller type +{ + if (eeprom_type != UNKNOWN_EEPROM) + mbc_type = BUNG; + else + { + unsigned char rom_type = buffer[0x47]; + + if (memcmp (buffer + 4, rocket_logodata, GB_LOGODATA_LEN) == 0) + mbc_type = ROCKET; // rom_type == 0x97 || rom_type == 0x99 + else if (rom_type == 0) + mbc_type = ROM; + else if ((rom_type >= 1 && rom_type <= 3) || rom_type == 0xff) + mbc_type = MBC1; + else if (rom_type == 5 || rom_type == 6) + mbc_type = MBC2; + else if (rom_type >= 0x0f && rom_type <= 0x13) + mbc_type = MBC3; + else if (rom_type >= 0x19 && rom_type <= 0x1e) + mbc_type = MBC5; + else if (rom_type == 0x1f) + mbc_type = CAMERA; + else + mbc_type = UNKNOWN_MBC; + } +} + + +static int +check_card (void) +/* + 16 kB ROM = 1 bank + 8 kB RAM = 1 bank +*/ +{ + unsigned char sum = 0; + char game_name[16]; + int i; + + puts ("Checking ROM data..."); + + if (mbc_type == ROCKET) + puts ("NOTE: Rocket Games cartridge detected"); + else if (memcmp (buffer + 4, gb_logodata, GB_LOGODATA_LEN) != 0) + { + puts ("WARNING: Cartridge does not contain official Nintendo logo data"); + dumper (stdout, buffer, 0x50, 0x100, DUMPER_HEX); + } + + memcpy (game_name, buffer + 0x34, 15); + game_name[15] = 0; + for (i = 0; i < 15; i++) + if (!isprint ((int) game_name[i]) && game_name[i] != 0) + game_name[i] = '.'; + printf ("Game name: \"%s\"\n", game_name); + + if (buffer[0x48] > 8) // ROM size + printf ("NOTE: Strange ROM size byte value in header (0x%02x)\n", buffer[0x48]); + + if (buffer[0x49] > 5) // SRAM size + printf ("NOTE: Strange RAM size byte value in header (0x%02x)\n", buffer[0x49]); + +/* + // [47] = ROM type + if (buffer[0x47] > 0 && buffer[0x47] < 4 && buffer[0x48] > 4) + mbc1_exp = 1; // MBC1 8 Mbit/16 Mbit +*/ + + for (i = 0x34; i < 0x4d; i++) + sum += ~buffer[i]; + if (buffer[0x4d] != sum) + printf ("NOTE: Incorrect header checksum (0x%02x), should be 0x%02x\n", + buffer[0x4d], sum); + + return (1 << (buffer[0x48] > 8 ? 9 : buffer[0x48])) * 32 * 1024; +} + + +static void +set_sram_bank (unsigned char bank) +// original code only did "set_adr (0x4000); out_byte (bank);" +{ + if (eeprom_type != UNKNOWN_EEPROM) + { // flash card + set_adr (0x4000); // set SRAM adr + out_byte (bank); // SRAM bank 0 + // this should be equivalent to the code above: set_bank (0x4000, bank) + } + else + { // game cartridge + switch (mbc_type) + { + case MBC1: + set_bank (0x6000, 1); + set_bank (0x4000, (unsigned char) (bank & 3)); + break; + case MBC3: + case MBC5: + set_bank (0x4000, bank); + break; + default: + break; + } + } +} + + +static void +set_bank2 (unsigned int bank) +{ + switch (mbc_type) + { + case BUNG: + case UNKNOWN_MBC: + case MBC5: + set_bank (0x2000, (unsigned char) bank); + if (eeprom_type != WINBOND) + set_bank (0x3000, (unsigned char) (bank >> 8)); + break; + case MBC1: + set_bank (0x6000, 0); + set_bank (0x2000, (unsigned char) (bank & 0x1f)); + set_bank (0x4000, (unsigned char) ((bank >> 5) & 3)); // (bank & 0x60) >> 5 + break; + case MBC2: + set_bank (0x2100, (unsigned char) (bank & 0x0f)); + break; + case MBC3: + set_bank (0x2000, (unsigned char) bank); + break; + case CAMERA: + set_bank (0x4000, (unsigned char) bank); + break; + case ROCKET: + set_bank (0x3f00, (unsigned char) bank); + break; + default: + break; + } +} + + +static void +read_rom_16k (unsigned int bank) // ROM or EEPROM +{ + int idx = 0, i, j; + char game_name[16]; + + set_bank2 (bank); + for (j = 0; j < 64; j++) + { // 16k bytes = 64 x 256 bytes + if (bank) + set_ai_data ((unsigned char) 1, (unsigned char) (j | 0x40)); // set adr[15..8] + else + set_ai_data ((unsigned char) 1, (unsigned char) j); // a[15..0] + set_ai_data ((unsigned char) 0, 0); // a[7..0] + set_ai_data ((unsigned char) 2, 0x81); // enable read inc. + set_ai (3); // read/write data + set_data_read + for (i = 0; i < 256; i++) // page = 256 + buffer[idx + i] = read_data (); + idx += 256; + + /* + One can select the game in 2-in-1 cartridges by writing the game number + to 0x3fc0. This has been verified for 2 cartridges with ROM type byte + value 0x99. Maybe there exist other Rocket Games n-in-1 games with a + different ROM type byte value. That's why we don't check for that + specific ROM type byte value. + */ + if (mbc_type == ROCKET && j == 1) + if (memcmp (buffer + 0x104, rocket_logodata, GB_LOGODATA_LEN) == 0) + { + set_adr (0x3fc0); + out_byte ((unsigned char) rocket_game_no++); + if (bank) + { + // Reread the last two pages, because the data came from the + // previously selected game (data is "mirrored"). This does not + // apply to the first game. + int k; + + idx = 0; + for (k = 0; k < 2; k++) + { + set_ai_data ((unsigned char) 1, (unsigned char) (k | 0x40)); + set_ai_data ((unsigned char) 0, 0); + set_ai_data ((unsigned char) 2, 0x81); + set_ai (3); + set_data_read + for (i = 0; i < 256; i++) + buffer[idx + i] = read_data (); + idx += 256; + } + + clear_line (); // remove last gauge + memcpy (game_name, buffer + 0x134, 15); + game_name[15] = 0; + for (i = 0; i < 15; i++) + if (!isprint ((int) game_name[i]) && game_name[i] != 0) + game_name[i] = '.'; + printf ("Found another game: \"%s\"\n\n", game_name); + } + } + } +} + + +static int +verify_rom_16k (unsigned int bank) // ROM or EEPROM +{ + int idx = 0, i, j; + + set_bank2 (bank); + for (j = 0; j < 64; j++) + { // 16k bytes = 64 x 256 bytes + if (bank) + set_ai_data ((unsigned char) 1, (unsigned char) (j | 0x40)); // set adr[15..8] + else + set_ai_data ((unsigned char) 1, (unsigned char) j); + set_ai_data ((unsigned char) 0, 0); // a[7..0] + set_ai_data ((unsigned char) 2, 0x81); // enable read inc. + set_ai (3); // read/write data + set_data_read + for (i = 0; i < 256; i++) + if (read_data () != buffer[idx + i]) + { + printf ("\nWARNING: Verify error at 0x%x\n", + (bank * 16384) + (j * 256) + i); + return -1; + } + idx += 256; + } + + return 0; +} + + +static int +win_write_eeprom_16k (unsigned int bank) +{ + int wr_done, err_cnt, idx = 0, i, j; + +// disable_protection (); + + for (j = 0; j < 64; j++) + { // 16k bytes = 64 x 256 bytes + err_cnt = 16; // retry write counter + wr_done = 1; + while (wr_done) + { + enable_protection (); + // write 256 bytes + set_bank (0x2000, (unsigned char) bank); // for MCB1 16k bank + if (bank) + set_ai_data ((unsigned char) 1, (unsigned char) (j | 0x40)); // set adr[15..8] + else + set_ai_data ((unsigned char) 1, (unsigned char) j); + + set_ai_data ((unsigned char) 0, 0); // a[7..0] +// set_ai_data ((unsigned char) 2, 0x82); // enable flash write + set_ai_data ((unsigned char) 2, 0x83); // enable flash write inc. + set_ai (3); // read/write data +// set_ai_data ((unsigned char) 2, 0x80); + + for (i = 0; i < 256; i++) + write_data (buffer[idx + i]); // write data to EEPROM + set_ai_data ((unsigned char) 2, 0x80); // disable wr/rd inc. + set_ai_data ((unsigned char) 0, 0xff); // point to xxff + if (data_polling ()) + puts ("\nWARNING: Write error"); // was: "Write error check (d6)" + + wr_done = 0; + + // verify 256 bytes + set_ai_data ((unsigned char) 0, 0); // a[7..0] + set_ai_data ((unsigned char) 2, 0x81); // enable read inc. + set_ai (3); // read/write data + set_data_read + for (i = 0; i < 256; i++) + if (read_data () != buffer[idx + i]) + { + err_cnt--; + wr_done = 1; + break; + } + if (err_cnt == 0) + { + fputs ("\nERROR: Programming failed after retry\n", stderr); + return -1; + } + } + idx += 256; + } +/* + enable_protection(); + disable_protection(); + delay_us (20000); +*/ + return verify_rom_16k (bank); +} + + +static void +set_page_write (void) // start page write command +{ + out_adr2_data (0x5555, 0xaa); // 5555:aa adr2,adr1,adr0,data + out_adr2_data (0x2aaa, 0x55); // 2aaa:55 + out_adr2_data (0x5555, 0xa0); // 5555:a0 +} + + +static int +page_write_128 (unsigned int bank, unsigned char hi_lo, int j, int idx) +{ + int retry = 3, verify_ok, i; + + while (retry) + { + set_page_write (); // each page is 128 bytes + set_bank (0x2000, (unsigned char) bank); // for MCB1 16k bank + if (bank) + set_ai_data ((unsigned char) 1, (unsigned char) (j | 0x40)); // set adr[15..8] + else + set_ai_data ((unsigned char) 1, (unsigned char) j); + + set_ai_data ((unsigned char) 0, hi_lo); // a[7..0] + set_ai_data ((unsigned char) 2, 0x83); // enable flash write inc. + set_ai (3); // read/write data + for (i = 0; i < 128; i++) + write_data (buffer[idx + i]); // write data to EEPROM + set_ai_data ((unsigned char) 2, 0x80); // disable wr/rd inc. + delay_us (10); // delay is large enough? - dbjh + if (wait_status ()) + return -1; // wait_status() prints error message + + // verify data + reset_to_read (); // return to read mode + verify_ok = 1; // verify ok + set_bank (0x2000, (unsigned char) bank); // for MCB1 16k bank + if (bank) + set_ai_data ((unsigned char) 1, (unsigned char) (j | 0x40)); // set adr[15..8] + else + set_ai_data ((unsigned char) 1, (unsigned char) j); + + set_ai_data ((unsigned char) 0, hi_lo); // a[7..0] + set_ai_data ((unsigned char) 2, 0x81); // enable inc. + set_ai (3); // read/write data + set_data_read + for (i = 0; i < 128; i++) // page = 128 + if (read_data () != buffer[idx + i]) + { + verify_ok = 0; // verify error + break; + } + if (verify_ok) + break; + else + { + retry--; + if (retry == 0) + { + fputs ("\nERROR: Programming failed after retry\n", stderr); + return -1; + } + } + } + return 0; +} + + +static int +mx_write_eeprom_16k (unsigned int bank) +{ + int idx = 0, j; + + for (j = 0; j < 64; j++) + { // 16k bytes = 64 x 256 bytes + if (page_write_128 (bank, 0, j, idx)) // write first 128 bytes + return -1; + idx += 128; + if (page_write_128 (bank, 0x80, j, idx)) // write second 128 bytes + return -1; + idx += 128; + } + reset_to_read (); // return to read mode + return verify_rom_16k (bank); +} + + +/* +static void +dump_intel_data (void) +{ + int i; + + out_adr_data (0, 0xff); // read array command + for (i = 0; i < 64; i++) + { // read 0x100-0x150 to buffer + set_adr (i); + buffer[i] = read_data (); + } + dumper (stdout, buffer, 64, 0, DUMPER_HEX); +} + + +static int +intel_byte_write_32 (unsigned int block_adr, int idx) +{ + int time_out, i; + + for (i = 0; i < 32; i++) + { + out_adr_data (block_adr + idx + i, 0x40); // Write byte command + out_adr_data (block_adr + idx + i, buffer[idx + i]); // Write data + time_out = 0x8000; + + if (intel_check_status ()) + { + fprintf (stderr, "\nERROR: Intel byte write command time out\n" + " Status = 0x%02x\n", intel_read_status ()); +// dump_intel_data (); + return -1; + } + } + if (intel_read_status () == 0x80) + return 0; + else + return -1; // error +} +*/ + + +static int +intel_buffer_write_32 (unsigned int block_adr, int idx) +{ + int time_out = 0x80000, i; + + out_adr_data (block_adr + idx, 0xe8); // Write buffer command + set_ai_data ((unsigned char) 2, 0x82); // wei enable + set_ai (3); // default write mode +// write_data (0xe8); // Write buffer command + set_data_read // ninit=0, nwrite=1 + while (!(read_data () & 0x80)) + { + time_out--; + if (time_out == 0) + { + fprintf (stderr, "\nERROR: Intel buffer write command time out\n" + " Status = 0x%02x\n", intel_read_status ()); +// dump_intel_data (); + return -1; + } + set_data_write + write_data (0xe8); // out data + set_data_read +// out_byte_eeprom (0xe8); // write buffer command + } + +// out_byte_eeprom (0x1f); // set write byte count command + write_data (0x1f); // out data + + set_ai_data ((unsigned char) 2, 0x83); // enable flash write inc. + set_ai (3); + for (i = 0; i < 32; i++) + write_data (buffer[idx + i]); // write data to EEPROM + write_data (0xd0); // write confirm command + + return 0; +} + + +static int +intel_write_eeprom_16k (unsigned int bank) +{ + unsigned int block_adr = bank << 14; // convert to real address + int idx, j; + + if ((bank & 0x07) == 0) + if (intel_block_erase (block_adr)) + return -1; + +// set_adr_long (block_adr, 0); // set real address + for (j = 0; j < 512; j++) + { // 16k bytes = 512 x 32 bytes + idx = j * 32; +// if (intel_byte_write_32 (block_adr, idx)) return -1; + if (intel_buffer_write_32 (block_adr, idx)) + { + fprintf (stderr, "\nERROR: Write error\n" + " Status = 0x%02x\n", intel_read_status ()); + return -1; + } + } + + if (intel_check_status ()) + { + fprintf (stderr, "\nERROR: Intel buffer write command error\n" + " Status = 0x%02x\n", intel_read_status ()); +// dump_intel_data(); + return -1; + } + if (intel_read_status () != 0x80) + return -1; // error + + out_adr_data (0, 0xff); // read array + set_data_read + return verify_rom_16k (bank); +} + + +static int +write_eeprom_16k (unsigned int bank) +{ + if (eeprom_type == WINBOND) // Winbond 4 Mbits EEPROM + return win_write_eeprom_16k (bank); + if (eeprom_type == MX) // MX 16 Mbits EEPROM + return mx_write_eeprom_16k (bank); + if (eeprom_type == INTEL) // Intel 64 Mbits EEPROM + return intel_write_eeprom_16k (bank); + return -1; +} + + +static void +enable_sram_bank (void) +{ + init_port (); + set_adr (0x0000); // write 0x0000:0x0a default read mode + out_byte (0x0a); // enable SRAM + out_byte (0xc0); // disable SRAM + set_adr (0xa000); + out_byte (0xa0); // ctr index + set_adr (0xa100); +// out_byte(0x00); // ram_off,ram_bank_disable,MBC1 + out_byte (0xc0); // ram_on,ram_bank_enable,MBC1 + + set_adr (0x0000); // write 0x0000:0x0a + out_byte (0x0a); // enable SRAM +} + + +/* +static void +usage (char *progname) +{ + fprintf (stderr, "Usage: %s [-option] \n", progname); + fprintf (stderr, "-l : load ROM file to GB Card.\n"); + fprintf (stderr, "-lsa : load 256k/1Mbits sram from PC to GB Card.\n"); + fprintf (stderr, + "-lsn : load 64kbits sram file from PC to specific sram bank(-n) in GB card.\n"); + fprintf (stderr, "-lsc : load 1Mbits sram file from PC to Pocket Camera.\n"); + fprintf (stderr, "-b : auto-detect size and backup entire GB Card to PC.\n"); + fprintf (stderr, "-ba : backup full 4Mbits/16Mbits GB Card to PC.\n"); + fprintf (stderr, "-bsa : retrieve all sram data (256k/1Mbits) from GB Card to PC.\n"); + fprintf (stderr, + "-bsn : retrieve specific bank(-n) sram data(64kbits) from GB Card to PC.\n"); + fprintf (stderr, "-bsc : retrieve 1Mbits sram from Pocket Camera to PC.\n"); + fprintf (stderr, "-v : verify file in PC with GB Card.\n"); + fprintf (stderr, "-e : erase Flash rom.\n"); + fprintf (stderr, "-c : check ROM file header.\n"); + exit (2); +} +*/ + + +static int +check_port_mode (void) +{ + init_port (); + set_ai_data ((unsigned char) 1, 0x12); + set_ai_data ((unsigned char) 0, 0x34); + set_ai (1); + set_data_read // ninit=0, nwrite=1 + if (read_data () != 0x12) + return 1; + set_ai (0); + set_data_read // ninit=0, nwrite=1 + if (read_data () != 0x34) + return 1; + end_port (); + return 0; +} + + +static int +check_port (void) +{ + if (ucon64.parport_mode == UCON64_EPP && port_8 != 0x3bc) + port_mode = UCON64_EPP; // if port == 0x3bc => no EPP available + else + port_mode = UCON64_SPP; + + if (check_port_mode ()) + { + port_mode = UCON64_SPP; + if (check_port_mode ()) + return 1; + else + end_port (); + } + + // If we get here, a GBX was detected + if (port_mode == UCON64_EPP) + puts ("GBX found. EPP found"); + else + puts ("GBX found. EPP not found or not enabled - SPP used"); + + return 0; +} + + +#if 0 // not used +static void +win_id (void) +{ + out_data (0, 0x55, 0x55, 0xaa); // software product ID entry + out_data (0, 0x2a, 0xaa, 0x55); // adr2,adr1,adr0,data + out_data (0, 0x55, 0x55, 0x80); // adr2,adr1,adr0,data + out_data (0, 0x55, 0x55, 0xaa); // adr2,adr1,adr0,data + out_data (0, 0x2a, 0xaa, 0x55); // adr2,adr1,adr0,data + out_data (0, 0x55, 0x55, 0x60); // adr2,adr1,adr0,data + + delay_us (10); + set_adr (0); // adr2,adr1,adr0 + printf ("Manufacturer code: 0x%02x\n", read_byte ()); + set_adr (1); // adr2,adr1,adr0 + printf ("Device code: 0x%02x\n", read_byte ()); +/* + set_adr (2); // adr2,adr1,adr0 + printf ("First 16 k protection code: 0x%02x\n", read_byte ()); + set_bank (0x2000, 0x1f); + set_adr (0x7ff2); // adr2,adr1,adr0=0x7fff2 + printf("Last 16 k protection code: 0x%02x\n", read_byte ()); +*/ + + out_data (0, 0x55, 0x55, 0xaa); // software product ID exit + out_data (0, 0x2a, 0xaa, 0x55); // adr2,adr1,adr0,data + out_data (0, 0x55, 0x55, 0xf0); // adr2,adr1,adr0,data +} + + +static void +mx_id (void) +{ + out_adr2_data (0x5555, 0xaa); // software product ID entry + out_adr2_data (0x2aaa, 0x55); // adr2,adr1,adr0,data + out_adr2_data (0x5555, 0x90); // adr2,adr1,adr0,data + + set_adr (0); // adr2,adr1,adr0 + printf ("Manufacturer code: 0x%02x\n", read_byte ()); + set_adr (2); // adr2,adr1,adr0 + printf ("Device code: 0x%02x\n", read_byte ()); + set_adr (4); // adr2,adr1,adr0 + printf ("First 16 k protection code: 0x%02x\n", read_byte ()); + reset_to_read (); // reset to read mode +} + + +static void +intel_id (void) +{ + int i; + + out_adr_data (0, 0x98); // read query + for (i = 0; i < 128; i += 2) + { + set_adr (i); // adr2,adr1,adr0 + buffer[i / 2] = read_byte (); + } +// dumper (stdout, buffer, 64, 0, DUMPER_HEX); + + printf ("Manufacture code = 0x%02x\n", buffer[0]); + printf ("Device code = 0x%02x\n", buffer[1]); +} + + +static void +disp_id (void) +{ + if (eeprom_type == WINBOND) + win_id (); + if (eeprom_type == MX) + mx_id (); + if (eeprom_type == INTEL) + intel_id (); +} + + +static void +gen_pat (unsigned int offset) +{ + int i; + + for (i = 0; i < 0x2000; i++) // 8 k words = 16 k bytes + ((unsigned short int *) buffer)[i] = i + offset; +} + + +static int +test_sram_v (int n_banks) +{ + int idx, i, j, bank; + + enable_sram_bank (); + for (bank = 0; bank < n_banks; bank++) + { + idx = 0; + set_sram_bank (bank); + gen_pat (bank); + for (j = 0; j < 0x20; j++) + { // 32 x 256 = 8192 (8 kbytes) + set_ai_data ((unsigned char) 1, (unsigned char) (0xa0 + j)); // SRAM at 0xa000-0xbfff + set_ai_data ((unsigned char) 0, 0); // a[7..0]=0 + set_ai_data ((unsigned char) 2, 0x81); // enable inc + set_ai (3); // point to data r/w port + set_data_read + for (i = 0; i < 256; i++) + if (read_data () != buffer[i + idx]) + { + fputs ("ERROR: SRAM verify error\n", stderr); + return -1; + } + set_ai_data ((unsigned char) 2, 0x80); // disable inc + idx += 256; + } + } + return 0; +} + + +static int +test_sram_wv (int n_banks) +{ + int idx, i, j, bank; + + enable_sram_bank (); + for (bank = 0; bank < n_banks; bank++) + { + idx = 0; + set_sram_bank (bank); + gen_pat (bank); +// dumper (stdout, buffer, 0x10, 0, DUMPER_HEX); + for (j = 0; j < 0x20; j++) + { // 32 x 256 = 8192(8kbytes) + set_ai_data ((unsigned char) 1, (unsigned char) (0xa0 + j)); // SRAM at 0xa000-0xbfff + set_ai_data ((unsigned char) 0, 0); // a[7..0]=0 + set_ai_data ((unsigned char) 2, 0x81); // enable inc + set_ai (3); // point to data r/w port + set_data_write + for (i = 0; i < 256; i++) + write_data (buffer[i + idx]); + set_ai_data ((unsigned char) 2, 0x80); // disable inc + idx += 256; + } + } + return test_sram_v (n_banks); +} + + +int cart_type = 0; // should be set to value of ROM[0x147] + +static void +set_rom_bank (unsigned char bank) +{ + // cart_type < 4 is MCB1, other is MCB2 + if (cart_type < 4) + set_bank (0x2000, bank); // for MCB1 + else + set_bank (0x2100, bank); // for MCB2 +} + + +static void +try_read (void) +{ + int i; + + set_ai_data ((unsigned char) 0, 0); // a[7..0]=0 + set_ai_data ((unsigned char) 1, 0x40); + set_ai_data ((unsigned char) 2, 0x81); // enable inc + set_ai (3); // point to data r/w port + for (i = 0; i < 16; i++) + buffer[i] = read_data (); + dumper (stdout, buffer, 16, 0, DUMPER_HEX); +} + + +static void +try_read0 (void) +{ + int j; + + set_rom_bank (1); + for (j = 0; j < 4; j++) + { + set_sram_bank ((unsigned char) j); + try_read (); + } + set_bank (0x6000, (unsigned char) 1); + puts ("6000:1"); + for (j = 0; j < 4; j++) + { + set_sram_bank ((unsigned char) j); + try_read (); + } + set_bank (0x6000, (unsigned char) 0); + puts ("6000:0"); + for (j = 0; j < 4; j++) + { + set_sram_bank ((unsigned char) j); + try_read (); + } +} + + +static void +test_intel (void) +{ + int i; + + out_adr2_data (0x0000, 0x90); // software product ID entry + for (i = 0; i < 128; i += 2) + { + set_adr (i); // adr2,adr1,adr0 + buffer[i / 2] = read_byte (); + } + dumper (stdout, buffer, 64, 0, DUMPER_HEX); + + out_adr2_data (0x0000, 0x70); // read status register + printf ("Status register = 0x%02x\n", read_byte ()); +} + + +static void gbx_init (unsigned int parport, int read_header); + +static int +verify_card_from_file (const char *filename, unsigned int parport) +{ + unsigned int bank, n_banks, filesize; + FILE *file; + time_t starttime; + + gbx_init (parport, 1); + filesize = fsizeof (filename); + if ((filesize < 0x8000) || (filesize & 0x7fff) || (filesize > 64 * MBIT)) + { + fputs ("ERROR: File size error\n", stderr); + exit (1); + } + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + + n_banks = (filesize / 0x8000) * 2; // how many 16k banks (rounded + starttime = time (NULL); // down to 32k boundary) + for (bank = 0; bank < n_banks; bank++) + { + if (fread (buffer, 1, 0x4000, file) != 0x4000) + { + fprintf (stderr, ucon64_msg[READ_ERROR], filename); + fclose (file); + exit (1); + } +/* + if (bank == 0) + { + // [0x147] = ROM type; [0x148] = ROM size + if (buffer[0x147] > 0 && buffer[0x147] < 4 && buffer[0x148] > 4) + mbc1_exp = 1; // MBC1 8 Mbit/16 Mbit + } +*/ + if (verify_eeprom_16k (bank)) + { + printf ("Verify card error at bank 0x%x\n", bank); + fclose (file); + exit (1); + } + ucon64_gauge (starttime, (bank + 1) * 0x4000, filesize); + } + fclose (file); + puts ("\nVerify card OK"); + + return 0; +} +#endif + + +static void +gbx_init (unsigned int parport, int read_header) +{ + int i; + + eeprom_type = UNKNOWN_EEPROM; + rocket_game_no = 0; + + port_8 = parport; + port_9 = parport + 1; + port_a = parport + 2; + port_b = parport + 3; + port_c = parport + 4; + + parport_print_info (); + + if (check_port () != 0) + { + fputs ("ERROR: GBX not found or not turned on\n", stderr); + exit (1); + } + init_port (); + check_eeprom (); + + if (read_header) + for (i = 0x100; i < 0x150; i++) + { // read 0x100-0x150 to buffer + set_adr (i); + buffer[i - 0x100] = read_data (); + } + /* + buffer is undefined if read_header == 0. This is not a problem as + read_header is only 0 if a flash card should be programmed + (gbx_write_rom()). In that case check_mbc() won't use buffer. + */ + check_mbc (); +} + + +int +gbx_read_rom (const char *filename, unsigned int parport) +{ + unsigned int bank, n_banks, rom_size, n_bytes = 0, totalbytes; + time_t starttime; + FILE *file; + + gbx_init (parport, 1); + rom_size = check_card (); + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + end_port (); + exit (1); + } + + /* + 0 256 kbit = 32 kB = 2 banks + 1 512 kbit = 64 kB = 4 banks + 2 1 Mbit = 128 kB = 8 banks + 3 2 Mbit = 256 kB = 16 banks + 4 4 Mbit = 512 kB = 32 banks + 5 8 Mbit = 1 MB = 64 banks + 6 16 Mbit = 2 MB = 128 banks + */ + n_banks = rom_size / (16 * 1024); + if (eeprom_type == WINBOND) + n_banks = 32; // backup 4 Mbit + if (eeprom_type == MX) + n_banks = 128; // backup 16 Mbit + if (eeprom_type == INTEL) + n_banks = 512; // backup 64 Mbit + + totalbytes = n_banks * 16 * 1024; + printf ("Receive: %d Bytes (%.4f Mb)\n\n", totalbytes, (float) totalbytes / MBIT); + + starttime = time (NULL); + for (bank = 0; bank < n_banks; bank++) + { + read_rom_16k (bank); + if (verify_rom_16k (bank)) + printf ("Verify card error at bank 0x%x\n", bank); + + fwrite (buffer, 1, 0x4000, file); + n_bytes += 16 * 1024; + ucon64_gauge (starttime, n_bytes, totalbytes); + } + + fclose (file); + end_port (); + + return 0; +} + + +int +gbx_write_rom (const char *filename, unsigned int parport) +{ + int bank, n_banks, n_bytes = 0, filesize; + time_t starttime; + FILE *file; + + gbx_init (parport, 0); + if (eeprom_type == UNKNOWN_EEPROM) + { + fputs ("ERROR: Unknown EEPROM type\n", stderr); + end_port (); + exit (1); + } + + filesize = fsizeof (filename); + if ((filesize < 0x8000) || (filesize & 0x7fff) || (filesize > 64 * MBIT)) + { + fputs ("ERROR: File size error\n", stderr); + exit (1); + } + n_banks = (filesize / 0x8000) * 2; // how many 16k banks (rounded + // down to 32k boundary) + if (eeprom_type == MX) + if (mx_erase ()) // erase 16M flash + { + // wait_status() prints error message + end_port (); + exit (1); + } + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + end_port (); + exit (1); + } + + printf ("Send: %d Bytes (%.4f Mb)\n\n", + n_banks * 0x4000, (float) (n_banks * 0x4000) / MBIT); + + starttime = time (NULL); + for (bank = 0; bank < n_banks; bank++) + { + if (fread (buffer, 1, 0x4000, file) != 0x4000) + { + fprintf (stderr, ucon64_msg[READ_ERROR], filename); + fclose (file); + end_port (); + exit (1); + } + if (write_eeprom_16k (bank)) + { + fprintf (stderr, "ERROR: Write card error at bank 0x%x\n", bank); + fclose (file); + end_port (); + exit (1); + } + n_bytes += 0x4000; + ucon64_gauge (starttime, n_bytes, filesize); + } + +#if 0 // write_eeprom_16k() already calls verify_rom_16k() (indirectly)... + clear_line (); // remove last gauge + puts ("Verifying card...\n"); + fseek (file, 0, SEEK_SET); + n_bytes = 0; + starttime = time (NULL); + for (bank = 0; bank < n_banks; bank++) + { + if (fread (buffer, 1, 0x4000, file) != 0x4000) + { + fprintf (stderr, ucon64_msg[READ_ERROR], filename); + fclose (file); + end_port (); + exit (1); + } + if (verify_rom_16k (bank)) + { + fprintf (stderr, "ERROR: Verify card error at bank 0x%x\n", bank); + fclose (file); + end_port (); + exit (1); + } + n_bytes += 0x4000; + ucon64_gauge (starttime, n_bytes, filesize); + } +#endif + + fclose (file); + end_port (); + + return 0; +} + + +static int +sram_size_banks (int pocket_camera, unsigned char sram_size_byte) +{ + int n_banks; + + if (eeprom_type == UNKNOWN_EEPROM) + { + // it seems reasonable that eeprom_type == UNKNOWN_EEPROM when a pocket + // camera (1 Mbit SRAM) is plugged in the GBX + if (pocket_camera) + n_banks = 16; + else // no flash card, must be game cartridge + { + int x = (sram_size_byte & 7) << 1; + if (x > 5) + x = 6; + if (x) + x = 1 << (x - 1); // SRAM size in kB + n_banks = (x + 8192 - 1) / 8192; // round up to 1 bank if it's 2 kB + } + } + else + { + // if eeprom_type != UNKNOWN_EEPROM, it has to be WINBOND, MX or INTEL (no + // need to set n_banks to a default value) + if (eeprom_type == WINBOND) + n_banks = 4; // 4 x 8 kB = 32 kB + else // if (eeprom_type == MX || eeprom_type == INTEL) + n_banks = 16; // 16 x 8 kB = 128 kB + } + + return n_banks; +} + + +int +gbx_read_sram (const char *filename, unsigned int parport, int start_bank) +{ + int bank, n_banks, n_bytes = 0, totalbytes, idx, i, j; + time_t starttime; + FILE *file; + + gbx_init (parport, 1); + + n_banks = sram_size_banks (buffer[0x47] == 0x1f, buffer[0x49]); + if (!n_banks) + { + fputs ("ERROR: No SRAM available\n", stderr); + end_port (); + exit (1); + } + + if (start_bank == -1) + start_bank = 0; + else + { + if (start_bank >= n_banks) + { + fprintf (stderr, "ERROR: Bank must be a value 0 - %d (for this card)\n", + n_banks - 1); + end_port (); + exit (1); + } + n_banks = 1; + } + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + end_port (); + exit (1); + } + + memset (buffer, 0, 8192); + totalbytes = n_banks * 8192; + printf ("Receive: %d Bytes (%.4f Mb)\n\n", totalbytes, (float) totalbytes / MBIT); + + enable_sram_bank (); + starttime = time (NULL); + for (bank = start_bank; bank < start_bank + n_banks; bank++) + { + idx = 0; + set_sram_bank ((unsigned char) bank); + for (j = 0; j < 32; j++) + { // 32 x 256 = 8192 (8 kbytes) + set_ai_data ((unsigned char) 1, (unsigned char) (0xa0 + j)); // SRAM at 0xa000-0xbfff + set_ai_data ((unsigned char) 0, 0); // a[7..0]=0 + set_ai_data ((unsigned char) 2, 0x81); // enable inc + set_ai (3); // point to data r/w port + set_data_read + for (i = 0; i < 256; i++) + buffer[idx + i] = read_data (); + set_ai_data ((unsigned char) 2, 0x80); // disable inc + idx += 256; + } + fwrite (buffer, 1, 8192, file); + n_bytes += 8192; + ucon64_gauge (starttime, n_bytes, totalbytes); + } + fclose (file); + end_port (); + + return 0; +} + + +int +gbx_write_sram (const char *filename, unsigned int parport, int start_bank) +{ + int bank, n_banks, n_bytes = 0, totalbytes, idx, i, j; + time_t starttime; + FILE *file; + + gbx_init (parport, 1); + + n_banks = sram_size_banks (buffer[0x47] == 0x1f, buffer[0x49]); + if (!n_banks) + { + fputs ("ERROR: No SRAM to write to\n", stderr); + end_port (); + exit (1); + } + + if (start_bank == -1) + { + start_bank = 0; + i = fsizeof (filename); + n_banks = MIN (n_banks, (i + 8192 - 1) / 8192); // "+ 8192 - 1" to round up + } + else + { + if (start_bank >= n_banks) + { + fprintf (stderr, "ERROR: Bank must be a value 0 - %d (for this card)\n", + n_banks - 1); + end_port (); + exit (1); + } + n_banks = 1; + } + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + end_port (); + exit (1); + } + + memset (buffer, 0, 8192); + totalbytes = n_banks * 8192; // yes, we _send_ totalbytes bytes + printf ("Send: %d Bytes (%.4f Mb)\n\n", totalbytes, (float) totalbytes / MBIT); + + enable_sram_bank (); + starttime = time (NULL); + for (bank = start_bank; bank < start_bank + n_banks; bank++) + { + idx = 0; + if (!fread (buffer, 1, 8192, file)) + { // handle/allow files that are not an exact multiple of 8 kB + fprintf (stderr, ucon64_msg[READ_ERROR], filename); + fclose (file); + end_port (); + exit (1); + } + set_sram_bank ((unsigned char) bank); + for (j = 0; j < 32; j++) + { // 32 x 256 = 8192 (8 kbytes) + set_ai_data ((unsigned char) 1, (unsigned char) (0xa0 + j)); // SRAM at 0xa000-0xbfff + set_ai_data ((unsigned char) 0, 0); // a[7..0]=0 + set_ai_data ((unsigned char) 2, 0x81); // enable inc + set_ai (3); // point to data r/w port + set_data_write + for (i = 0; i < 256; i++) + write_data (buffer[idx + i]); + set_ai_data ((unsigned char) 2, 0x80); // disable inc + idx += 256; + } + n_bytes += 8192; + ucon64_gauge (starttime, n_bytes, totalbytes); + } + fclose (file); + end_port (); + + return 0; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/gbx.h b/ucon64/2.0/src/backup/gbx.h new file mode 100644 index 0000000..afed590 --- /dev/null +++ b/ucon64/2.0/src/backup/gbx.h @@ -0,0 +1,34 @@ +/* +gbx.h - Game Boy Xchanger support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2001 - 2002 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef GBX_H +#define GBX_H + +extern const st_getopt2_t gbx_usage[]; + +#ifdef USE_PARALLEL +extern int gbx_read_rom (const char *filename, unsigned int parport); +extern int gbx_write_rom (const char *filename, unsigned int parport); +extern int gbx_read_sram (const char *filename, unsigned int parport, int bank); +extern int gbx_write_sram (const char *filename, unsigned int parport, int bank); +#endif + +#endif // GBX_H diff --git a/ucon64/2.0/src/backup/gd.c b/ucon64/2.0/src/backup/gd.c new file mode 100644 index 0000000..c7bb853 --- /dev/null +++ b/ucon64/2.0/src/backup/gd.c @@ -0,0 +1,1144 @@ +/* +gd.c - Game Doctor support for uCON64 + +Copyright (c) 2002 - 2003 John Weidman +Copyright (c) 2002 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include "misc/string.h" +#include "misc/misc.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "gd.h" +#include "console/snes.h" // for snes_make_gd_names() & +#include "misc/parallel.h" // snes_get_snes_hirom() + + +const st_getopt2_t gd_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Game Doctor SF3(SF6/SF7)/Professor SF(SF II)"/*"19XX Bung Enterprises Ltd http://www.bung.com.hk"*/, + NULL + }, +#ifdef USE_PARALLEL + { + "xgd3", 0, 0, UCON64_XGD3, // supports split files + NULL, "send ROM to Game Doctor SF3/SF6/SF7; " OPTION_LONG_S "port=PORT\n" + "this option uses the Game Doctor SF3 protocol", + &ucon64_wf[WF_OBJ_SNES_DEFAULT_STOP_NO_ROM] + }, + { + "xgd6", 0, 0, UCON64_XGD6, +#if 1 // dumping is not yet supported + NULL, "send ROM to Game Doctor SF6/SF7; " OPTION_LONG_S "port=PORT\n" // supports split files +#else + NULL, "send/receive ROM to/from Game Doctor SF6/SF7; " OPTION_LONG_S "port=PORT\n" + "receives automatically when ROM does not exist\n" +#endif + "this option uses the Game Doctor SF6 protocol", + &ucon64_wf[WF_OBJ_SNES_DEFAULT_STOP_NO_ROM] + }, + { + "xgd3s", 0, 0, UCON64_XGD3S, + NULL, "send SRAM to Game Doctor SF3/SF6/SF7; " OPTION_LONG_S "port=PORT", + &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM] + }, + // --xgd3r should remain hidden until receiving works + { + "xgd3r", 0, 0, UCON64_XGD3R, + NULL, NULL, + &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM] + }, + { + "xgd6s", 0, 0, UCON64_XGD6S, + NULL, "send/receive SRAM to/from Game Doctor SF6/SF7; " OPTION_LONG_S "port=PORT\n" + "receives automatically when SRAM does not exist", + &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM] + }, + { + "xgd6r", 0, 0, UCON64_XGD6R, + NULL, "send/receive saver (RTS) data to/from Game Doctor SF6/SF7;\n" OPTION_LONG_S "port=PORT\n" + "receives automatically when saver file does not exist", + &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM] + }, +#endif // USE_PARALLEL + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +#ifdef USE_PARALLEL + +#define BUFFERSIZE 8192 +#define GD_OK 0 +#define GD_ERROR 1 +#define GD3_PROLOG_STRING "DSF3" +#define GD6_READ_PROLOG_STRING "GD6R" // GD reading, PC writing +#define GD6_WRITE_PROLOG_STRING "GD6W" // GD writing, PC reading +#define GD6_TIMEOUT_ATTEMPTS 0x4000 +#define GD6_RX_SYNC_TIMEOUT_ATTEMPTS 0x2000 +#define GD6_SYNC_RETRIES 16 + +#ifdef _MSC_VER +// Visual C++ doesn't allow inline in C source code +#define inline __inline +#endif + + +static void init_io (unsigned int port); +static void deinit_io (void); +static void io_error (void); +static void gd_checkabort (int status); +static void remove_destfile (void); +static int gd3_send_prolog_byte (unsigned char data); +static int gd3_send_prolog_bytes (unsigned char *data, int len); +static void gd3_send_byte (unsigned char data); +static int gd3_send_bytes (unsigned char *data, int len); +static int gd6_sync_hardware (void); +static int gd6_sync_receive_start (void); +static inline int gd6_send_byte_helper (unsigned char data, unsigned int timeout); +static int gd6_send_prolog_byte (unsigned char data); +static int gd6_send_prolog_bytes (unsigned char *data, int len); +static int gd6_send_bytes (unsigned char *data, int len); +static int gd6_receive_bytes (unsigned char *buffer, int len); +static int gd_send_unit_prolog (int header, unsigned size); +static int gd_write_rom (const char *filename, unsigned int parport, + st_rominfo_t *rominfo, const char *prolog_str); +static int gd_write_sram (const char *filename, unsigned int parport, + const char *prolog_str); +static int gd_write_saver (const char *filename, unsigned int parport, + const char *prolog_str); + + +typedef struct st_gd3_memory_unit +{ + char name[12]; // Exact size is 11 chars but I'll +// unsigned char *data; // add one extra for string terminator + unsigned int size; // Usually either 0x100000 or 0x80000 +} st_gd3_memory_unit_t; + +static int (*gd_send_prolog_byte) (unsigned char data); +static int (*gd_send_prolog_bytes) (unsigned char *data, int len); +static int (*gd_send_bytes) (unsigned char *data, int len); +static st_gd3_memory_unit_t gd3_dram_unit[GD3_MAX_UNITS]; +static int gd_port, gd_bytessend, gd_fsize, gd_name_i = 0; +static time_t gd_starttime; +static char **gd_names; +static unsigned char gd6_send_toggle; +static const char *gd_destfname = NULL; +static FILE *gd_destfile; + + +void +init_io (unsigned int port) +/* + - sets global `gd_port'. Then the send/receive functions don't need to pass + `port' to all the I/O functions. + - calls init_conio(). Necessary for kbhit() and DOS-like behaviour of getch(). +*/ +{ + gd_port = port; +#if 0 // we want to support non-standard parallel port addresses + if (gd_port != 0x3bc && gd_port != 0x378 && gd_port != 0x278) + { + fputs ("ERROR: PORT must be 0x3bc, 0x378 or 0x278\n", stderr); + exit (1); + } +#endif + +#if (defined __unix__ || defined __BEOS__) && !defined __MSDOS__ + init_conio (); +#endif + + parport_print_info (); +} + + +void +deinit_io (void) +{ +// Put possible transfer cleanup stuff here +#if (defined __unix__ || defined __BEOS__) && !defined __MSDOS__ + deinit_conio (); +#endif +} + + +void +io_error (void) +// This function could be changed to take a string argument that describes the +// error. Or take an integer code that we can interpret here. +{ + fflush (stdout); + fputs ("ERROR: Communication with Game Doctor failed\n", stderr); + fflush (stderr); + // calling fflush() seems to be necessary under Msys in order to make the + // error message be displayed before the "Removing: " message + exit (1); +} + + +void +gd_checkabort (int status) +{ + if (((!ucon64.frontend) ? kbhit () : 0) && getch () == 'q') + { + puts ("\nProgram aborted"); + exit (status); + } +} + + +static void +remove_destfile (void) +{ + if (gd_destfname) + { + printf ("Removing: %s\n", gd_destfname); + fclose (gd_destfile); + remove (gd_destfname); + gd_destfname = NULL; + } +} + + +int +gd3_send_prolog_byte (unsigned char data) +/* + Prolog specific data output routine + We could probably get away with using the general routine but the + transfer program I (JW) traced to analyze the protocol did this for + the bytes used to set up the transfer so here it is. +*/ +{ + // Wait until SF3 is not busy + do + { + if ((inportb ((unsigned short) (gd_port + PARPORT_STATUS)) & 0x08) == 0) + return GD_ERROR; + } + while ((inportb ((unsigned short) (gd_port + PARPORT_STATUS)) & 0x80) == 0); + + outportb ((unsigned short) gd_port, data); // set data + outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 5); // Clock data out to SF3 + outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 4); + + return GD_OK; +} + + +int +gd3_send_prolog_bytes (unsigned char *data, int len) +{ + int i; + + for (i = 0; i < len; i++) + if (gd3_send_prolog_byte (data[i]) == GD_ERROR) + return GD_ERROR; + return GD_OK; +} + + +int +gd_send_unit_prolog (int header, unsigned size) +{ + if (gd_send_prolog_byte (0x00) == GD_ERROR) + return GD_ERROR; + if (gd_send_prolog_byte ((unsigned char) ((header != 0) ? 0x02 : 0x00)) == GD_ERROR) + return GD_ERROR; + if (gd_send_prolog_byte ((unsigned char) (size >> 16)) == GD_ERROR) // 0x10 = 8 Mbit + return GD_ERROR; + if (gd_send_prolog_byte (0x00) == GD_ERROR) + return GD_ERROR; + return GD_OK; +} + + +void +gd3_send_byte (unsigned char data) +/* + General data output routine + Use this routine for sending ROM data bytes to the Game Doctor SF3 (SF6/SF7 + too). +*/ +{ + // Wait until SF3 is not busy + while ((inportb ((unsigned short) (gd_port + PARPORT_STATUS)) & 0x80) == 0) + ; + + outportb ((unsigned short) gd_port, data); // set data + outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 5); // Clock data out to SF3 + outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 4); +} + + +int +gd3_send_bytes (unsigned char *data, int len) +{ + int i; + + for (i = 0; i < len; i++) + { + gd3_send_byte (data[i]); + gd_bytessend++; + if ((gd_bytessend - GD_HEADER_LEN) % 8192 == 0) + { + ucon64_gauge (gd_starttime, gd_bytessend, gd_fsize); + gd_checkabort (2); // 2 to return something other than 1 + } + } + return GD_OK; +} + + +int +gd6_sync_hardware (void) +// Sets the SF7 up for an SF6/SF7 protocol transfer +{ + int timeout, retries; + volatile int delay; + + for (retries = GD6_SYNC_RETRIES; retries > 0; retries--) + { + timeout = GD6_TIMEOUT_ATTEMPTS; + + outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 4); + outportb ((unsigned short) gd_port, 0); + outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 4); + + for (delay = 0x1000; delay > 0; delay--) // A delay may not be necessary here + ; + + outportb ((unsigned short) gd_port, 0xaa); + outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 0); + + while ((inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x08) == 0) + if (--timeout == 0) + break; + if (timeout == 0) + continue; + + outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 4); + + while ((inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x08) != 0) + if (--timeout == 0) + break; + if (timeout == 0) + continue; + + outportb ((unsigned short) gd_port, 0x55); + outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 0); + + while ((inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x08) == 0) + if (--timeout == 0) + break; + if (timeout == 0) + continue; + + outportb ((unsigned short) (gd_port + PARPORT_CONTROL), 4); + + while ((inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x08) != 0) + if (--timeout == 0) + break; + if (timeout == 0) + continue; + + return GD_OK; + } + return GD_ERROR; +} + + +int +gd6_sync_receive_start (void) +// Sync with the start of the received data +{ + int timeout = GD6_RX_SYNC_TIMEOUT_ATTEMPTS; + + while (1) + { + if (((inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x03) == 0x03) || + ((inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x03) == 0)) + break; + + if (--timeout == 0) + return GD_ERROR; + } + + outportb ((unsigned short) gd_port, 0); + + timeout = GD6_RX_SYNC_TIMEOUT_ATTEMPTS; + while ((inportb ((unsigned short) (gd_port + PARPORT_STATUS)) & 0x80) != 0) + if (--timeout == 0) + return GD_ERROR; + + return GD_OK; +} + + +inline int +gd6_send_byte_helper (unsigned char data, unsigned int timeout) +{ + while ((inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x02) != gd6_send_toggle) + if (--timeout == 0) + return GD_ERROR; + + gd6_send_toggle = ~gd6_send_toggle & 0x02; + outportb ((unsigned short) gd_port, data); + outportb ((unsigned short) (gd_port + PARPORT_CONTROL), (unsigned char) (4 | (gd6_send_toggle >> 1))); + + return GD_OK; +} + + +int +gd6_send_prolog_byte (unsigned char data) +{ + unsigned int timeout = 0x100000; + + gd6_send_toggle = (inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x01) << 1; + return gd6_send_byte_helper (data, timeout); +} + + +int +gd6_send_prolog_bytes (unsigned char *data, int len) +{ + int i; + unsigned int timeout = 0x1e00000; + + gd6_send_toggle = (inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x01) << 1; + for (i = 0; i < len; i++) + if (gd6_send_byte_helper (data[i], timeout) != GD_OK) + return GD_ERROR; + return GD_OK; +} + + +int +gd6_send_bytes (unsigned char *data, int len) +{ + int i; + unsigned int timeout = 0x1e0000; + + gd6_send_toggle = (inportb ((unsigned short) (gd_port + PARPORT_CONTROL)) & 0x01) << 1; + for (i = 0; i < len; i++) + { + if (gd6_send_byte_helper (data[i], timeout) != GD_OK) + return GD_ERROR; + + gd_bytessend++; + if ((gd_bytessend - GD_HEADER_LEN) % 8192 == 0) + { + ucon64_gauge (gd_starttime, gd_bytessend, gd_fsize); + gd_checkabort (2); // 2 to return something other than 1 + } + } + return GD_OK; +} + + +int +gd6_receive_bytes (unsigned char *buffer, int len) +{ + int i; + unsigned char nibble1, nibble2; + unsigned int timeout = 0x1e0000; + + outportb ((unsigned short) gd_port, 0x80); // Signal the SF6/SF7 to send the next nibble + for (i = 0; i < len; i++) + { + while ((inportb ((unsigned short) (gd_port + PARPORT_STATUS)) & 0x80) == 0) + if (--timeout == 0) + return GD_ERROR; + + nibble1 = (inportb ((unsigned short) (gd_port + PARPORT_STATUS)) >> 3) & 0x0f; + outportb ((unsigned short) gd_port, 0x00); // Signal the SF6/SF7 to send the next nibble + + while ((inportb ((unsigned short) (gd_port + PARPORT_STATUS)) & 0x80) != 0) + if (--timeout == 0) + return GD_ERROR; + + nibble2 = (inportb ((unsigned short) (gd_port + PARPORT_STATUS)) << 1) & 0xf0; + buffer[i] = nibble2 | nibble1; + outportb ((unsigned short) gd_port, 0x80); + } + return GD_OK; +} + + +int +gd_add_filename (const char *filename) +{ + char buf[FILENAME_MAX], *p; + + if (gd_name_i < GD3_MAX_UNITS) + { + strcpy (buf, filename); + p = strrchr (buf, '.'); + if (p) + *p = 0; + strncpy (gd_names[gd_name_i], basename2 (buf), 11); + gd_names[gd_name_i][11] = 0; + gd_name_i++; + } + return 0; +} + + +int +gd3_read_rom (const char *filename, unsigned int parport) +{ + (void) filename; // warning remover + (void) parport; // warning remover + return fputs ("ERROR: The function for dumping a cartridge is not yet implemented for the SF3\n", stderr); +} + + +int +gd6_read_rom (const char *filename, unsigned int parport) +{ +#if 0 + FILE *file; + unsigned char *buffer; + + init_io (parport); + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + free (buffer); + fclose (file); + deinit_io (); + + return 0; +#else + (void) filename; // warning remover + (void) parport; // warning remover + return fputs ("ERROR: The function for dumping a cartridge is not yet implemented for the SF6\n", stderr); +#endif +} + + +int +gd3_write_rom (const char *filename, unsigned int parport, st_rominfo_t *rominfo) +{ + gd_send_prolog_byte = gd3_send_prolog_byte; // for gd_send_unit_prolog() + gd_send_prolog_bytes = gd3_send_prolog_bytes; + gd_send_bytes = gd3_send_bytes; + + return gd_write_rom (filename, parport, rominfo, GD3_PROLOG_STRING); +} + + +int +gd6_write_rom (const char *filename, unsigned int parport, st_rominfo_t *rominfo) +{ + gd_send_prolog_byte = gd6_send_prolog_byte; // for gd_send_unit_prolog() + gd_send_prolog_bytes = gd6_send_prolog_bytes; + gd_send_bytes = gd6_send_bytes; + + return gd_write_rom (filename, parport, rominfo, GD6_READ_PROLOG_STRING); +} + + +/* + Note: On most Game Doctor's the way you enter link mode to be able to upload + the ROM to the unit is to hold down the R key on the controller while + resetting the SNES. You will see the Game Doctor menu has a message that + says "LINKING..." +*/ +int +gd_write_rom (const char *filename, unsigned int parport, st_rominfo_t *rominfo, + const char *prolog_str) +{ + FILE *file = NULL; + unsigned char *buffer; + char *names[GD3_MAX_UNITS], names_mem[GD3_MAX_UNITS][12], + *filenames[GD3_MAX_UNITS], dir[FILENAME_MAX]; + int num_units, i, send_header, x, split = 1, hirom = snes_get_snes_hirom(); + + init_io (parport); + + // We don't want to malloc() ridiculously small chunks (of 12 bytes) + for (i = 0; i < GD3_MAX_UNITS; i++) + names[i] = names_mem[i]; + + gd_names = (char **) names; + ucon64_testsplit_callback = gd_add_filename; + num_units = ucon64.split = ucon64_testsplit (filename); // this will call gd_add_filename() + ucon64_testsplit_callback = NULL; + if (!ucon64.split) + { + split = 0; + num_units = snes_make_gd_names (filename, rominfo, (char **) names); + } + + dirname2 (filename, dir); + gd_fsize = 0; + for (i = 0; i < num_units; i++) + { + // No suffix is necessary but the name entry must be upper case and MUST + // be 11 characters long, padded at the end with spaces if necessary. + memset (gd3_dram_unit[i].name, ' ', 11); // "pad" with spaces + gd3_dram_unit[i].name[11] = 0; // terminate string so we can print it (debug) + // Use memcpy() instead of strcpy() so that the string terminator in + // names[i] won't be copied. + memcpy (gd3_dram_unit[i].name, strupr (names[i]), strlen (names[i])); + + x = strlen (dir) + strlen (names[i]) + 6; // file sep., suffix, ASCII-z => 6 + if ((filenames[i] = (char *) malloc (x)) == NULL) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], x); + exit (1); + } + sprintf (filenames[i], "%s" FILE_SEPARATOR_S "%s.078", dir, names[i]); // should match with what code of -s does + + if (split) + { + x = fsizeof (filenames[i]); + gd_fsize += x; + gd3_dram_unit[i].size = x; + if (i == 0) // Correct for header of first file + gd3_dram_unit[i].size -= GD_HEADER_LEN; + } + else + { + if (!gd_fsize) // Don't call fsizeof() more + gd_fsize = fsizeof (filename); // often than necessary + if (hirom) + gd3_dram_unit[i].size = (gd_fsize - GD_HEADER_LEN) / num_units; + else + { + if ((i + 1) * 8 * MBIT <= gd_fsize - GD_HEADER_LEN) + gd3_dram_unit[i].size = 8 * MBIT; + else + gd3_dram_unit[i].size = gd_fsize - GD_HEADER_LEN - i * 8 * MBIT; + } + } + } + + if ((buffer = (unsigned char *) malloc (8 * MBIT)) == NULL) + { // a DRAM unit can hold 8 Mbit at maximum + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], 8 * MBIT); + exit (1); + } + + printf ("Send: %d Bytes (%.4f Mb)\n", gd_fsize, (float) gd_fsize / MBIT); + + // Put this just before the real transfer begins or else the ETA won't be + // correct. + gd_starttime = time (NULL); + + // Send the ROM to the hardware + if (memcmp (prolog_str, GD6_READ_PROLOG_STRING, 4) == 0) + if (gd6_sync_hardware () == GD_ERROR) + io_error (); + memcpy (buffer, prolog_str, 4); + buffer[4] = num_units; + if (gd_send_prolog_bytes (buffer, 5) == GD_ERROR) + io_error (); + + puts ("Press q to abort\n"); + for (i = 0; i < num_units; i++) + { +#ifdef DEBUG + printf ("\nfilename (%d): \"%s\", ", split, (split ? (char *) filenames[i] : filename)); + printf ("name: \"%s\", size: %d\n", gd3_dram_unit[i].name, gd3_dram_unit[i].size); +#endif + if (split) + { + if ((file = fopen (filenames[i], "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filenames[i]); + exit (1); + } + } + else + if (file == NULL) // don't open the file more than once + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + + send_header = i == 0 ? 1 : 0; + if (gd_send_unit_prolog (send_header, gd3_dram_unit[i].size) == GD_ERROR) + io_error (); + if (gd_send_prolog_bytes ((unsigned char *) gd3_dram_unit[i].name, 11) == GD_ERROR) + io_error (); + if (send_header) + { + // Send the Game Doctor 512 byte header + fread (buffer, 1, GD_HEADER_LEN, file); + if (gd_send_prolog_bytes (buffer, GD_HEADER_LEN) == GD_ERROR) + io_error (); + gd_bytessend += GD_HEADER_LEN; + } + if (split == 0) // Not pre-split -- have to split it ourselves + { + if (hirom) + fseek (file, i * gd3_dram_unit[0].size + GD_HEADER_LEN, SEEK_SET); + else + fseek (file, i * 8 * MBIT + GD_HEADER_LEN, SEEK_SET); + } + fread (buffer, 1, gd3_dram_unit[i].size, file); + if (gd_send_bytes (buffer, gd3_dram_unit[i].size) == GD_ERROR) + io_error (); + + if (split || i == num_units - 1) + fclose (file); + } + + for (i = 0; i < num_units; i++) + free (filenames[i]); + free (buffer); + deinit_io (); + + return 0; +} + + +int +gd3_read_sram (const char *filename, unsigned int parport) +{ + (void) filename; // warning remover + (void) parport; // warning remover + return fputs ("ERROR: The function for dumping SRAM is not yet implemented for the SF3\n", stderr); +} + + +int +gd6_read_sram (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer, gdfilename[12]; + int len, bytesreceived = 0, transfer_size; + time_t starttime; + + init_io (parport); + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + // Be nice to the user and automatically remove the file on an error (or abortion) + gd_destfname = filename; + gd_destfile = file; + register_func (remove_destfile); + + if (gd6_sync_hardware () == GD_ERROR) + io_error (); + if (gd6_send_prolog_bytes ((unsigned char *) GD6_WRITE_PROLOG_STRING, 4) == GD_ERROR) + io_error (); + + /* + The BRAM (SRAM) filename doesn't have to exactly match any game loaded in + the SF7. It needs to match any valid Game Doctor file name AND have an + extension of .B## (where # is a digit from 0-9) + */ + strcpy ((char *) gdfilename, "SF16497 B00"); // TODO: We might need to make a GD file name from the real one + if (gd6_send_prolog_bytes (gdfilename, 11) == GD_ERROR) + io_error (); + + if (gd6_sync_receive_start () == GD_ERROR) + io_error (); + + if (gd6_receive_bytes (buffer, 16) == GD_ERROR) + io_error (); + + transfer_size = buffer[1] | (buffer[2] << 8) | (buffer[3] << 16) | (buffer[4] << 24); + if (transfer_size != 0x8000) + { + fprintf (stderr, "ERROR: SRAM transfer size from Game Doctor != 0x8000 bytes\n"); + exit (1); + } + + printf ("Receive: %d Bytes\n", transfer_size); + puts ("Press q to abort\n"); + + starttime = time (NULL); + while (bytesreceived < transfer_size) + { + if (transfer_size - bytesreceived >= BUFFERSIZE) + len = BUFFERSIZE; + else + len = transfer_size - bytesreceived; + + if (gd6_receive_bytes (buffer, len) == GD_ERROR) + io_error (); + fwrite (buffer, 1, len, file); + + bytesreceived += len; + ucon64_gauge (starttime, bytesreceived, 32 * 1024); + gd_checkabort (2); + } + + unregister_func (remove_destfile); + free (buffer); + fclose (file); + deinit_io (); + + return 0; +} + + +int +gd3_write_sram (const char *filename, unsigned int parport) +{ + gd_send_prolog_bytes = gd3_send_prolog_bytes; + gd_send_bytes = gd3_send_bytes; + + return gd_write_sram (filename, parport, GD3_PROLOG_STRING); +} + + +int +gd6_write_sram (const char *filename, unsigned int parport) +{ + gd_send_prolog_bytes = gd6_send_prolog_bytes; + gd_send_bytes = gd6_send_bytes; + + return gd_write_sram (filename, parport, GD6_READ_PROLOG_STRING); +} + + +int +gd_write_sram (const char *filename, unsigned int parport, const char *prolog_str) +{ + FILE *file; + unsigned char *buffer, gdfilename[12]; + int bytesread, bytessend = 0, size, header_size; + time_t starttime; + + init_io (parport); + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + size = fsizeof (filename); // GD SRAM is 4*8 KB, emu SRAM often not + + if (size == 0x8000) + header_size = 0; + else if (size == 0x8200) + { + header_size = 0x200; + size = 0x8000; + } + else + { + fputs ("ERROR: GD SRAM file size must be 32768 or 33280 bytes\n", stderr); + exit (1); + } + + printf ("Send: %d Bytes\n", size); + fseek (file, header_size, SEEK_SET); // skip the header + + if (memcmp (prolog_str, GD6_READ_PROLOG_STRING, 4) == 0) + if (gd6_sync_hardware () == GD_ERROR) + io_error (); + memcpy (buffer, prolog_str, 4); + buffer[4] = 1; + if (gd_send_prolog_bytes (buffer, 5) == GD_ERROR) + io_error (); + + buffer[0] = 0x00; + buffer[1] = 0x80; + buffer[2] = 0x00; + buffer[3] = 0x00; + if (gd_send_prolog_bytes (buffer, 4) == GD_ERROR) + io_error (); + + /* + The BRAM (SRAM) filename doesn't have to exactly match any game loaded in + the SF7. It needs to match any valid Game Doctor file name AND have an + extension of .B## (where # is a digit from 0-9) + */ + strcpy ((char *) gdfilename, "SF8123 B00"); // TODO: We might need to make a GD file name from the real one + if (gd_send_prolog_bytes (gdfilename, 11) == GD_ERROR) + io_error (); + + puts ("Press q to abort\n"); // print here, NOT before first GD I/O, + // because if we get here q works ;-) + starttime = time (NULL); + while ((bytesread = fread (buffer, 1, BUFFERSIZE, file))) + { + if (gd_send_bytes (buffer, bytesread) == GD_ERROR) + io_error (); + + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, size); + gd_checkabort (2); + } + + free (buffer); + fclose (file); + deinit_io (); + + return 0; +} + + +int +gd3_read_saver (const char *filename, unsigned int parport) +{ + (void) filename; // warning remover + (void) parport; // warning remover + return fputs ("ERROR: The function for dumping saver data is not yet implemented for the SF3\n", stderr); +} + + +int +gd6_read_saver (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer, gdfilename[12]; + int len, bytesreceived = 0, transfer_size; + time_t starttime; + + init_io (parport); + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + // Be nice to the user and automatically remove the file on an error (or abortion) + gd_destfname = filename; + gd_destfile = file; + register_func (remove_destfile); + + if (gd6_sync_hardware () == GD_ERROR) + io_error (); + if (gd6_send_prolog_bytes ((unsigned char *) GD6_WRITE_PROLOG_STRING, 4) == GD_ERROR) + io_error (); + + /* + TODO: Graceful handling of an abort because of a name error? + Currently we fail with a generic error. + + TODO: We could make a GD file name from the real one but a valid dummy name + seems to work OK here. The user must have the proper game selected in + the SF7 menu even if the real name is used. + */ + strcpy ((char *) gdfilename, "SF16497 S00"); + if (gd6_send_prolog_bytes (gdfilename, 11) == GD_ERROR) + io_error (); + + if (gd6_sync_receive_start () == GD_ERROR) + io_error (); + + if (gd6_receive_bytes (buffer, 16) == GD_ERROR) + io_error (); + + transfer_size = buffer[1] | (buffer[2] << 8) | (buffer[3] << 16) | (buffer[4] << 24); + if (transfer_size != 0x38000) + { + fputs ("ERROR: Saver transfer size from Game Doctor != 0x38000 bytes\n", stderr); + exit (1); + } + + printf ("Receive: %d Bytes\n", transfer_size); + puts ("Press q to abort\n"); + + starttime = time (NULL); + while (bytesreceived < transfer_size) + { + if (transfer_size - bytesreceived >= BUFFERSIZE) + len = BUFFERSIZE; + else + len = transfer_size - bytesreceived; + + if (gd6_receive_bytes (buffer, len) == GD_ERROR) + io_error (); + fwrite (buffer, 1, len, file); + + bytesreceived += len; + ucon64_gauge (starttime, bytesreceived, 32 * 1024); + gd_checkabort (2); + } + + unregister_func (remove_destfile); + free (buffer); + fclose (file); + deinit_io (); + + return 0; +} + + +int +gd3_write_saver (const char *filename, unsigned int parport) +{ + gd_send_prolog_bytes = gd3_send_prolog_bytes; + gd_send_bytes = gd3_send_bytes; + + return gd_write_saver (filename, parport, GD3_PROLOG_STRING); +} + + +int +gd6_write_saver (const char *filename, unsigned int parport) +{ + gd_send_prolog_bytes = gd6_send_prolog_bytes; + gd_send_bytes = gd6_send_bytes; + + return gd_write_saver (filename, parport, GD6_READ_PROLOG_STRING); +} + + +int +gd_write_saver (const char *filename, unsigned int parport, const char *prolog_str) +{ + FILE *file; + unsigned char *buffer, gdfilename[12]; + const char *p; + int bytesread, bytessend = 0, size, fn_length; + time_t starttime; + + init_io (parport); + + /* + Check that filename is a valid Game Doctor saver filename. + It should start with SF, followed by the game ID, followed by the extension. + The extension is of the form .S## (where # is a digit from 0-9). + E.g., SF16123.S00 + The saver base filename must match the base name of the game (loaded in the + Game Doctor) that you are loading the saver data for. + */ + + // Strip the path out of filename for use in the GD + p = basename2 (filename); + fn_length = strlen (p); + + if (fn_length < 6 || fn_length > 11 // 7 ("base") + 1 (period) + 3 (extension) + || toupper (p[0]) != 'S' || toupper (p[1]) != 'F' + || p[fn_length - 4] != '.' || toupper (p[fn_length - 3]) != 'S') + { + fprintf (stderr, "ERROR: Filename (%s) is not a saver filename (SF*.S##)\n", p); + exit (1); + } + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + size = fsizeof (filename); + if (size != 0x38000) // GD saver size is always 0x38000 bytes -- no header + { + fputs ("ERROR: GD saver file size must be 229376 bytes\n", stderr); + exit (1); + } + + // Make a GD file name from the real one + memset (gdfilename, ' ', 11); // "pad" with spaces + gdfilename[11] = 0; // terminate string + memcpy (gdfilename, p, fn_length - 4); // copy name except extension + memcpy (&gdfilename[8], "S00", 3); // copy extension S00 + strupr ((char *) gdfilename); + + printf ("Send: %d Bytes\n", size); + fseek (file, 0, SEEK_SET); + + if (memcmp (prolog_str, GD6_READ_PROLOG_STRING, 4) == 0) + if (gd6_sync_hardware () == GD_ERROR) + io_error (); + memcpy (buffer, prolog_str, 4); + buffer[4] = 1; + if (gd_send_prolog_bytes (buffer, 5) == GD_ERROR) + io_error (); + + // Transfer 0x38000 bytes + buffer[0] = 0x00; + buffer[1] = 0x80; + buffer[2] = 0x03; + buffer[3] = 0x00; + if (gd_send_prolog_bytes (buffer, 4) == GD_ERROR) + io_error (); + + if (gd_send_prolog_bytes (gdfilename, 11) == GD_ERROR) + io_error (); + + puts ("Press q to abort\n"); // print here, NOT before first GD I/O, + // because if we get here q works ;-) + starttime = time (NULL); + while ((bytesread = fread (buffer, 1, BUFFERSIZE, file))) + { + if (gd_send_bytes (buffer, bytesread) == GD_ERROR) + io_error (); + + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, size); + gd_checkabort (2); + } + + free (buffer); + fclose (file); + deinit_io (); + + return 0; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/gd.h b/ucon64/2.0/src/backup/gd.h new file mode 100644 index 0000000..dadc9ed --- /dev/null +++ b/ucon64/2.0/src/backup/gd.h @@ -0,0 +1,49 @@ +/* +gd.h - Game Doctor support for uCON64 + +Copyright (c) 2002 John Weidman +Copyright (c) 2002 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef GD_H +#define GD_H + +extern const st_getopt2_t gd_usage[]; + +#define GD_HEADER_START 0 +#define GD_HEADER_LEN 512 +#define GD3_MAX_UNITS 16 // Maximum that the hardware supports +// Each logical memory unit is 8 Mbit in size (internally it's 2*4 Mbit) + +#ifdef USE_PARALLEL +extern int gd3_read_rom (const char *filename, unsigned int parport); +extern int gd3_write_rom (const char *filename, unsigned int parport, + st_rominfo_t *rominfo); +extern int gd6_read_rom (const char *filename, unsigned int parport); +extern int gd6_write_rom (const char *filename, unsigned int parport, + st_rominfo_t *rominfo); +extern int gd3_read_sram (const char *filename, unsigned int parport); +extern int gd3_write_sram (const char *filename, unsigned int parport); +extern int gd6_read_sram (const char *filename, unsigned int parport); +extern int gd6_write_sram (const char *filename, unsigned int parport); +extern int gd3_read_saver (const char *filename, unsigned int parport); +extern int gd3_write_saver (const char *filename, unsigned int parport); +extern int gd6_read_saver (const char *filename, unsigned int parport); +extern int gd6_write_saver (const char *filename, unsigned int parport); +#endif // USE_PARALLEL + +#endif // GD_H diff --git a/ucon64/2.0/src/backup/interceptor.c b/ucon64/2.0/src/backup/interceptor.c new file mode 100644 index 0000000..457ad06 --- /dev/null +++ b/ucon64/2.0/src/backup/interceptor.c @@ -0,0 +1,46 @@ +/* +interceptor.c - Mega Disk/Super Disk Interceptor support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include "misc/misc.h" +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "interceptor.h" + + +const st_getopt2_t interceptor_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Mega Disk/Super Disk (Interceptor),"/*"19XX Taiwan Sang Ting Co. Ltd."*/, + NULL + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; diff --git a/ucon64/2.0/src/backup/interceptor.h b/ucon64/2.0/src/backup/interceptor.h new file mode 100644 index 0000000..957d1fb --- /dev/null +++ b/ucon64/2.0/src/backup/interceptor.h @@ -0,0 +1,31 @@ +/* +interceptor.h - Mega Disk/Super Disk Interceptor support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef INTERCEPTOR_H +#define INTERCEPTOR_H + +extern const st_getopt2_t interceptor_usage[]; + +typedef struct st_interceptor_header +{ + char pad[512]; +} st_interceptor_header_t; + +#endif diff --git a/ucon64/2.0/src/backup/libcd64/Makefile b/ucon64/2.0/src/backup/libcd64/Makefile new file mode 100644 index 0000000..4e0c4e5 --- /dev/null +++ b/ucon64/2.0/src/backup/libcd64/Makefile @@ -0,0 +1,169 @@ +CC=gcc + +ifdef DEBUG +# I think we only use gnu99 instead of c99 due to va_args extensions. +CFLAGS=-I. -Wall -W -pg -g -pedantic -ansi -DDEBUG +else +CFLAGS=-I. -Wall -W -O6 -funroll-loops -fexpensive-optimizations +endif + +ifndef DJGPP +# uname is not available by default under DOS +OSTYPE=$(shell uname -s) +else +OSTYPE=DJGPP +endif + +GCC_WIN=0 +ifeq ($(findstring MINGW,$(OSTYPE)),MINGW) +GCC_WIN=1 +endif +ifeq ($(findstring CYGWIN,$(OSTYPE)),CYGWIN) +GCC_WIN=1 +endif + +ifdef DJGPP +LDFLAGS= +else +ifeq ($(findstring BeOS,$(OSTYPE)),BeOS) +LDFLAGS=-nostart +else # Unix or Win GCC +LDFLAGS=-shared +endif +endif + +ifeq ($(findstring DJGPP,$(OSTYPE)),) +ifneq ($(GCC_WIN),1) +CFLAGS+=-fPIC +else +# Cygwin and MinGW need an import library for a DLL +LDFLAGS+=-Wl,--out-implib,libcd64dll.a +endif +endif + +# The next check is not really specific to FreeBSD or OpenBSD -- the version of +# gcc I use is just old. +ifeq ($(findstring FreeBSD,$(OSTYPE)),) +ifeq ($(findstring OpenBSD,$(OSTYPE)),) +CFLAGS+=-std=gnu99 +endif +endif + + +DEFAULT_BUILD=1 + +# If the user passed anything, we are not a default build. + +ifdef LIBIEEE1284 +DEFAULT_BUILD=0 +else +ifdef PPDEV +DEFAULT_BUILD=0 +else +ifdef PORTDEV +DEFAULT_BUILD=0 +else +ifdef RAWIO +DEFAULT_BUILD=0 +endif +endif +endif +endif + +ifeq ($(DEFAULT_BUILD),1) +# Put default build options for each OS here + +ifeq ($(findstring DJGPP,$(OSTYPE)),DJGPP) +RAWIO=1 +endif + +ifeq ($(findstring MINGW,$(OSTYPE)),MINGW) +RAWIO=1 +endif + +ifeq ($(findstring CYGWIN,$(OSTYPE)),CYGWIN) +RAWIO=1 +endif + +ifeq ($(findstring BeOS,$(OSTYPE)),BeOS) +RAWIO=1 +endif + +ifeq ($(findstring OpenBSD,$(OSTYPE)),OpenBSD) +# i386_iopl() is located in libi386.a (note the .a) +LIBS+=-L/usr/lib -li386 +RAWIO=1 +endif + +ifeq ($(findstring FreeBSD,$(OSTYPE)),FreeBSD) +RAWIO=1 +endif + +ifeq ($(findstring Linux,$(OSTYPE)),Linux) +ifeq ($(shell if test -r /usr/include/ieee1284.h; then echo 1; else echo 0; fi),1) +LIBIEEE1284=1 +endif +ifeq ($(shell if test -r /usr/include/linux/ppdev.h; then echo 1; else echo 0; fi),1) +PPDEV=1 +endif +PORTDEV=1 +RAWIO=1 +endif + +endif # DEFAULT_BUILD = 1 + +# Now for backend-specific defs + +ifdef LIBIEEE1284 +CFLAGS+=-DCD64_USE_LIBIEEE1284 +LIBS+=-lieee1284 +endif + +ifdef PPDEV +CFLAGS+=-DCD64_USE_PPDEV +endif + +ifdef PORTDEV +CFLAGS+=-DCD64_USE_PORTDEV +endif + +ifdef RAWIO +CFLAGS+=-DCD64_USE_RAWIO +endif + +default: all + +ifeq ($(findstring DJGPP,$(OSTYPE)),DJGPP) +all: libcd64.a +else +ifeq ($(GCC_WIN),1) +all: libcd64.a cd64.dll +else +all: libcd64.a libcd64.so +endif # GCC_WIN +endif # DJGPP + +# libcd64 stuff + +cd64io.o: cd64io.c + $(CC) $(CFLAGS) $^ -c -o $@ + +cd64lib.o: cd64lib.c + $(CC) $(CFLAGS) $^ -c -o $@ + +libcd64.a: cd64lib.o cd64io.o + ld -r $^ $(LIBS) -o $*.o +# rm -f $@ + ar crs $@ $*.o + +LDFLAGS+=$(LIBS) +ifeq ($(GCC_WIN),1) +cd64.dll: cd64lib.o cd64io.o +else +libcd64.so: cd64lib.o cd64io.o +endif +# rm -f $@ + $(CC) $^ $(LDFLAGS) -o $@ + +clean: + rm -f *.o *.so *.dll *.a a.out diff --git a/ucon64/2.0/src/backup/libcd64/Makefile.vc6 b/ucon64/2.0/src/backup/libcd64/Makefile.vc6 new file mode 100644 index 0000000..e71b0f1 --- /dev/null +++ b/ucon64/2.0/src/backup/libcd64/Makefile.vc6 @@ -0,0 +1,62 @@ +CC=cl.exe + +!ifdef DEBUG +CFLAGS=/nologo /I. /Zi /Oi /GZ /W3 /DDEBUG +!else +CFLAGS=/nologo /I. /W3 /O2 +!endif + +DEFAULT_BUILD=1 + +# If the user passed anything, we are not a default build. + +!ifdef LIBIEEE1284 +DEFAULT_BUILD=0 +!else +!ifdef RAWIO +DEFAULT_BUILD=0 +!endif +!endif + +!if $(DEFAULT_BUILD)==1 +# Put default build options here + +RAWIO=1 + +!endif # DEFAULT_BUILD = 1 + +# Now for backend-specific defs + +!ifdef LIBIEEE1284 +CFLAGS=$(CFLAGS) /DCD64_USE_LIBIEEE1284 +LIBS=$(LIBS) ieee1284.lib +!endif + +!ifdef RAWIO +CFLAGS=$(CFLAGS) /DCD64_USE_RAWIO +!endif + +default: all + +all: cd64.lib cd64.dll + +# libcd64 stuff + +cd64io.obj: cd64io.c + $(CC) $(CFLAGS) $** /c /Fo$@ + +cd64lib.obj: cd64lib.c + $(CC) $(CFLAGS) $** /c /Fo$@ + +cd64.lib: cd64lib.obj cd64io.obj + lib.exe /NOLOGO $** $(LIBS) /OUT:$@ + +cd64.dll: cd64lib.obj cd64io.obj + link.exe /NOLOGO /DLL $** $(LIBS) /DEF:cd64.def /IMPLIB:cd64dll.lib /OUT:$@ + +clean: + del *.obj + del *.exp + del *.lib + del *.dll + del *.pdb diff --git a/ucon64/2.0/src/backup/libcd64/cd64.def b/ucon64/2.0/src/backup/libcd64/cd64.def new file mode 100644 index 0000000..d368d99 --- /dev/null +++ b/ucon64/2.0/src/backup/libcd64/cd64.def @@ -0,0 +1,28 @@ +EXPORTS +cd64_create +cd64_send_byte +cd64_send_dword +cd64_grab_byte +cd64_grab_dword +cd64_trade_bytes +cd64_bios_grab +cd64_bios_send +cd64_ghemor_grab +cd64_ghemor_send +cd64_upload_dram +cd64_upload_ram +cd64_upload_bootemu +cd64_upload_sram +cd64_upload_flashram +cd64_upload_eeprom +cd64_upload_mempak +cd64_download_cart +cd64_download_dram +;cd64_download_ram +cd64_download_sram +cd64_download_flashram +cd64_download_eeprom +cd64_download_mempak +cd64_run_dram +cd64_run_cart +cd64_download_header diff --git a/ucon64/2.0/src/backup/libcd64/cd64io.c b/ucon64/2.0/src/backup/libcd64/cd64io.c new file mode 100644 index 0000000..b05a8f7 --- /dev/null +++ b/ucon64/2.0/src/backup/libcd64/cd64io.c @@ -0,0 +1,1005 @@ +/* + * + * cd64io.c + * + * I/O routines for CD64 device + * + * (c) 2004 Ryan Underwood + * Portions (c) 2004 Daniel Horchner (OpenBSD, FreeBSD, BeOS, Win32, DOS) + * + * May be distributed under the terms of the GNU Lesser/Library General Public + * License, or any later version of the same, as published by the Free Software + * Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#if defined __unix__ || defined __BEOS__ /* ioctl() */ +#include +#endif + +#include +#include "cd64io.h" + +#define DEBUG_LOWLEVEL 0 +#define BUSY_THRESHOLD 10000 +#define MAX_TRIES 5 + +#ifdef CD64_USE_RAWIO + +#if defined _WIN32 || defined __CYGWIN__ +#ifdef __CYGWIN__ +#define FILE_SEPARATOR_S "/" +#else +#define snprintf _snprintf +#define FILE_SEPARATOR_S "\\" +#endif + +static void *io_driver = NULL; +static int io_driver_found = 0; +/* io.dll */ +static char (WINAPI *PortIn)(short int) = NULL; +static void (WINAPI *PortOut)(short int, char) = NULL; +static short int (WINAPI *IsDriverInstalled)() = NULL; +/* DlPortIO.dll */ +static unsigned char (__stdcall *DlPortReadPortUchar)(unsigned long) = NULL; +static void (__stdcall *DlPortWritePortUchar)(unsigned long, unsigned char) = NULL; + +INLINE uint8_t inb(uint16_t); +INLINE void outb(uint8_t, uint16_t); +static uint8_t (*input_byte)(uint16_t) = inb; +static void (*output_byte)(uint8_t, uint16_t) = outb; +#endif + +#ifdef __BEOS__ +static int io_portfd; + +typedef struct st_ioport { + unsigned int port; + unsigned char data8; + unsigned short data16; +} st_ioport_t; +#endif + +#endif /* CD64_USE_RAWIO */ + +int cd64_send_byte(struct cd64_t *cd64, uint8_t what) { + return cd64->xfer(cd64, &what, NULL, 0); +} + +int cd64_send_dword(struct cd64_t *cd64, uint32_t what) { + + int ret = 1; + ret &= cd64_send_byte(cd64, (uint8_t) (what>>24)); + ret &= cd64_send_byte(cd64, (uint8_t) (what>>16)); + ret &= cd64_send_byte(cd64, (uint8_t) (what>>8)); + ret &= cd64_send_byte(cd64, (uint8_t) what); + return ret; +} + +int cd64_grab_byte(struct cd64_t *cd64, uint8_t *val) { + return cd64->xfer(cd64, NULL, val, 0); +} + +int cd64_grab_dword(struct cd64_t *cd64, uint32_t *val) { + + int ret = 1; + uint8_t grab; + if (val == NULL) return 0; + *val = 0; + + ret &= cd64_grab_byte(cd64, &grab); + *val |= grab << 24; + ret &= cd64_grab_byte(cd64, &grab); + *val |= grab << 16; + ret &= cd64_grab_byte(cd64, &grab); + *val |= grab << 8; + ret &= cd64_grab_byte(cd64, &grab); + *val |= grab; + return ret; +} + +int cd64_trade_bytes(struct cd64_t *cd64, uint8_t give, uint8_t *recv) { + return cd64->xfer(cd64, &give, recv, 0); +} + +/* Backend-specific defs go down here. */ + +#ifdef CD64_USE_LIBIEEE1284 + +int cd64_open_ieee1284(struct cd64_t *cd64) { + + struct parport_list pplist; + int ppflags = F1284_EXCL; + int ppcaps = 0; + int i; + int opened = 0; + + if (cd64->ppdev || !cd64->using_ppa) return 0; + + if (ieee1284_find_ports(&pplist, 0) < 0) { + cd64->notice_callback2("couldn't get port list\n"); + return 0; + } + + if (cd64->port < pplist.portc) { + /* Just use it as an index. */ + cd64->ppdev = pplist.portv[cd64->port]; + } + else { + /* Search for the ppdev matching its base address. */ + for (i = 0; i < pplist.portc; i++) { + if (cd64->port == (int) pplist.portv[i]->base_addr) { + cd64->ppdev = pplist.portv[i]; + } + } + } + + if (cd64->ppdev) { + if (ieee1284_open(cd64->ppdev, ppflags, &ppcaps) < 0) { + cd64->notice_callback2("failed opening ieee1284 port %d\n", cd64->port); + cd64->ppdev = NULL; + } + else { + opened = 1; + } + } + + ieee1284_free_ports(&pplist); + + if (opened && ieee1284_claim(cd64->ppdev) < 0) return 0; + else return opened; +} + +int cd64_close_ieee1284(struct cd64_t *cd64) { + + int ret; + + if (cd64->ppdev == NULL) return 1; + + ieee1284_release(cd64->ppdev); + ret = ieee1284_close(cd64->ppdev); + if (ret < 0) ret = 0; + else { + cd64->ppdev = NULL; + ret = 1; + } + + return ret; +} + +static INLINE int cd64_wait_ieee(struct cd64_t *cd64) { + + /* With ppdev, could we use an interrupt instead? The PPA + * could be modified... */ + + int i = 0; + int reset_tries = 0; + while (i < 10000) i++; /* FIXME is this necessary? */ + i = 0; + + while((ieee1284_read_status(cd64->ppdev)^S1284_INVERTED) & S1284_BUSY) { + i++; + if (i >= BUSY_THRESHOLD) { + /* The PPA is in a weird state. + * Try to knock some sense into it. */ + ieee1284_write_control(cd64->ppdev, (C1284_NINIT|C1284_NAUTOFD)^C1284_INVERTED); + ieee1284_write_control(cd64->ppdev, C1284_NINIT^C1284_INVERTED); + ieee1284_write_control(cd64->ppdev, (C1284_NINIT|C1284_NSTROBE)^C1284_INVERTED); + ieee1284_write_control(cd64->ppdev, C1284_NINIT^C1284_INVERTED); + reset_tries++; + i = 0; + USLEEP(1); + } + if (reset_tries > MAX_TRIES) break; + if (cd64->abort) return 0; + } + + return (reset_tries < MAX_TRIES); +} + +int cd64_xfer_ieee1284(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms) { + + if (!cd64_wait_ieee(cd64)) { return 0; } + + if (delayms) USLEEP(delayms); + ieee1284_data_dir(cd64->ppdev, 1); + if (delayms) USLEEP(delayms); + ieee1284_write_control(cd64->ppdev, (C1284_NINIT|C1284_NAUTOFD)^C1284_INVERTED); + if (delayms) USLEEP(delayms); + if (rd) { + *rd = ieee1284_read_data(cd64->ppdev); +#if DEBUG_LOWLEVEL + printf("got %xh", *rd); + if (*rd > 0x20) printf(" (%c)", *rd); + printf("\n"); +#endif + } + + if (delayms) USLEEP(delayms); + ieee1284_data_dir(cd64->ppdev, 0); + if (delayms) USLEEP(delayms); + ieee1284_write_control(cd64->ppdev, C1284_NINIT^C1284_INVERTED); + if (delayms) USLEEP(delayms); + if (wr) { + ieee1284_write_data(cd64->ppdev, *wr); +#if DEBUG_LOWLEVEL + printf("put %xh", *wr); + if (*wr > 0x20) printf(" (%c)", *wr); + printf("\n"); +#endif + } + if (delayms) USLEEP(delayms); + ieee1284_write_control(cd64->ppdev, (C1284_NINIT|C1284_NSTROBE)^C1284_INVERTED); + if (delayms) USLEEP(delayms); + ieee1284_write_control(cd64->ppdev, C1284_NINIT^C1284_INVERTED); + + return 1; +} + +#endif /* CD64_USE_LIBIEEE1284 */ + + +#ifdef CD64_USE_PPDEV + +int cd64_open_ppdev(struct cd64_t *cd64) { + + char *device = "/dev/parport%d"; + char realdev[128+1]; + + if (cd64->ppdevfd || !cd64->using_ppa) return 0; + /* This should be a port number only, not an address */ + if (cd64->port > PARPORT_MAX) return 0; + + snprintf(realdev, 128, device, cd64->port); + + if ((cd64->ppdevfd = open(realdev, O_RDWR)) == -1) { + cd64->notice_callback2("open: %s", strerror(errno)); + cd64->ppdevfd = 0; + return 0; + } + + if (ioctl(cd64->ppdevfd, PPEXCL) != 0) { + cd64->notice_callback2("PPEXCL: %s", strerror(errno)); + close(cd64->ppdevfd); + cd64->ppdevfd = 0; + return 0; + } + + if (ioctl(cd64->ppdevfd, PPCLAIM) != 0) { + cd64->notice_callback2("PPCLAIM: %s", strerror(errno)); + close(cd64->ppdevfd); + cd64->ppdevfd = 0; + return 0; + } + + return 1; +} + +int cd64_close_ppdev(struct cd64_t *cd64) { + + int ret = 1; + + if (cd64->ppdevfd == 0) return 1; + + if (ioctl(cd64->ppdevfd, PPRELEASE) != 0) { + cd64->notice_callback2("PPRELEASE: %s", strerror(errno)); + ret = 0; + } + + close(cd64->ppdevfd); + cd64->ppdevfd = 0; + return ret; +} + +static INLINE int cd64_wait_ppdev(struct cd64_t *cd64) { + + /* With ppdev, could we use an interrupt instead? The PPA + * could be modified... */ + + int i = 0; + int reset_tries = 0; + uint8_t status; + int dir; + i = 0; + + if (ioctl(cd64->ppdevfd, PPRSTATUS, &status) != 0) cd64->notice_callback2("PPRSTATUS: %s", strerror(errno)); + + while(status & 0x80) { + i++; + if (i >= BUSY_THRESHOLD) { + /* The PPA is in a weird state. + * Try to knock some sense into it. */ + dir = 1; + if (ioctl(cd64->ppdevfd, PPDATADIR, &dir) != 0) cd64->notice_callback2("PPDATADIR: %s", strerror(errno)); + status = PARPORT_CONTROL_INIT | PARPORT_CONTROL_AUTOFD; /* 0x26 */ + if (ioctl(cd64->ppdevfd, PPWCONTROL, &status) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno)); + + dir = 0; + if (ioctl(cd64->ppdevfd, PPDATADIR, &dir) != 0) cd64->notice_callback2("PPDATADIR: %s", strerror(errno)); + status = PARPORT_CONTROL_INIT; /* 0x04 */ + if (ioctl(cd64->ppdevfd, PPWCONTROL, &status) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno)); + status = PARPORT_CONTROL_INIT | PARPORT_CONTROL_STROBE; /* 0x05 */ + if (ioctl(cd64->ppdevfd, PPWCONTROL, &status) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno)); + status = PARPORT_CONTROL_INIT; /* 0x04 */ + if (ioctl(cd64->ppdevfd, PPWCONTROL, &status) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno)); + reset_tries++; + i = 0; + USLEEP(1); + } + if (cd64->abort) return 0; + if (reset_tries > MAX_TRIES) break; + + if (ioctl(cd64->ppdevfd, PPRSTATUS, &status) != 0) cd64->notice_callback2("PPRSTATUS: %s", strerror(errno)); + } + + return (reset_tries < MAX_TRIES); +} + +int cd64_xfer_ppdev(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms) { + + uint8_t ctl; + int dir; + + if (!cd64_wait_ppdev(cd64)) { return 0; } + + if (delayms) USLEEP(delayms); + dir = 1; + if (ioctl(cd64->ppdevfd, PPDATADIR, &dir) != 0) cd64->notice_callback2("PPDATADIR: %s", strerror(errno)); + if (delayms) USLEEP(delayms); + ctl = PARPORT_CONTROL_INIT | PARPORT_CONTROL_AUTOFD; + if (ioctl(cd64->ppdevfd, PPWCONTROL, &ctl) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno)); + if (delayms) USLEEP(delayms); + if (rd) { + if (ioctl(cd64->ppdevfd, PPRDATA, rd) != 0) cd64->notice_callback2("PPRDATA: %s", strerror(errno)); +#if DEBUG_LOWLEVEL + printf("got %xh", *rd); + if (*rd > 0x20) printf(" (%c)", *rd); + printf("\n"); +#endif + } + + if (delayms) USLEEP(delayms); + dir = 0; + if (ioctl(cd64->ppdevfd, PPDATADIR, &dir) != 0) cd64->notice_callback2("PPDATADIR: %s", strerror(errno)); + if (delayms) USLEEP(delayms); + ctl = PARPORT_CONTROL_INIT; + if (ioctl(cd64->ppdevfd, PPWCONTROL, &ctl) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno)); + if (delayms) USLEEP(delayms); + if (wr) { + if (ioctl(cd64->ppdevfd, PPWDATA, wr) != 0) cd64->notice_callback2("PPWDATA: %s", strerror(errno)); +#if DEBUG_LOWLEVEL + printf("put %xh", *wr); + if (*wr > 0x20) printf(" (%c)", *wr); + printf("\n"); +#endif + } + if (delayms) USLEEP(delayms); + ctl = PARPORT_CONTROL_INIT | PARPORT_CONTROL_STROBE; + if (ioctl(cd64->ppdevfd, PPWCONTROL, &ctl) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno)); + if (delayms) USLEEP(delayms); + ctl = PARPORT_CONTROL_INIT; + if (ioctl(cd64->ppdevfd, PPWCONTROL, &ctl) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno)); + + return 1; +} + +#endif + +#ifdef CD64_USE_PORTDEV + +int cd64_open_portdev(struct cd64_t *cd64) { + + if (cd64->portdevfd || cd64->port == 0) return 0; + + if ((cd64->portdevfd = open("/dev/port", O_RDWR)) == -1) { + cd64->notice_callback2("open: %s", strerror(errno)); + cd64->notice_callback2("portdev requires CAP_SYS_RAWIO capability"); + cd64->portdevfd = 0; + return 0; + } + + return 1; +} + +int cd64_close_portdev(struct cd64_t *cd64) { + + if (cd64->portdevfd == 0) return 1; + + if (close(cd64->portdevfd) == -1) { + cd64->notice_callback2("close: %s", strerror(errno)); + return 0; + } + cd64->portdevfd = 0; + return 1; +} + +static INLINE int cd64_wait_portdev(struct cd64_t *cd64) { + + int i = 0; + int reset_tries = 0; + uint8_t status; + int dir; + i = 0; + + if (cd64->using_ppa) { + lseek(cd64->portdevfd, cd64->port+1, SEEK_SET); + read(cd64->portdevfd, &status, 1); + + while(status & 0x80) { + i++; + if (i >= BUSY_THRESHOLD) { + /* The PPA is in a weird state. + * Try to knock some sense into it. */ + dir = 1; + status = 0x06 | (dir << 5); + lseek(cd64->portdevfd, cd64->port+2, SEEK_SET); + write(cd64->portdevfd, &status, 1); + + dir = 0; + status = 0x04 | (dir << 5); + lseek(cd64->portdevfd, cd64->port+2, SEEK_SET); + write(cd64->portdevfd, &status, 1); + status = 0x05 | (dir << 5); + lseek(cd64->portdevfd, cd64->port+2, SEEK_SET); + write(cd64->portdevfd, &status, 1); + status = 0x04 | (dir << 5); + lseek(cd64->portdevfd, cd64->port+2, SEEK_SET); + write(cd64->portdevfd, &status, 1); + + reset_tries++; + i = 0; + USLEEP(1); + } + if (cd64->abort) return 0; + if (reset_tries > MAX_TRIES) { + break; + } + + lseek(cd64->portdevfd, cd64->port+1, SEEK_SET); + read(cd64->portdevfd, &status, 1); + } + } + else { /* Comms link */ + lseek(cd64->portdevfd, cd64->port+2, SEEK_SET); + read(cd64->portdevfd, &status, 1); + while (status & 1) { + /* Do we need to handle a stuck situation here? */ + lseek(cd64->portdevfd, cd64->port+2, SEEK_SET); + read(cd64->portdevfd, &status, 1); + } + } + + return (reset_tries < MAX_TRIES); +} + +int cd64_xfer_portdev(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms) { + + uint8_t ctl; + int dir; + + if (cd64->using_ppa) { + + if (!cd64_wait_portdev(cd64)) { return 0; } + + if (delayms) USLEEP(delayms); + dir = 1; + ctl = 0x06 | (dir << 5); + lseek(cd64->portdevfd, cd64->port+2, SEEK_SET); + write(cd64->portdevfd, &ctl, 1); + if (delayms) USLEEP(delayms); + if (rd) { + lseek(cd64->portdevfd, cd64->port, SEEK_SET); + read(cd64->portdevfd, rd, 1); +#if DEBUG_LOWLEVEL + printf("got %xh", *rd); + if (*rd > 0x20) printf(" (%c)", *rd); + printf("\n"); +#endif + } + + if (delayms) USLEEP(delayms); + dir = 0; + ctl = 0x04 | (dir << 5); + lseek(cd64->portdevfd, cd64->port+2, SEEK_SET); + write(cd64->portdevfd, &ctl, 1); + if (delayms) USLEEP(delayms); + if (wr) { + lseek(cd64->portdevfd, cd64->port, SEEK_SET); + write(cd64->portdevfd, wr, 1); +#if DEBUG_LOWLEVEL + printf("put %xh", *wr); + if (*wr > 0x20) printf(" (%c)", *wr); + printf("\n"); +#endif + } + if (delayms) USLEEP(delayms); + ctl = 0x05 | (dir << 5); + lseek(cd64->portdevfd, cd64->port+2, SEEK_SET); + write(cd64->portdevfd, &ctl, 1); + if (delayms) USLEEP(delayms); + ctl = 0x04 | (dir << 5); + lseek(cd64->portdevfd, cd64->port+2, SEEK_SET); + write(cd64->portdevfd, &ctl, 1); + } + else { /* Comms link */ + lseek(cd64->portdevfd, cd64->port, SEEK_SET); + write(cd64->portdevfd, wr, 1); + if (!cd64_wait_portdev(cd64)) { return 0; } + lseek(cd64->portdevfd, cd64->port, SEEK_SET); + read(cd64->portdevfd, rd, 1); + } + + return 1; +} + +#endif + + +#ifdef CD64_USE_RAWIO + +#if defined _WIN32 || defined __CYGWIN__ + +static void *open_module(char *module_name, struct cd64_t *cd64) { + + void *handle = LoadLibrary(module_name); + if (handle == NULL) { + LPTSTR strptr; + + cd64->notice_callback2("LoadLibrary: %s", strerror(errno)); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &strptr, 0, NULL); + cd64->notice_callback2(strptr); + LocalFree(strptr); + exit(1); + } + return handle; +} + +static void close_module(void *handle, struct cd64_t *cd64) { + + if (!FreeLibrary((HINSTANCE) handle)) { + LPTSTR strptr; + + cd64->notice_callback2("FreeLibrary: %s", strerror(errno)); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &strptr, 0, NULL); + cd64->notice_callback2(strptr); + LocalFree(strptr); + exit(1); + } +} + +static void *get_symbol(void *handle, char *symbol_name, struct cd64_t *cd64) { + + void *symptr = (void *) GetProcAddress((HINSTANCE) handle, symbol_name); + if (symptr == NULL) { + LPTSTR strptr; + + cd64->notice_callback2("GetProcAddress: %s", strerror(errno)); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &strptr, 0, NULL); + cd64->notice_callback2(strptr); + LocalFree(strptr); + exit(1); + } + return symptr; +} + +/* io.dll */ +static uint8_t io_input_byte(uint16_t port) { + return PortIn(port); +} + +static void io_output_byte(uint8_t byte, uint16_t port) { + PortOut(port, byte); +} + +/* DlPortIO.dll */ +static uint8_t dlportio_input_byte(uint16_t port) { + return DlPortReadPortUchar(port); +} + +static void dlportio_output_byte(uint8_t byte, uint16_t port) { + DlPortWritePortUchar(port, byte); +} + +#define NODRIVER_MSG "ERROR: No (working) I/O port driver\n" + +#ifdef __CYGWIN__ +static int new_exception_handler(PEXCEPTION_RECORD exception_record, + void *establisher_frame, PCONTEXT context_record, + void *dispatcher_context) { + + (void) establisher_frame; + (void) context_record; + (void) dispatcher_context; + if (exception_record->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION) { + fputs(NODRIVER_MSG, stderr); + exit(1); + } + return EXCEPTION_CONTINUE_SEARCH; +} +#elif defined _WIN32 +static LONG new_exception_filter(LPEXCEPTION_POINTERS exception_pointers) { + + if (exception_pointers->ExceptionRecord->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION) { + fputs(NODRIVER_MSG, stderr); + exit(1); + } + return EXCEPTION_CONTINUE_SEARCH; +} +#endif +#endif /* _WIN32 || __CYGWIN__ */ + +#if ((defined _WIN32 || defined __CYGWIN__ || defined __BEOS__ || \ + defined __MSDOS__) && \ + (defined __i386__ || defined __x86_64__)) || defined _MSC_VER +INLINE uint8_t inb(uint16_t port) { + +#ifdef __MSDOS__ + return inportb(port); +#elif defined __BEOS__ + st_ioport_t temp; + + temp.port = port; + ioctl(io_portfd, 'r', &temp, 0); + + return temp.data8; +#else /* Win32 */ + if (io_driver_found) { + return input_byte(port); + } + else { +#ifdef _MSC_VER + return (unsigned char) inp(port); +#else + unsigned char byte; + __asm__ __volatile__ + ("inb %1, %0" + : "=a" (byte) + : "d" (port) + ); + return byte; +#endif + } +#endif +} + +INLINE void outb(uint8_t byte, uint16_t port) { + +#ifdef __MSDOS__ + outportb(port, byte); +#elif defined __BEOS__ + st_ioport_t temp; + + temp.port = port; + temp.data8 = byte; + ioctl(io_portfd, 'w', &temp, 0); +#else /* Win32 */ + if (io_driver_found) { + output_byte(byte, port); + } + else { +#ifdef _MSC_VER + outp(port, byte); +#else + __asm__ __volatile__ + ("outb %1, %0" + : + : "d" (port), "a" (byte) + ); +#endif + } +#endif +} +#endif /* inb/outb defs */ + +int cd64_open_rawio(struct cd64_t *cd64) { + + int ret; + (void) ret; + + /* NOTE: we will soon be able to use ioperm on the entire + * 16-bit port range. Find out what Linux kernels support it. */ + + if (cd64->port < 0x200) { + cd64->notice_callback2("Erroneous port %xh", cd64->port); + return 0; + } + +#ifdef __linux__ + if (cd64->port < 0x3fd) { + if (cd64->using_ppa) { + ret = ioperm(cd64->port, 3, 1); + } + else { + ret = ioperm(cd64->port, 1, 1); + ret |= ioperm(cd64->port+2, 1, 1); + } + + if (ret == -1) { + cd64->notice_callback2("ioperm: %s", strerror(errno)); + cd64->notice_callback2("rawio requires CAP_SYS_RAWIO capability"); + return 0; + } + } + else { + ret = iopl(3); + if (ret == -1) { + cd64->notice_callback2("iopl: %s", strerror(errno)); + cd64->notice_callback2("rawio requires CAP_SYS_RAWIO capability"); + return 0; + } + } +#elif defined __OpenBSD__ + /* I cannot test i386_set_ioperm(), so I only use i386_iopl() */ + ret = i386_iopl(3); + if (ret == -1) { + cd64->notice_callback2("i386_iopl: %s", strerror(errno)); + return 0; + } +#elif defined __FreeBSD__ + cd64->portdevfd = open("/dev/io", O_RDWR); + if (cd64->portdevfd == -1) { + cd64->portdevfd = 0; + cd64->notice_callback2("open: %s", strerror(errno)); + cd64->notice_callback2("Could not open I/O port device (/dev/io)"); + return 0; + } +#elif defined __BEOS__ + io_portfd = open("/dev/misc/ioport", O_RDWR | O_NONBLOCK); + if (io_portfd == -1) { + io_portfd = 0; + cd64->notice_callback2("open: %s", strerror(errno)); + cd64->notice_callback2("Could not open I/O port device (no driver)"); + exit(1); + } +#elif defined _WIN32 || defined __CYGWIN__ + { + char fname[FILENAME_MAX]; + io_driver_found = 0; + + if (!cd64->io_driver_dir[0]) strcpy(cd64->io_driver_dir, "."); + snprintf (fname, FILENAME_MAX, "%s" FILE_SEPARATOR_S "%s", + cd64->io_driver_dir, "dlportio.dll"); + if (access(fname, F_OK) == 0) { + io_driver = open_module(fname, cd64); + + io_driver_found = 1; + DlPortReadPortUchar = (unsigned char (__stdcall *) (unsigned long)) + get_symbol(io_driver, "DlPortReadPortUchar", cd64); + DlPortWritePortUchar = (void (__stdcall *) (unsigned long, unsigned char)) + get_symbol(io_driver, "DlPortWritePortUchar", cd64); + input_byte = dlportio_input_byte; + output_byte = dlportio_output_byte; + } + + if (!io_driver_found) { + snprintf (fname, FILENAME_MAX, "%s" FILE_SEPARATOR_S "%s", + cd64->io_driver_dir, "io.dll"); + if (access(fname, F_OK) == 0) { + io_driver = open_module(fname, cd64); + + IsDriverInstalled = (short int (WINAPI *) ()) + get_symbol(io_driver, "IsDriverInstalled", cd64); + if (IsDriverInstalled()) { + io_driver_found = 1; + PortIn = (char (WINAPI *) (short int)) + get_symbol(io_driver, "PortIn", cd64); + PortOut = (void (WINAPI *) (short int, char)) + get_symbol(io_driver, "PortOut", cd64); + input_byte = io_input_byte; + output_byte = io_output_byte; + } + } + } + } + + { + /* __try and __except are not supported by MinGW and Cygwin. MinGW has + * __try1 and __except1, but using them requires more code than we + * currently have. Cygwin does something stupid which breaks + * SetUnhandledExceptionFilter()... */ +#ifdef __CYGWIN__ /* Cygwin */ + exception_list list; + exception_handler *org_handler; + cygwin_internal(CW_INIT_EXCEPTIONS, &list); + org_handler = list.handler; + list.handler = new_exception_handler; + input_byte(0x378); + list.handler = org_handler; +#elif defined _WIN32 /* MinGW & Visual C++ */ + LPTOP_LEVEL_EXCEPTION_FILTER org_exception_filter = + SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER) new_exception_filter); + input_byte(0x378); /* 0x378 is okay */ + + /* if we get here accessing I/O port 0x378 did not cause an exception */ + SetUnhandledExceptionFilter(org_exception_filter); +#endif + } +#endif /* _WIN32 || __CYGWIN__ */ + + return 1; +} + +int cd64_close_rawio(struct cd64_t *cd64) { + + int ret; + (void) ret; + (void) cd64; + +#ifdef __linux__ + if (cd64->port < 0x3fd) { + if (cd64->using_ppa) { + ret = ioperm(cd64->port, 3, 0); + } + else { + ret = ioperm(cd64->port, 1, 0); + ret |= ioperm(cd64->port+2, 1, 0); + } + + if (ret == -1) { + cd64->notice_callback2("ioperm: %s", strerror(errno)); + return 0; + } + } + else { + ret = iopl(0); + if (ret == -1) { + cd64->notice_callback2("iopl: %s", strerror(errno)); + return 0; + } + } +#elif defined __OpenBSD__ + /* I cannot test i386_set_ioperm(), so I only use i386_iopl() */ + ret = i386_iopl(0); + if (ret == -1) { + cd64->notice_callback2("i386_iopl: %s", strerror(errno)); + return 0; + } +#elif defined __FreeBSD__ + if (close(cd64->portdevfd) == -1) { + cd64->notice_callback2("close: %s", strerror(errno)); + return 0; + } + cd64->portdevfd = 0; +#elif defined __BEOS__ + if (close(io_portfd) == -1) { + cd64->notice_callback2("close: %s", strerror(errno)); + return 0; + } + io_portfd = 0; +#elif defined _WIN32 || defined __CYGWIN__ + close_module(io_driver, cd64); + io_driver = NULL; + io_driver_found = 0; + input_byte = inb; + output_byte = outb; +#endif + + return 1; +} + +static INLINE int cd64_wait_rawio(struct cd64_t *cd64) { + + int i = 0; + int reset_tries = 0; + uint8_t status; + int dir; + i = 0; + + if (cd64->using_ppa) { + status = inb((uint16_t) (cd64->port+1)); + + while(status & 0x80) { + i++; + if (i >= BUSY_THRESHOLD) { + /* The PPA is in a weird state. + * Try to knock some sense into it. */ + dir = 1; + status = 0x06 | (dir << 5); + outb(status, (uint16_t) (cd64->port+2)); + + dir = 0; + status = 0x04 | (dir << 5); + outb(status, (uint16_t) (cd64->port+2)); + status = 0x05 | (dir << 5); + outb(status, (uint16_t) (cd64->port+2)); + status = 0x04 | (dir << 5); + outb(status, (uint16_t) (cd64->port+2)); + + reset_tries++; + i = 0; + USLEEP(1); + } + if (cd64->abort) return 0; + if (reset_tries > MAX_TRIES) { + break; + } + + status = inb((uint16_t) (cd64->port+1)); + } + } + else { /* Comms link */ + status = inb((uint16_t) (cd64->port+2)); + while (status & 1) { + /* Do we need to handle a stuck situation here? */ + status = inb((uint16_t) (cd64->port+2)); + } + } + + return (reset_tries < MAX_TRIES); +} + +int cd64_xfer_rawio(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms) { + + uint8_t ctl; + int dir; + + if (cd64->using_ppa) { + + if (!cd64_wait_rawio(cd64)) { return 0; } + + if (delayms) USLEEP(delayms); + dir = 1; + ctl = 0x06 | (dir << 5); + outb(ctl, (uint16_t) (cd64->port+2)); + if (delayms) USLEEP(delayms); + if (rd) { + *rd = inb((uint16_t) cd64->port); +#if DEBUG_LOWLEVEL + printf("got %xh", *rd); + if (*rd > 0x20) printf(" (%c)", *rd); + printf("\n"); +#endif + } + + if (delayms) USLEEP(delayms); + dir = 0; + ctl = 0x04 | (dir << 5); + outb(ctl, (uint16_t) (cd64->port+2)); + if (delayms) USLEEP(delayms); + if (wr) { + outb(*wr, (uint16_t) cd64->port); +#if DEBUG_LOWLEVEL + printf("put %xh", *wr); + if (*wr > 0x20) printf(" (%c)", *wr); + printf("\n"); +#endif + } + if (delayms) USLEEP(delayms); + ctl = 0x05 | (dir << 5); + outb(ctl, (uint16_t) (cd64->port+2)); + if (delayms) USLEEP(delayms); + ctl = 0x04 | (dir << 5); + outb(ctl, (uint16_t) (cd64->port+2)); + } + else { /* Comms link */ + outb(*wr, (uint16_t) cd64->port); + if (!cd64_wait_rawio(cd64)) { return 0; } + *rd = inb((uint16_t) cd64->port); + } + + return 1; +} + +#endif diff --git a/ucon64/2.0/src/backup/libcd64/cd64io.h b/ucon64/2.0/src/backup/libcd64/cd64io.h new file mode 100644 index 0000000..f790f4e --- /dev/null +++ b/ucon64/2.0/src/backup/libcd64/cd64io.h @@ -0,0 +1,114 @@ +#ifndef __CD64IO_H__ +#define __CD64IO_H__ + +#ifdef CD64_USE_LIBIEEE1284 +#include +int cd64_open_ieee1284(struct cd64_t *cd64); +int cd64_close_ieee1284(struct cd64_t *cd64); +int cd64_xfer_ieee1284(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms); +#endif + +#ifdef CD64_USE_PPDEV +#ifndef __linux__ +#error ppdev can only be used on Linux +#endif +#include +#include +#include +int cd64_open_ppdev(struct cd64_t *cd64); +int cd64_close_ppdev(struct cd64_t *cd64); +int cd64_xfer_ppdev(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms); +#endif + +#ifdef CD64_USE_PORTDEV +#ifndef __linux__ +#error portdev can only be used on Linux +#endif +int cd64_open_portdev(struct cd64_t *cd64); +int cd64_close_portdev(struct cd64_t *cd64); +int cd64_xfer_portdev(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms); +#endif + +#ifdef CD64_USE_RAWIO +/* #define REALLY_SLOW_IO */ +#if defined __linux__ && (defined __i386__ || defined __x86_64__) +#include +#endif +#ifdef __OpenBSD__ +#include +#include +#include +/* pio.h defines several I/O functions & macros, including the macros inb() and + * outb(). This shows that using a bit of inline assembly is not such a bad idea + * at all. */ +#undef inb +#define inb(port) __inb(port) +#undef outb +#define outb(data, port) __outb(port, data) +#endif +#ifdef __FreeBSD__ +#include +#include +/* Almost the same story as under OpenBSD. cpufunc.h defines the macros inb() + * and outb(). We redefine them. Be sure _POSIX_SOURCE is not defined before + * including . */ +#undef inb +#define inb(port) inbv(port) +#undef outb +#define outb(data, port) outbv(port, data) +#endif +#ifdef __BEOS__ +#include +#endif +#ifdef _MSC_VER +#include /* inp() & outp() */ +#include /* access() */ +#define F_OK 0 +#endif +#ifdef __MSDOS__ +#include /* inportb() & outportb() */ +#endif +#if defined _WIN32 || defined __CYGWIN__ +#include /* defines _WIN32 (checks for */ +#include /* __CYGWIN__ must come first) */ +#ifdef __CYGWIN__ +#include +#include +#include +#endif +#endif /* _WIN32 || __CYGWIN__ */ + +int cd64_open_rawio(struct cd64_t *cd64); +int cd64_close_rawio(struct cd64_t *cd64); +int cd64_xfer_rawio(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms); +#endif + +#if defined _WIN32 && !defined __CYGWIN__ +/* milliseconds */ +#include +#define USLEEP(x) Sleep(x) +#elif defined __MSDOS__ +/* milliseconds */ +#include +#define USLEEP(x) delay(x) +#elif defined __BEOS__ +/* microseconds */ +#include +#define USLEEP(x) snooze(x) +#else /* Unix & Cygwin */ +/* microseconds */ +#include +#define USLEEP(x) usleep(x) +#endif + +#if __STDC_VERSION__ >= 19990L && !defined DEBUG +/* If DEBUG is defined the keyword inline is not recognised (syntax error). */ +#define INLINE inline +#elif defined _MSC_VER +/* Visual C++ doesn't allow inline in C source code */ +#define INLINE __inline +#else +#define INLINE +#endif + +#endif diff --git a/ucon64/2.0/src/backup/libcd64/cd64lib.c b/ucon64/2.0/src/backup/libcd64/cd64lib.c new file mode 100644 index 0000000..819601c --- /dev/null +++ b/ucon64/2.0/src/backup/libcd64/cd64lib.c @@ -0,0 +1,990 @@ +/* + * + * cd64lib.c + * + * Library routines for CD64 handling + * + * (c) 2004 Ryan Underwood + * Portions (c) 2004 Daniel Horchner (Win32, read/write/seek callbacks) + * + * May be distributed under the terms of the GNU Lesser/Library General + * Public License, or any later version of the same, as published by the Free + * Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#if defined __unix__ || defined __BEOS__ +#include +#endif +#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__ +#include +#else +#include +#endif + +#include +#include +#include + +#include "cd64io.h" + +static uint8_t *cd64_tmp_buf; +static uint32_t cd64_tmp_buf_offset; + +static int cd64_notice_helper(FILE *file, const char *format, va_list argptr) { + + int n_chars; + + fputs("libcd64: ", file); + n_chars = vfprintf(file, format, argptr); + fputc('\n', file); + fflush(file); + + return n_chars; +} + +int cd64_notice(const char *format, ...) { + + va_list argptr; + int n_chars; + + va_start(argptr, format); + n_chars = cd64_notice_helper(stdout, format, argptr); + va_end(argptr); + + return n_chars; +} + +int cd64_notice2(const char *format, ...) { + + va_list argptr; + int n_chars; + + va_start(argptr, format); + n_chars = cd64_notice_helper(stderr, format, argptr); + va_end(argptr); + + return n_chars; +} + +int cd64_read(void *io_id, void *buffer, uint32_t size) { + return fread(buffer, 1, size, (FILE *) io_id); +} + +int cd64_write(void *io_id, void *buffer, uint32_t size) { + return fwrite(buffer, 1, size, (FILE *) io_id); +} + +int32_t cd64_tell(void *io_id) { + return (int32_t) ftell((FILE *) io_id); +} + +int cd64_seek(void *io_id, int32_t offset, int whence) { + return fseek((FILE *) io_id, offset, whence); +} + +int cd64_create(struct cd64_t *cd64, method_t method, + uint16_t port, protocol_t protocol, int ppa) { + + cd64->using_ppa = (ppa)? 1 : 0; + cd64->protocol = protocol; + cd64->port = port; + + if (!cd64->notice_callback) cd64->notice_callback = cd64_notice; + if (!cd64->notice_callback2) cd64->notice_callback2 = cd64_notice2; + + cd64->read_callback = cd64_read; + cd64->write_callback = cd64_write; + cd64->tell_callback = cd64_tell; + cd64->seek_callback = cd64_seek; + +#ifdef CD64_USE_LIBIEEE1284 + if (method == LIBIEEE1284) { + cd64->devopen = cd64_open_ieee1284; + cd64->xfer = cd64_xfer_ieee1284; + cd64->devclose = cd64_close_ieee1284; + return 1; + } + else +#endif + +#ifdef CD64_USE_PPDEV + if (method == PPDEV) { + cd64->devopen = cd64_open_ppdev; + cd64->xfer = cd64_xfer_ppdev; + cd64->devclose = cd64_close_ppdev; + return 1; + } + else +#endif + +#ifdef CD64_USE_PORTDEV + if (method == PORTDEV) { + cd64->devopen = cd64_open_portdev; + cd64->xfer = cd64_xfer_portdev; + cd64->devclose = cd64_close_portdev; + return 1; + } + else +#endif + +#ifdef CD64_USE_RAWIO + if (method == RAWIO) { + cd64->devopen = cd64_open_rawio; + cd64->xfer = cd64_xfer_rawio; + cd64->devclose = cd64_close_rawio; + return 1; + } + else +#endif + + { + switch (method) { + case LIBIEEE1284: + cd64->notice_callback2("libieee1284 not supported."); + break; + case PPDEV: + cd64->notice_callback2("ppdev not supported."); + break; + case PORTDEV: + cd64->notice_callback2("portdev not supported."); + break; + case RAWIO: + cd64->notice_callback2("rawio not supported."); + break; + default: + cd64->notice_callback2("unknown hw access method."); + break; + } + return 0; + } + +} + +/* CD64 BIOS routines */ + +static int cd64_bios_sync(struct cd64_t *cd64) { + + unsigned char ret1 = 0, ret2 = 0; + + cd64->notice_callback("Waiting for CD64 comms to come online..."); + + while (!(ret1 == 'R' && ret2 == 'W')) { + if (cd64->abort) return 0; + /* approximation here */ + cd64_send_byte(cd64, 'W'); + cd64_send_byte(cd64, 'B'); + cd64_trade_bytes(cd64, 'B', &ret1); + cd64_trade_bytes(cd64, 'B', &ret2); + } + return 1; +} + +int cd64_bios_grab(struct cd64_t *cd64, void *io_id, uint32_t addr, + uint32_t length, int *elapsed) { + + unsigned int i; + unsigned short pc_csum = 0, n64_csum = 0; +#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__ + struct timeb tb; +#else + struct timeval tv; +#endif + unsigned long int sec = 0, usec = 0; + uint8_t send, recv; + uint8_t cmd = 0xff; + uint8_t tmp; + + if (!length || length&0x00000003 || addr&0x00000003) return 0; + if (addr <= 0xa03fffff && addr+length <= 0xa03fffff) { + cmd = BIOS_DUMP_N64; + } + else if (addr >= 0xa8000000 && addr <= 0xbfffffff + && addr+length <= 0xbfffffff) { + cmd = BIOS_DUMP_PI; + } + + if (cmd == 0xff) { + cd64->notice_callback2("Invalid memory range %lxh-%lxh for operation.", + (long unsigned int) addr, (long unsigned int) addr+length); + return 0; + } + + /* Try to get in sync with the CD64 + * We use a delay here to give the PPA a chance to power up. */ + send = 0xff; + cd64->xfer(cd64, &send, NULL, 1000); + cd64_bios_sync(cd64); + + if (elapsed != NULL) { +#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__ + ftime(&tb); + sec = tb.time; + usec = tb.millitm*1000; +#else + gettimeofday(&tv, 0); + sec = tv.tv_sec; + usec = tv.tv_usec; +#endif + } + + cd64_send_byte(cd64, cmd); + cd64_send_dword(cd64, addr); + cd64_send_dword(cd64, length); + + /* dummy exchange, needed for some reason */ + cd64_grab_byte(cd64, &recv); + + for (i = 1; i <= length; i++) { + + if (cd64->abort) return 0; + if (!cd64_grab_byte(cd64, &tmp)) return 0; + if (!cd64->write_callback(io_id, &tmp, 1)) { + cd64->notice_callback2("Error writing to output."); + return 0; + } + pc_csum += tmp; + if ((i % CD64_BUFFER_SIZE == 0) && cd64->progress_callback) { + cd64->progress_callback(i, length); + } + } + + if (cd64->progress_callback) { + cd64->progress_callback(i, length); + } + + if (elapsed != NULL) { +#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__ + ftime(&tb); + *elapsed = ((tb.time - sec)*1000000) + ((tb.millitm*1000) - usec); +#else + gettimeofday(&tv, 0); + *elapsed = ((tv.tv_sec - sec)*1000000) + (tv.tv_usec - usec); +#endif + } + + pc_csum &= 0xfff; + cd64_trade_bytes(cd64, 0, &recv); + n64_csum = recv << 8; + cd64_trade_bytes(cd64, 0, &recv); + n64_csum += recv; + n64_csum &= 0xfff; + +/* debug("\nVerifying checksum: pcsum = %d, n64sum = %d\n", pc_csum, n64_csum); */ + if (pc_csum^n64_csum) return -1; + else return 1; +} + +int cd64_bios_send(struct cd64_t *cd64, void *io_id, uint32_t addr, + uint32_t length, int *elapsed, int cmd) { + + unsigned int i; + uint16_t pc_csum = 0; +#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__ + struct timeb tb; +#else + struct timeval tv; +#endif + unsigned long int sec = 0, usec = 0; + uint8_t send; + uint8_t buf[4]; + int valid = 1; + uint8_t send_bogus_csum = 0; + uint8_t tmp; + int32_t pos; + + if (!io_id || !length || length&0x00000003 || addr&0x00000003) return 0; + if (cmd != BIOS_TRANSFER_PI && cmd != BIOS_EXECUTE_PI && cmd != BIOS_TRANSFER_N64 + && !(cd64->protocol == GHEMOR && cmd == GHEMOR_TRANSFER_PROGRAM)) return 0; + + if (cmd == BIOS_TRANSFER_PI || cmd == BIOS_EXECUTE_PI) { + if (addr < 0xa8000000 || addr > 0xbfffffff || + addr+length > 0xbfffffff) valid = 0; + } + else if (cmd == BIOS_TRANSFER_N64) { + if (addr < 0xa0000000 || addr > 0xa03fffff + || addr+length > 0xa03fffff) valid = 0; + + if (addr != BIOS_TEMP_RAM || length > 0x80000) { + /* FIXME: is 0x80000 (512Kbit) really all the CD64 + * BIOS will copy from a mempak? See if it copies + * 1Mbit from a 4x linear card. */ + + /* We are sending somewhere in RAM besides the CD64's + * scratch area. We will send a bogus checksum so the + * CD64 doesn't try to run it or copy it to a mempak. + */ + send_bogus_csum = 1; + } + } + + if (!valid) { + cd64->notice_callback2("Invalid address %lxh for operation.", + (long unsigned int) addr); + return 0; + } + + if (cd64->protocol == GHEMOR && addr != 0xb4000000 && + (cmd == BIOS_TRANSFER_PI || cmd == BIOS_EXECUTE_PI)) { + /* They might try to send to Ghemor in BIOS mode, but + * oh well. Warn them if we know it's Ghemor. */ + cd64->notice_callback("Ignoring address %lxh != 0xb4000000 for Ghemor.", + (long unsigned int) addr); + } + if (cmd == GHEMOR_TRANSFER_PROGRAM) { + cd64->notice_callback("Ghemor ignores this command currently..."); + } + + + /* Try to get in sync with the CD64 + * We use a delay here to give the PPA a chance to power up. */ + send = 0xff; + cd64->xfer(cd64, &send, NULL, 1000); + cd64_bios_sync(cd64); + + if (elapsed != NULL) { +#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__ + ftime(&tb); + sec = tb.time; + usec = tb.millitm*1000; +#else + gettimeofday(&tv, 0); + sec = tv.tv_sec; + usec = tv.tv_usec; +#endif + } + + cd64_send_byte(cd64, (uint8_t) cmd); + cd64_send_dword(cd64, addr); /* Ghemor ignores this */ + cd64_send_dword(cd64, length); + + pos = cd64->tell_callback(io_id); + + i = 0; + while (i < length) { + + if (cd64->abort) return 0; + cd64->read_callback(io_id, &tmp, 1); + pc_csum += tmp; + pc_csum &= 0xfff; + if (!cd64_send_byte(cd64, tmp)) { + if (cd64->protocol == CD64BIOS) { + /* Probably the BIOS was stupid and dropped a + * send as it likes to. Try to recover from + * a convenient point. */ + while (i % 4 != 0) i--; + cd64->seek_callback(io_id, pos+i, SEEK_SET); + cd64->read_callback(io_id, &tmp, 1); + cd64->notice_callback("Trying to recover dropped send, waiting for CD64..."); + cd64_bios_sync(cd64); + cd64_send_byte(cd64, (uint8_t) cmd); + cd64_send_dword(cd64, addr+i); + cd64_send_dword(cd64, length-i); + + if (!cd64_send_byte(cd64, tmp)) { + /* Oh well, at least we tried. */ + return -1; + } + + /* Unfortunately we can only calculate a checksum + * from _this_ point onward. */ + pc_csum = tmp; + + /* Now continue as if nothing ever happened. */ + } + else { + cd64->notice_callback2("Send dropped, unable to recover."); + return 0; + } + } + + if ((i % CD64_BUFFER_SIZE == 0) && cd64->progress_callback) { + cd64->progress_callback(i, length); + } + i++; + } + if (cd64->progress_callback) { + cd64->progress_callback(i, length); + } + + if (elapsed != NULL) { +#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__ + ftime(&tb); + *elapsed = ((tb.time - sec)*1000000) + ((tb.millitm*1000) - usec); +#else + gettimeofday(&tv, 0); + *elapsed = ((tv.tv_sec - sec)*1000000) + (tv.tv_usec - usec); +#endif + } + +/* debug("checksum: 0x%x\n",pc_csum) */ + cd64_send_byte(cd64, (unsigned char)((pc_csum & 0xff00) >> 8)); +/* debug("Sent checksum high byte: 0x%x\n",(unsigned char)pc_csum>>8); */ + cd64_send_byte(cd64, (uint8_t) ((pc_csum & 0xff) + send_bogus_csum)); +/* debug("Sent checksum low byte: 0x%x\n",pc_csum); */ + cd64_grab_byte(cd64, &buf[2]); + cd64_trade_bytes(cd64, 0, &buf[0]); + cd64_trade_bytes(cd64, 0, &buf[1]); + +/* debug("Got: (dummy) 0x%x, 0x%x (%c), 0x%x (%c)",buf[2], buf[0], buf[0], buf[1],buf[1]); */ + if (buf[0]=='O' && buf[1]=='K') return 1; + else if (send_bogus_csum) { + cd64->notice_callback("No way to verify integrity of data in N64 RAM."); + return 1; + } + return -1; +} + +int cd64_ghemor_grab(struct cd64_t *cd64, void *io_id, uint8_t slow, int *elapsed) { + + int ret; + uint8_t tmp; + int sec = 0, usec = 0; +#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__ + struct timeb tb; +#else + struct timeval tv; +#endif + uint32_t len; + uint16_t mycsum = 0; + uint16_t cd64csum; + unsigned int i; + + if (slow) { + cd64->notice_callback2("Ghemor slow receive feature not supported."); + return 0; + } + if (elapsed != NULL) { +#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__ + ftime(&tb); + sec = tb.time; + usec = tb.millitm*1000; +#else + gettimeofday(&tv, 0); + sec = tv.tv_sec; + usec = tv.tv_usec; +#endif + } + + cd64_send_byte(cd64, slow); + i = 0; + while (cd64_grab_byte(cd64, &tmp) && tmp != 1) { + i++; + if (i > 25) { + cd64->notice_callback2("Ghemor was not ready."); + return 0; + } + } + + cd64_grab_dword(cd64, &len); + cd64->notice_callback("Downloading %lu megabits via Ghemor.", + (long unsigned int) len/BYTES_IN_MBIT); + for (i = 0; i < len; i++) { + if (!cd64_grab_byte(cd64, &tmp)) return 0; + if (!cd64->write_callback(io_id, &tmp, 1)) { + cd64->notice_callback2("Error writing to output."); + return 0; + } + mycsum += tmp; + mycsum &= 0xfff; + if ((i % CD64_BUFFER_SIZE == 0) && cd64->progress_callback) { + cd64->progress_callback(i, len); + } + } + if (cd64->progress_callback) { + cd64->progress_callback(i, len); + } + + cd64_grab_byte(cd64, &tmp); + cd64csum = tmp << 8; + cd64_grab_byte(cd64, &tmp); + cd64csum |= tmp; + if (mycsum^cd64csum) ret = -1; + else ret = 1; + if (elapsed != NULL) { +#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__ + ftime(&tb); + *elapsed = ((tb.time - sec)*1000000) + ((tb.millitm*1000) - usec); +#else + gettimeofday(&tv, 0); + *elapsed = ((tv.tv_sec - sec)*1000000) + (tv.tv_usec - usec); +#endif + } + + return ret; +} + +int cd64_ghemor_send(struct cd64_t *cd64, void *io_id, uint32_t length, + int *elapsed) { + + int sec = 0, usec = 0; + uint16_t mycsum = 0; + unsigned int i; +#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__ + struct timeb tb; +#else + struct timeval tv; +#endif + uint8_t tmp; + + if (elapsed != NULL) { +#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__ + ftime(&tb); + sec = tb.time; + usec = tb.millitm*1000; +#else + gettimeofday(&tv, 0); + sec = tv.tv_sec; + usec = tv.tv_usec; +#endif + } + + cd64_send_byte(cd64, 0); /* No slow mode for sends */ + i = 0; + while (cd64_grab_byte(cd64, &tmp) && tmp != 1) { + i++; + if (i > 25) { + cd64->notice_callback2("Ghemor was not ready."); + return 0; + } + } + + cd64_send_dword(cd64, length); + for (i = 0; i < length; i++) { + if (!cd64->read_callback(io_id, &tmp, 1)) { + cd64->notice_callback2("Error reading from input."); + return 0; + } + if (!cd64_send_byte(cd64, tmp)) return 0; + mycsum += tmp; + mycsum &= 0xfff; + if ((i % CD64_BUFFER_SIZE == 0) && cd64->progress_callback) { + cd64->progress_callback(i, length); + } + } + if (cd64->progress_callback) { + cd64->progress_callback(i, length); + } + cd64_send_byte(cd64, (uint8_t)((mycsum << 8) & 0xff)); + cd64_send_byte(cd64, (uint8_t)(mycsum & 0xff)); + if (elapsed != NULL) { +#if (defined _WIN32 && !defined __CYGWIN__) || defined __MSDOS__ + ftime(&tb); + *elapsed = ((tb.time - sec)*1000000) + ((tb.millitm*1000) - usec); +#else + gettimeofday(&tv, 0); + *elapsed = ((tv.tv_sec - sec)*1000000) + (tv.tv_usec - usec); +#endif + } + + return 1; +} + + +/* Generic API functions */ + +int cd64_upload_dram(struct cd64_t *cd64, FILE *source, uint32_t length, + int *elapsed, int exec) { + + if (cd64->protocol == CD64BIOS || cd64->protocol == GHEMOR) { + int cmd; + if (exec == 1) cmd = BIOS_EXECUTE_PI; + else cmd = BIOS_TRANSFER_PI; + + if (cd64->protocol == CD64BIOS && length == 0) { + cd64->notice_callback2("CD64 BIOS needs a file length."); + return 0; + } + if (cd64->protocol == CD64BIOS) { + cd64->notice_callback("Choose CD64 Tools->Pro Comms Link."); + } + + return cd64_bios_send(cd64, source, 0xb4000000, length, elapsed, cmd); + } + cd64->notice_callback2("Operation not supported by protocol."); + return 0; +} + +int cd64_upload_bootemu(struct cd64_t *cd64, FILE *infile, uint32_t length, int *elapsed) { + + if (cd64->protocol == CD64BIOS) { + + if (cd64->protocol == CD64BIOS && length == 0) { + cd64->notice_callback2("CD64 BIOS needs a file length.\n"); + return 0; + } + + cd64->notice_callback("Choose CD64 Tools->Pro Comms Link."); + return cd64_bios_send(cd64, infile, BIOS_TEMP_RAM, length, elapsed, + BIOS_TRANSFER_N64); + } + else if (cd64->protocol == GHEMOR) { + cd64_bios_sync(cd64); + cd64_send_byte(cd64, GHEMOR_EXECUTE_BOOTEMU); + return cd64_ghemor_send(cd64, infile, length, elapsed); + } + cd64->notice_callback2("Operation not supported by protocol."); + return 0; +} + +int cd64_upload_ram(struct cd64_t *cd64, FILE *infile, uint32_t length, + int *elapsed, uint32_t address) { + + if (cd64->protocol == CD64BIOS) { + cd64->notice_callback("Choose CD64 Tools->Pro Comms Link."); + return cd64_bios_send(cd64, infile, address, length, + elapsed, BIOS_TRANSFER_N64); + } + cd64->notice_callback2("Operation not supported by protocol."); + return 0; +} + +int cd64_upload_mempak(struct cd64_t *cd64, FILE *infile, int8_t which) { + + int32_t len; + int32_t pos = cd64->tell_callback(infile); + cd64->seek_callback(infile, 0, SEEK_END); + len = cd64->tell_callback(infile); + cd64->seek_callback(infile, pos, SEEK_SET); + if (len != CONTROLLER_MEMPAK_LENGTH) { + cd64->notice_callback("Mempack file must be %d bytes, not %d.", + CONTROLLER_MEMPAK_LENGTH, len); + } + + if (cd64->protocol == CD64BIOS) { + if (which != -1) { + cd64->notice_callback2("CD64 BIOS doesn't let mempak index be chosen."); + return 0; + } + cd64->notice_callback("Choose Memory Manager->Up/Download Pak."); + return cd64_bios_send(cd64, infile, BIOS_TEMP_RAM, CONTROLLER_MEMPAK_LENGTH, + NULL, BIOS_TRANSFER_N64); + } + else if (cd64->protocol == GHEMOR) { + cd64_bios_sync(cd64); + cd64_send_byte(cd64, GHEMOR_RESTORE_MEMPAK); + cd64_send_byte(cd64, which); + return cd64_ghemor_send(cd64, infile, CONTROLLER_MEMPAK_LENGTH, NULL); + } + cd64->notice_callback2("Operation not supported by protocol."); + return 0; +} + +int cd64_upload_sram(struct cd64_t *cd64, FILE *infile) { + + if (cd64->protocol == CD64BIOS) { + cd64->notice_callback("Choose CD64 Tools->Pro Comms Link."); + return cd64_bios_send(cd64, infile, 0xa8000000, CART_SRAM_LENGTH, + NULL, BIOS_TRANSFER_PI); + } + else if (cd64->protocol == GHEMOR) { + cd64_bios_sync(cd64); + cd64_send_byte(cd64, GHEMOR_RESTORE_SRAM); + return cd64_ghemor_send(cd64, infile, CART_SRAM_LENGTH, NULL); + } + cd64->notice_callback2("Operation not supported by protocol."); + return 0; +} + +int cd64_upload_flashram(struct cd64_t *cd64, FILE *infile) { + + /* Urm, we need to figure out if this really works. Probably, CTR + * needs to release a new Ghemor version. Maybe it works with + * CD64 BIOS but probably not. */ + + if (cd64->protocol == CD64BIOS) { + cd64->notice_callback("Choose CD64 Tools->Pro Comms Link."); + return cd64_bios_send(cd64, infile, 0xa8000000, CART_FLASHRAM_LENGTH, + NULL, BIOS_TRANSFER_PI); + } + else if (cd64->protocol == GHEMOR) { + cd64_bios_sync(cd64); + cd64_send_byte(cd64, GHEMOR_RESTORE_FLASHRAM); + return cd64_ghemor_send(cd64, infile, CART_FLASHRAM_LENGTH, NULL); + } + cd64->notice_callback2("Operation not supported by protocol."); + return 0; +} + +int cd64_upload_eeprom(struct cd64_t *cd64, FILE *infile) { + + /* Check the size of the EEPROM data first */ + + int32_t origpos = cd64->tell_callback(infile); + int32_t length; + + if (cd64->protocol == CD64BIOS) { + cd64->notice_callback2("CD64 BIOS can only transfer EEPROM through BRAM Manager."); + return 0; + } + + cd64->seek_callback(infile, 0, SEEK_END); + length = cd64->tell_callback(infile); + cd64->seek_callback(infile, origpos, SEEK_SET); + + if (length != CART_EEPROM_LENGTH && length != CART_2XEEPROM_LENGTH) { + cd64->notice_callback2("Wrong length of EEPROM data: %d bytes", (int) length); + return 0; + } + else if (cd64->protocol == GHEMOR) { + cd64_bios_sync(cd64); + cd64_send_byte(cd64, GHEMOR_RESTORE_EEPROM); + return cd64_ghemor_send(cd64, infile, length, NULL); + } + cd64->notice_callback2("Operation not supported by protocol."); + return 0; +} + +static int cd64_write_mem(void *dummy, void *buffer, uint32_t size) { + + (void) dummy; + memcpy (cd64_tmp_buf + cd64_tmp_buf_offset, buffer, size); + cd64_tmp_buf_offset += size; + return size; +} + +int cd64_download_header(struct cd64_t *cd64, n64header_t *head, uint32_t location) { + + if (cd64->protocol == CD64BIOS) { + int size = sizeof(n64header_t); + int ret; + int (*org_write_cb)(void *, void *, uint32_t) = cd64->write_callback; + + while (size % 4 != 0) size++; + if (!head) return 0; + cd64_tmp_buf = (uint8_t *) head; + cd64_tmp_buf_offset = 0; + cd64->write_callback = cd64_write_mem; + ret = cd64_bios_grab(cd64, (void *) -1, location, size, NULL); /* -1 is just a random (non-zero) value */ + cd64->write_callback = org_write_cb; /* restore original callback */ + return 1; + } + cd64->notice_callback2("Operation not supported by protocol."); + return 0; +} + +int cd64_download_cart(struct cd64_t *cd64, FILE *outfile, uint32_t length, + int *elapsed) { + + if (cd64->protocol == CD64BIOS) { + int ret; + unsigned int i; + int32_t curpos = 0; + int32_t origpos = cd64->tell_callback(outfile); + if (length == 0) { + cd64->notice_callback2("CD64 BIOS needs a file length."); + return 0; + } + cd64->notice_callback("Choose CD64 Tools->Pro Comms Link."); + + ret = cd64_bios_grab(cd64, outfile, 0xb2000000, length, elapsed); + /* Scan through the file at 8MBit intervals to + * see if we overdumped. If we did, truncate the + * file. */ + i = 0; + cd64->seek_callback(outfile, origpos, SEEK_SET); + cd64->notice_callback("Checking for overdump..."); + while (i < length) { + int j = 0; + int overdump = 1; + uint8_t buf[4]; + + curpos = cd64->tell_callback(outfile); + + while(i+j < length) { + cd64->read_callback(outfile, &buf, 4); + + /* To elaborate on what we are checking here: + * When the CD64 accesses an address which is not + * decoded, in each 32-bit word is the lower 16 bits + * of the address of that 32-bit word, repeated twice. + * The pattern therefore looks like: + * 00 00 00 00 00 04 00 04 00 08 00 08 00 0c 00 0c + * and continues on like that. This pattern is what + * we are looking for here. It is possible, but + * extremely unlikely, that this pattern appears in a + * actual game and begins on a 8Mbit boundary too. */ + + if ( + ((uint8_t*)buf)[0] != ((j >> 8) & 0xff) + || ((uint8_t*)buf)[1] != (j & 0xff) + || ((uint8_t*)buf)[2] != ((j >> 8) & 0xff) + || ((uint8_t*)buf)[3] != (j & 0xff) + ) { + overdump = 0; + break; + } + else { + j+=4; + } + } + + if (overdump) { + break; + } + i += 0x100000; + cd64->seek_callback(outfile, curpos+0x100000, SEEK_SET); + } + + if (i < length) { + cd64->notice_callback("File apparently overdumped."); +#if (!defined _WIN32 || defined __CYGWIN__) + /* Don't call ftruncate() if the user installed a callback, because + * outfile may not be a real FILE *. */ + if (cd64->read_callback == cd64_read) { + cd64->notice_callback("Truncating to %dMbits.", i/BYTES_IN_MBIT); + ftruncate(fileno(outfile), curpos+i); + } +#endif + } + return ret; + } + else if (cd64->protocol == GHEMOR) { + cd64_bios_sync(cd64); + cd64_send_byte(cd64, GHEMOR_DUMP_CART); + return cd64_ghemor_grab(cd64, outfile, 0, elapsed); + } + cd64->notice_callback2("Operation not supported by protocol."); + return 0; +} + +int cd64_download_dram(struct cd64_t *cd64, FILE *outfile, uint32_t start, + uint32_t end, int *elapsed) { + + if (cd64->protocol == CD64BIOS) { + cd64->notice_callback("Choose CD64 Tools->Pro Comms Link."); + return cd64_bios_grab(cd64, outfile, 0xb4000000, end-start, elapsed); + } + cd64->notice_callback2("Operation not supported by protocol."); + return 0; +} + +int cd64_download_sram(struct cd64_t *cd64, FILE *outfile) { + + if (cd64->protocol == CD64BIOS) { + cd64->notice_callback("Choose CD64 Tools->Pro Comms Link."); + return cd64_bios_grab(cd64, outfile, CART_SRAM_ADDR, CART_SRAM_LENGTH, NULL); + } + else if (cd64->protocol == GHEMOR) { + cd64_bios_sync(cd64); + cd64_send_byte(cd64, GHEMOR_DUMP_SRAM); + return cd64_ghemor_grab(cd64, outfile, 0, NULL); + } + cd64->notice_callback2("Operation not supported by protocol."); + return 0; +} + +int cd64_download_flashram(struct cd64_t *cd64, FILE *outfile) { + + /* We might be able to support CD64 BIOS here. Needs testing. */ + if (cd64->protocol == GHEMOR) { + cd64_bios_sync(cd64); + cd64_send_byte(cd64, GHEMOR_DUMP_FLASH); + return cd64_ghemor_grab(cd64, outfile, 0, NULL); + } + cd64->notice_callback2("Operation not supported by protocol."); + return 0; +} + +int cd64_download_eeprom(struct cd64_t *cd64, FILE *outfile) { + + if (cd64->protocol == GHEMOR) { + cd64_bios_sync(cd64); + cd64_send_byte(cd64, GHEMOR_DUMP_EEPROM); + return cd64_ghemor_grab(cd64, outfile, 0, NULL); + } + cd64->notice_callback2("Operation not supported by protocol."); + return 0; +} + +int cd64_download_mempak(struct cd64_t *cd64, FILE *outfile, int8_t which) { + + if (cd64->protocol == CD64BIOS) { + if (which != -1) { + cd64->notice_callback2("CD64 BIOS doesn't let mempak index be chosen."); + return 0; + } + cd64->notice_callback("Choose Memory Manager->Up/Download Pak."); + return cd64_bios_grab(cd64, outfile, BIOS_TEMP_RAM, CONTROLLER_MEMPAK_LENGTH, NULL); + } + else if (cd64->protocol == GHEMOR) { + cd64_bios_sync(cd64); + cd64_send_byte(cd64, GHEMOR_DUMP_MEMPAK); + cd64_send_byte(cd64, which); + return cd64_ghemor_grab(cd64, outfile, 0, NULL); + } + cd64->notice_callback2("Operation not supported by protocol."); + return 0; +} + +static int cd64_read_mem(void *dummy, void *buffer, uint32_t size) { + + (void) dummy; + memcpy (buffer, cd64_tmp_buf + cd64_tmp_buf_offset, size); + cd64_tmp_buf_offset += size; + return size; +} + +static int32_t cd64_tell_mem(void *dummy) { + + (void) dummy; + return cd64_tmp_buf_offset; +} + +static int cd64_seek_mem(void *dummy, int32_t offset, int whence) { + + (void) dummy; + (void) whence; /* only called with SEEK_SET */ + cd64_tmp_buf_offset = offset; + return 0; +} + +int cd64_run_dram(struct cd64_t *cd64) { + + if (cd64->protocol == GHEMOR) { + cd64_bios_sync(cd64); + cd64_send_byte(cd64, GHEMOR_RESET_DRAM); + return 1; + } + else if (cd64->protocol == CD64BIOS) { + /* Heh. Write some dummy bytes to the cart area. We + * can't just send a zero length because the CD64 + * BIOS gives "File length error". */ + uint8_t dummy[4] = { 0, 0, 0, 0 }; + int ret; + int (*org_read_cb)(void *, void *, uint32_t) = cd64->read_callback; + int32_t (*org_tell_cb)(void *) = cd64->tell_callback; + int (*org_seek_cb)(void *, int32_t, int) = cd64->seek_callback; + + cd64->notice_callback("Choose CD64 Tools->Pro Comms Link."); + cd64_tmp_buf = dummy; + cd64_tmp_buf_offset = 0; + cd64->read_callback = cd64_read_mem; + cd64->tell_callback = cd64_tell_mem; + cd64->seek_callback = cd64_seek_mem; + ret = cd64_bios_send(cd64, (void *) -1, 0xb2000000, 4, NULL, BIOS_EXECUTE_PI); /* -1 is just a random (non-zero) value */ + cd64->read_callback = org_read_cb; /* restore original callbacks */ + cd64->tell_callback = org_tell_cb; + cd64->seek_callback = org_seek_cb; + return ret; + } + cd64->notice_callback2("Operation not supported by protocol."); + return 0; +} + +int cd64_run_cart(struct cd64_t *cd64) { + + if (cd64->protocol == GHEMOR) { + cd64_bios_sync(cd64); + cd64_send_byte(cd64, GHEMOR_RESET_CART); + return 1; + } + cd64->notice_callback2("Operation not supported by protocol."); + return 0; +} diff --git a/ucon64/2.0/src/backup/libcd64/ultra64/cartmem.h b/ucon64/2.0/src/backup/libcd64/ultra64/cartmem.h new file mode 100644 index 0000000..ebb07fa --- /dev/null +++ b/ucon64/2.0/src/backup/libcd64/ultra64/cartmem.h @@ -0,0 +1,13 @@ +#ifndef __CARTMEM_H__ +#define __CARTMEM_H__ + +#define CART_EEPROM_LENGTH 0x200 /* 512 bytes */ +#define CART_2XEEPROM_LENGTH 0x800 /* 2048 bytes */ +#define CART_SRAM_LENGTH 0x8000 /* 32KB */ +#define CART_FLASHRAM_LENGTH 0x20000 /* 128KB */ +#define CONTROLLER_MEMPAK_LENGTH 0x8000 /* 32KB */ + +#define CART_SRAM_ADDR 0xa8000000 +#define CART_FLASHRAM_ADDR CART_SRAM_ADDR + +#endif diff --git a/ucon64/2.0/src/backup/libcd64/ultra64/host/cartinfo.h b/ucon64/2.0/src/backup/libcd64/ultra64/host/cartinfo.h new file mode 100644 index 0000000..c7d00c0 --- /dev/null +++ b/ucon64/2.0/src/backup/libcd64/ultra64/host/cartinfo.h @@ -0,0 +1,8 @@ +#ifndef __ULTRA64__HOST__CARTINFO_H__ +#define __ULTRA64__HOST__CARTINFO_H__ + +#include + +void ultra64_header_info(n64header_t *carthead); + +#endif diff --git a/ucon64/2.0/src/backup/libcd64/ultra64/host/cd64lib.h b/ucon64/2.0/src/backup/libcd64/ultra64/host/cd64lib.h new file mode 100644 index 0000000..ee996fc --- /dev/null +++ b/ucon64/2.0/src/backup/libcd64/ultra64/host/cd64lib.h @@ -0,0 +1,189 @@ +#ifndef __CD64LIB_H__ +#define __CD64LIB_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define CD64_BUFFER_SIZE 32768 + +/* This is the only public header file for cd64lib. */ + +#if __STDC_VERSION >= 19990L +#include +#else +#if !(defined __MSDOS__ || defined _MSC_VER) +#include +#else +#ifndef OWN_INTTYPES +#define OWN_INTTYPES /* signal that these are defined */ +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +#ifndef _MSC_VER /* _WIN32 */ +typedef unsigned long long int uint64_t; +#else +typedef unsigned __int64 uint64_t; +#endif +typedef signed char int8_t; +typedef signed short int int16_t; +typedef signed int int32_t; +#ifndef _MSC_VER /* _WIN32 */ +typedef signed long long int int64_t; +#else +typedef signed __int64 int64_t; +#endif +#endif /* OWN_INTTYPES */ +#endif /* __MSDOS__ || _MSC_VER */ +#endif /* STDC_VERSION */ + +#include /* FILE, FILENAME_MAX */ +#include + +typedef enum { + CD64BIOS = 0, + GHEMOR = 1, + ULTRALINK = 2 +} protocol_t; + +typedef enum { + LIBIEEE1284 = 1, + PPDEV = 2, + PORTDEV = 3, + RAWIO = 4 +} method_t; + +/* When using this structure, be sure to calloc it or otherwise set it to + * zero before setting values or calling library functions. */ + +struct cd64_t { + int using_ppa; + protocol_t protocol; + + struct parport *ppdev; /* libieee1284 */ + int ppdevfd; /* ppdev */ + int portdevfd; /* /dev/port */ + + /* If using inb/outb or /dev/port, this is the I/O address + * Otherwise it is the parport* number */ + int port; + + /* Directory with io.dll or dlportio.dll. Used by the Windows ports. */ + char io_driver_dir[FILENAME_MAX]; + + /* A flag that can be set/read to determine whether + * the current operation should be canceled. */ + int abort; + + int (*devopen)(struct cd64_t *cd64); + int (*xfer)(struct cd64_t *cd64, uint8_t *write, uint8_t *read, int delayms); + int (*devclose)(struct cd64_t *cd64); + + /* Progress callback is responsible for printing header info if the + * user wants it */ + + void (*progress_callback)(uint32_t curbyte, uint32_t totalbytes); + int (*notice_callback)(const char *format, ...); + int (*notice_callback2)(const char *format, ...); + + /* Callbacks for read, write and seek operations. By default they point to + * callbacks in the library which just call fread(), fwrite(), ftell() and + * fseek(). You can change them so that the library doesn't read from or + * write to a FILE * (io_id). For example, a client can install its own + * callback to make it possible to read from .zip files. */ + int (*read_callback)(void *io_id, void *buffer, uint32_t size); + int (*write_callback)(void *io_id, void *buffer, uint32_t size); + int32_t (*tell_callback)(void *io_id); + int (*seek_callback)(void *io_id, int32_t offset, int whence); +}; + +/* This function must be called and return successful before any of the + * other functions may be used. */ + +int cd64_create(struct cd64_t *cd64, method_t method, + uint16_t port, protocol_t protocol, int ppa); + +/* The following five functions are wrappers above the I/O abstraction. + * Use them to write code that works regardless of the underlying + * transport. */ + +int cd64_send_byte(struct cd64_t *cd64, uint8_t what); +int cd64_send_dword(struct cd64_t *cd64, uint32_t what); +int cd64_grab_byte(struct cd64_t *cd64, uint8_t *val); +int cd64_grab_dword(struct cd64_t *cd64, uint32_t *val); +int cd64_trade_bytes(struct cd64_t *cd64, uint8_t give, uint8_t *recv); + +/* Generic protocol handlers */ + +int cd64_bios_grab(struct cd64_t *cd64, void *io_id, uint32_t addr, uint32_t length, + int *elapsed); +int cd64_bios_send(struct cd64_t *cd64, void *io_id, uint32_t addr, + uint32_t length, int *elapsed, int cmd); + +int cd64_ghemor_grab(struct cd64_t *cd64, void *io_id, uint8_t slow, int *elapsed); +int cd64_ghemor_send(struct cd64_t *cd64, void *io_id, uint32_t length, + int *elapsed); + +/* Functions for sending files to CD64 */ +int cd64_upload_dram(struct cd64_t *cd64, FILE *infile, uint32_t length, + int *elapsed, int exec); +int cd64_upload_ram(struct cd64_t *cd64, FILE *infile, uint32_t length, + int *elapsed, uint32_t address); + +int cd64_upload_bootemu(struct cd64_t *cd64, FILE *infile, uint32_t length, int *elapsed); + +int cd64_upload_sram(struct cd64_t *cd64, FILE *infile); +int cd64_upload_flashram(struct cd64_t *cd64, FILE *infile); +int cd64_upload_eeprom(struct cd64_t *cd64, FILE *infile); +int cd64_upload_mempak(struct cd64_t *cd64, FILE *infile, int8_t which); + +/* Functions for receiving files from CD64 */ +int cd64_download_cart(struct cd64_t *cd64, FILE *outfile, uint32_t length, + int *elapsed); +int cd64_download_dram(struct cd64_t *cd64, FILE *outfile, uint32_t start, + uint32_t end, int *elapsed); +int cd64_download_ram(struct cd64_t *cd64, FILE *outfile, uint32_t length, + int *elapsed, uint32_t address); + +int cd64_download_sram(struct cd64_t *cd64, FILE *outfile); +int cd64_download_flashram(struct cd64_t *cd64, FILE *outfile); +int cd64_download_eeprom(struct cd64_t *cd64, FILE *outfile); +int cd64_download_mempak(struct cd64_t *cd64, FILE *outfile, int8_t which); + +/* Remote control functions */ +int cd64_run_dram(struct cd64_t *cd64); +int cd64_run_cart(struct cd64_t *cd64); + +/* This function simply gets the header from the cart and can be displayed + * using ultra64_header_info() */ + +int cd64_download_header(struct cd64_t *cd64, n64header_t *head, uint32_t location); + +#ifdef __cplusplus +} +#endif + +#define BIOS_TEMP_RAM 0xa0300000 + +#define BIOS_DUMP_N64 'D' +#define BIOS_TRANSFER_N64 'B' + +#define BIOS_DUMP_PI 'G' +#define BIOS_TRANSFER_PI 'T' +#define BIOS_EXECUTE_PI 'X' + +#define GHEMOR_RESTORE_MEMPAK 1 +#define GHEMOR_RESTORE_EEPROM 2 +#define GHEMOR_RESTORE_SRAM 3 +#define GHEMOR_RESTORE_FLASHRAM 4 +#define GHEMOR_EXECUTE_BOOTEMU 5 +#define GHEMOR_TRANSFER_PROGRAM 6 +#define GHEMOR_DUMP_CART 7 +#define GHEMOR_DUMP_MEMPAK 8 +#define GHEMOR_DUMP_EEPROM 9 +#define GHEMOR_DUMP_SRAM 10 +#define GHEMOR_DUMP_FLASH 11 +#define GHEMOR_RESET_DRAM 12 +#define GHEMOR_RESET_CART 13 + +#endif diff --git a/ucon64/2.0/src/backup/libcd64/ultra64/rom.h b/ucon64/2.0/src/backup/libcd64/ultra64/rom.h new file mode 100644 index 0000000..1eca488 --- /dev/null +++ b/ucon64/2.0/src/backup/libcd64/ultra64/rom.h @@ -0,0 +1,60 @@ + +#ifndef __ROM_H__ +#define __ROM_H__ + +/* Based on Daedalus header */ + +#define N64HEADER_SIZE 0x40 +#define BYTES_IN_MBIT 0x20000 + +#define SwapEndian(x) \ + ((x >> 24)&0x000000FF) \ + | ((x >> 8 )&0x0000FF00) \ + | ((x << 8 )&0x00FF0000) \ + | ((x << 24)&0xFF000000) + +typedef enum { + UNKNOWN = 0, + EEP4K = 1, + EEP16K = 2, + SRAM = 3, + FLASHRAM = 4 +} savetype_t; + +typedef struct { /* From Daedalus */ + unsigned char x1; /* initial PI_BSB_DOM1_LAT_REG value */ + unsigned char x2; /* initial PI_BSB_DOM1_PGS_REG value */ + unsigned char x3; /* initial PI_BSB_DOM1_PWD_REG value */ + unsigned char x4; /* initial PI_BSB_DOM1_RLS_REG value */ + + unsigned long int ClockRate; + unsigned long int BootAddress; + unsigned long int Release; + unsigned long int CRC1; + unsigned long int CRC2; + unsigned long int Unknown0; + unsigned long int Unknown1; + char Name[20]; + unsigned long int Unknown2; + unsigned short int Unknown3; + unsigned char Unknown4; + unsigned char Manufacturer; + unsigned short int CartID; + char CountryID; + unsigned char Unknown5; +} n64header_t; + +typedef enum { + OS_TV_NTSC = 0, + OS_TV_PAL, + OS_TV_MPAL +} tv_type_t; + +typedef struct +{ + char nCountryID; + char szName[15]; + unsigned long int nTvType; +} CountryIDInfo_t; + +#endif diff --git a/ucon64/2.0/src/backup/lynxit.c b/ucon64/2.0/src/backup/lynxit.c new file mode 100644 index 0000000..a2aabb7 --- /dev/null +++ b/ucon64/2.0/src/backup/lynxit.c @@ -0,0 +1,1055 @@ +/* +lynxit.c - lynxit support for uCON64 + +Copyright (c) 1997 - ???? K. Wilkins +Copyright (c) 2002 NoisyB +Copyright (c) 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "misc/misc.h" +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "console/lynx.h" +#include "lynxit.h" +#include "misc/parallel.h" + + +const st_getopt2_t lynxit_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Lynxit (Lynx cartridge backup board)"/*"1997 K.Wilkins (selfmade)"*/, + NULL + }, +#ifdef USE_PARALLEL + { + "xlit", 0, 0, UCON64_XLIT, + NULL, "receive ROM from Lynxit interface; " OPTION_LONG_S "port=PORT", +// " receives automatically when ROM does not exist\n" + &ucon64_wf[WF_OBJ_LYNX_STOP_NO_ROM] + }, +#endif + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +#ifdef USE_PARALLEL + +#define MAGIC_STRING "LYNX" +#define FILE_FORMAT_VERSION 0x0001 + + +// +// Some basic cartidge definitions +// + +#define MAX_PAGE_SIZE 0x1000 // Must be 2x largest page + +#define CART_PAGE_64K 0x100 // 256 Bytes per page +#define CART_PAGE_128K 0x200 // 512 Bytes per page +#define CART_PAGE_256K 0x400 // 1024 Bytes per page +#define CART_PAGE_512K 0x800 // 2048 Bytes per page + +#define BANK0 0 +#define BANK1 1 + + +// +// DEFINE CENTRONICS PORT MASKS & PORTS +// + +#define PORT_BASE 0x80 // Keep the power on 8-) +#define CTRL_STB 0x01 +#define DATA_OUT 0x02 +#define DATA_CLK 0x04 +#define DATA_STB 0x08 +#define DATA_OE 0x10 +#define DATA_LOAD 0x20 +#define PAGE_STB 0x40 + +unsigned int printer_port = 1, print_data = 0, print_ctrl = 0, print_stat = 0; +char cartname[32], manufname[16]; + +// +// CONTROL REGISTER DEFINITIONS +// + + +#define CTRL_BANK0B 0x01 +#define CTRL_BANK1B 0x02 +#define CTRL_WR_EN 0x04 +#define CTRL_CTR_CLKB 0x40 +#define CTRL_CTR_RST 0x80 + +#define CTRL_INACTIVE (CTRL_BANK0B|CTRL_BANK1B|CTRL_CTR_CLKB) + + +// Make cart strobes inactive & counter clock & reset inactive + +unsigned char control_register = CTRL_INACTIVE; +static void lynxit_write_control (unsigned char data); + + +// +// Global control vars +// + +static int debug = FALSE, quiet = FALSE, verify = TRUE; + + +#define MESSAGE(body) printf body + +#define INPUT(port) inportb((unsigned short) (port)) +#define OUTPUT(port, data) outportb((unsigned short) (port), (unsigned char) (data)) + + +#if 0 +void +usage (void) +{ + MESSAGE (("\nUsage: lynxit [-pX] [-d] [-q] [cartname] [manuf]\n")); + MESSAGE (("\n")); + MESSAGE ((" Commands = read/write/verify/test\n")); + MESSAGE ((" -pX = Use printer port LPTX: (Default LPT1:)\n")); + MESSAGE ((" -d = Debug mode enable\n")); + MESSAGE ((" -q = Quiet mode\n")); + MESSAGE ((" -n = Don't verify read/write operations\n")); + MESSAGE ((" -h = Print this text\n")); +} +#endif + + +int +ptr_port_init (unsigned int port) +{ +#if 0 + if (port < 1 && port > 4) + return FALSE; + + print_data = *(unsigned int far *) MK_FP (0x0040, 6 + (2 * port)); + + if (!print_data) + return FALSE; +#else + (void) port; // warning remover +#endif + print_stat = print_data + 1; + print_ctrl = print_data + 2; + +#if 0 + DEBUG (("\nPrinter port initialised OK: LPT%d\n", port)); + DEBUG (("Data I/O 0x%04x\n", print_data)); + DEBUG (("Status I/O 0x%04x\n", print_stat)); + DEBUG (("Control I/O 0x%04x\n\n", print_ctrl)); +#endif + + lynxit_write_control (control_register); + + return TRUE; +} + + +void +lynxit_shift_out_byte (unsigned char data) +{ + unsigned int loop; + unsigned char outbyte, dbgdata; + + dbgdata = data; + + OUTPUT (print_data, PORT_BASE); // Set inactive + + for (loop = 0; loop < 8; loop++) + { + outbyte = PORT_BASE; + outbyte |= (data & 0x80) ? DATA_OUT : 0; + OUTPUT (print_data, outbyte); // Output data; clock low + data = data << 1; + outbyte |= DATA_CLK; + OUTPUT (print_data, outbyte); // clock high + } + + OUTPUT (print_data, PORT_BASE); // Leave outputs low + +#if 0 + DEBUG (("lynxit_shift_out_byte() - Wrote %02x\n", dbgdata)); +#endif +} + + +unsigned char +lynxit_shift_in_byte (void) +{ + unsigned int loop; + unsigned char data = 0; + + OUTPUT (print_data, PORT_BASE); // Set inactive + + for (loop = 0; loop < 8; loop++) + { + data |= (((INPUT (print_stat)) & 0x80) ? 0 : 1) << (7 - loop); +#if 0 + DEBUG (("Status port returned %02x\n", INPUT (print_stat))); +#endif + OUTPUT (print_data, PORT_BASE | DATA_CLK); // clock high + OUTPUT (print_data, PORT_BASE); // clock low + } + +#if 0 + DEBUG (("lynxit_shift_in_byte() - Read %02x\n", data)); +#endif + return (data); +} + + +void +lynxit_write_control (unsigned char data) +{ +#if 0 + DEBUG (("lynxit_write_control() - Set to %02x\n", data)); +#endif + lynxit_shift_out_byte (data); + + OUTPUT (print_data, PORT_BASE); // clock low; strobe low + OUTPUT (print_data, PORT_BASE | CTRL_STB); // clock low; strobe high + OUTPUT (print_data, PORT_BASE); // clock low; strobe low + + control_register = data; +} + + +void +lynxit_write_page (unsigned char page) +{ +#if 0 + DEBUG (("lynxit_write_page() - Set to %02x\n", page)); +#endif + lynxit_shift_out_byte (page); + lynxit_shift_out_byte (0); + + OUTPUT (print_data, PORT_BASE); // clock low; strobe low + OUTPUT (print_data, PORT_BASE | PAGE_STB); // clock low; strobe high + OUTPUT (print_data, PORT_BASE); // clock low; strobe low +} + + +void +lynxit_counter_reset (void) +{ + lynxit_write_control ((unsigned char) (control_register | CTRL_CTR_RST)); + lynxit_write_control ((unsigned char) (control_register & (CTRL_CTR_RST ^ 0xff))); +} + + +void +lynxit_counter_increment (void) +{ + lynxit_write_control ((unsigned char) (control_register & (CTRL_CTR_CLKB ^ 0xff))); + lynxit_write_control ((unsigned char) (control_register | CTRL_CTR_CLKB)); +} + + +unsigned char +cart_read_byte (unsigned int cart) +{ + unsigned char data; + + // Clear relevant cart strobe to activate read + + if (cart == BANK0) + { + lynxit_write_control ((unsigned char) (control_register & (0xff ^ CTRL_BANK0B))); + } + else + { + lynxit_write_control ((unsigned char) (control_register & (0xff ^ CTRL_BANK1B))); + } + + // Clock byte into shift register with load + // + // Note 500ns read cycle for ROM == 5x125ns out cycles + + + OUTPUT (print_data, PORT_BASE); // clock low + OUTPUT (print_data, PORT_BASE); // clock low + OUTPUT (print_data, PORT_BASE); // clock low + OUTPUT (print_data, PORT_BASE); // clock low + OUTPUT (print_data, PORT_BASE | DATA_LOAD); // Parallel load + OUTPUT (print_data, PORT_BASE | DATA_LOAD); // Parallel load + OUTPUT (print_data, PORT_BASE); // Clear load + + // This must be done before strobe is cleared as data will be + // destoryed bye setting control reg + + data = lynxit_shift_in_byte (); + + // Clear the cartridge strobe + + if (cart == BANK0) + { + lynxit_write_control ((unsigned char) (control_register | CTRL_BANK0B)); + } + else + { + lynxit_write_control ((unsigned char) (control_register | CTRL_BANK1B)); + } + +#if 0 + DEBUG (("cart_read_byte() - Returning %02x\n", data)); +#endif + +// MESSAGE(("%c",data)); + + return (data); +} + + +void +cart_write_byte (unsigned int cart, unsigned char data) +{ +#if 0 + DEBUG (("cart_write_byte() - Set to %02x\n", data)); +#endif + + // Shift data to correct position + + lynxit_shift_out_byte (data); + lynxit_shift_out_byte (0); + lynxit_shift_out_byte (0); + + // Strobe byte to be written into the data register + + OUTPUT (print_data, PORT_BASE); // clock low; strobe low + OUTPUT (print_data, PORT_BASE | DATA_STB); // clock low; strobe high + OUTPUT (print_data, PORT_BASE); // clock low; strobe low + + // Assert write enable (active low) + + lynxit_write_control ((unsigned char) (control_register & (0xff ^ CTRL_WR_EN))); + + // Assert output enable + + OUTPUT (print_data, PORT_BASE | DATA_OE); + + // Assert cartridge strobe LOW to write then HIGH + + if (cart == BANK0) + { + lynxit_write_control ((unsigned char) (control_register & (0xff ^ CTRL_BANK0B))); + lynxit_write_control ((unsigned char) (control_register | CTRL_BANK0B)); + } + else + { + lynxit_write_control ((unsigned char) (control_register & (0xff ^ CTRL_BANK1B))); + lynxit_write_control ((unsigned char) (control_register | CTRL_BANK1B)); + } + + // Clear output enable + + OUTPUT (print_data, PORT_BASE); + + // Clear write enable + + lynxit_write_control ((unsigned char) (control_register | CTRL_WR_EN)); + +} + + +void +cart_read_page (unsigned int cart, unsigned int page_number, + unsigned int page_size, unsigned char *page_ptr) +{ + unsigned int loop; + + lynxit_write_page ((unsigned char) page_number); + lynxit_counter_reset (); + + for (loop = 0; loop < page_size; loop++) + { + *page_ptr = cart_read_byte (cart); + page_ptr++; + lynxit_counter_increment (); + } +} + + +#if 0 +void +cart_write_page (unsigned int cart, unsigned int page_number, + unsigned int page_size, unsigned char *page_ptr) +{ +} +#endif + + +int +cart_analyse (int cart) +{ + unsigned char image[MAX_PAGE_SIZE]; + unsigned int page = 0; + unsigned char test = 0; + unsigned int loop = 0; + + MESSAGE (("ANALYSE : BANK%d ", cart)); + + for (;;) + { + // Read a page - start at zero, try a max of 8 pages + + if (page > 8) + { + MESSAGE (("N/A\n")); + return FALSE; + } + + cart_read_page (cart, page++, CART_PAGE_512K * 2, image); + + // Explicit check for no bank + + test = 0xff; + for (loop = 0; loop < CART_PAGE_512K; loop++) + test &= image[loop]; + + if (test == 0xff) + { + MESSAGE (("N/A\n")); + return FALSE; + } + + // Check bytes are not all same + + for (loop = 1; loop < CART_PAGE_512K; loop++) + { + if (image[loop] != image[0]) + break; + } + + // If we are at end of loop then buffer is all same + + if (loop != CART_PAGE_512K) + break; + } + +// { +// FILE *fp; +// fp=fopen("TEST.IMG","wb"); +// fwrite(image,sizeof(unsigned char),MAX_PAGE_SIZE,fp); +// fclose(fp); +// { + + // Check for 64K Cart + + for (loop = 0; loop < CART_PAGE_64K; loop++) + { + if (image[loop] != image[loop + CART_PAGE_64K]) + break; + } + + if (loop == CART_PAGE_64K) + { + MESSAGE (("64K\n")); + return CART_PAGE_64K; + } + + // Check for 128K Cart + + for (loop = 0; loop < CART_PAGE_128K; loop++) + { + if (image[loop] != image[loop + CART_PAGE_128K]) + break; + } + + if (loop == CART_PAGE_128K) + { + MESSAGE (("128K\n")); + return CART_PAGE_128K; + } + + // Check for 256K Cart + + for (loop = 0; loop < CART_PAGE_256K; loop++) + { + if (image[loop] != image[loop + CART_PAGE_256K]) + break; + } + + if (loop == CART_PAGE_256K) + { + MESSAGE (("256K\n")); + return CART_PAGE_256K; + } + + // Check for 512K Cart + + for (loop = 0; loop < CART_PAGE_512K; loop++) + { + if (image[loop] != image[loop + CART_PAGE_512K]) + break; + } + + if (loop == CART_PAGE_512K) + { + MESSAGE (("512K\n")); + return CART_PAGE_512K; + } + + // Must be no cart situation - floating data !!!) + + MESSAGE (("Bad cartridge\n")); + + return FALSE; +} + + +#define MAX_ERRORS 16 + +int +cart_verify (char *filename) +{ + unsigned char image1[MAX_PAGE_SIZE], image2[MAX_PAGE_SIZE]; + int offset = 0; + unsigned int loop = 0; + FILE *fp; + st_lnx_header_t header; + unsigned int result0 = MAX_ERRORS, result1 = MAX_ERRORS; + + +#if 0 + DEBUG (("cart_verify() called with <%s>\n\n", filename)); +#endif + + if ((fp = fopen (filename, "rb")) == NULL) + { +// MESSAGE (("ERROR : Could not open %s\n", filename)); + MESSAGE ((ucon64_msg[OPEN_READ_ERROR], filename)); + return FALSE; + } + + if (fread (&header, sizeof (st_lnx_header_t), 1, fp) != 1) + { + MESSAGE (("ERROR : Disk read operation failed on %s\n", filename)); + fclose (fp); + return FALSE; + } + + if (strcmp (header.magic, "LYNX") != 0) + { + MESSAGE (("ERROR : %s is not a lynx image\n", filename)); + fclose (fp); + return FALSE; + } + + if (header.version != FILE_FORMAT_VERSION) + { + MESSAGE (("ERROR : %s has wrong version information\n", filename)); + fclose (fp); + return FALSE; + } + + if (header.page_size_bank0 != cart_analyse (BANK0)) + { + MESSAGE (("ERROR : Cartridge BANK0 size mismatch\n")); + fclose (fp); + return FALSE; + } + + if (header.page_size_bank1 != cart_analyse (BANK1)) + { + MESSAGE (("ERROR : Cartridge BANK1 size mismatch\n")); + fclose (fp); + return FALSE; + } + + + if (header.page_size_bank0) + { + for (loop = 0; loop < 256; loop++) + { + MESSAGE (("Verifying BANK0: Page <%03d> of <256>", loop + 1)); + cart_read_page (BANK0, loop, header.page_size_bank0, image1); + MESSAGE (("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b")); + if (fread (image2, sizeof (unsigned char), header.page_size_bank0, fp) + != (size_t) header.page_size_bank0) + { + MESSAGE (("\nERROR : Disk read operation failed on %s\n", + filename)); + fclose (fp); + return FALSE; + } + + for (offset = 0; offset < header.page_size_bank0; offset++) + { + if (image1[offset] != image2[offset]) + { + MESSAGE (("VERIFY : Mismatch in BANK<0>:PAGE<%02x>:OFFSET<%03x>\n", loop, offset)); + if (!(--result0)) + { + MESSAGE (("VERIFY : Too many errors in BANK0, aborting\n")); + loop = 256; + break; + } + } + } + } + } + + if (header.page_size_bank1) + { + for (loop = 0; loop < 256; loop++) + { + MESSAGE (("Verifying BANK1: Page <%03d> of <256>", loop + 1)); + cart_read_page (BANK1, loop, header.page_size_bank1, image1); + MESSAGE (("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b")); + if (fread (image2, sizeof (unsigned char), header.page_size_bank1, fp) + != (size_t) header.page_size_bank1) + { + MESSAGE (("\nERROR : Disk read operation failed on %s\n", + filename)); + fclose (fp); + return FALSE; + } + + for (offset = 0; offset < header.page_size_bank0; offset++) + { + if (image1[offset] != image2[offset]) + { + MESSAGE (("VERIFY : Mismatch in BANK<1>:PAGE<%02x>:OFFSET<%03x>\n", loop, offset)); + if (!(--result1)) + { + MESSAGE (("VERIFY : Too many errors in BANK1, aborting\n")); + break; + } + } + } + } + } + + fclose (fp); + + if (result0 != MAX_ERRORS || result1 != MAX_ERRORS) + { + MESSAGE (("VERIFY : FAILED \n")); + return FALSE; + } + else + { + MESSAGE (("VERIFY : OK \n")); + return TRUE; + } +} + + +int +cart_read (char *filename) +{ + unsigned char image[MAX_PAGE_SIZE]; +// unsigned int page = 0; + unsigned int loop = 0; + FILE *fp; + st_lnx_header_t header; + +#if 0 + DEBUG (("read_cart() called with <%s>\n\n", filename)); +#endif + + memset (&header, 0, sizeof (st_lnx_header_t)); + strcpy (header.magic, MAGIC_STRING); + strcpy (header.cartname, cartname); + strcpy (header.manufname, manufname); + header.page_size_bank0 = cart_analyse (BANK0); + header.page_size_bank1 = cart_analyse (BANK1); + header.version = FILE_FORMAT_VERSION; + + if ((fp = fopen (filename, "wb")) == NULL) + return FALSE; + + if (fwrite (&header, sizeof (st_lnx_header_t), 1, fp) != 1) + { + MESSAGE (("ERROR : Disk write operation failed on %s\n", filename)); + fclose (fp); + return FALSE; + } + + if (header.page_size_bank0) + { + for (loop = 0; loop < 256; loop++) + { + MESSAGE (("Reading BANK0: Page <%03d> of <256>", loop + 1)); + cart_read_page (BANK0, loop, header.page_size_bank0, image); + MESSAGE (("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b")); + if (fwrite (image, sizeof (unsigned char), header.page_size_bank0, fp) + != (size_t) header.page_size_bank0) + { + MESSAGE (("\nERROR : Disk write operation failed on %s\n", + filename)); + fclose (fp); + return FALSE; + } + } + } + + if (header.page_size_bank1) + { + for (loop = 0; loop < 256; loop++) + { + MESSAGE (("Reading BANK1: Page <%03d> of <256>", loop + 1)); + cart_read_page (BANK1, loop, header.page_size_bank1, image); + MESSAGE (("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b")); + if (fwrite (image, sizeof (unsigned char), header.page_size_bank1, fp) + != (size_t) header.page_size_bank1) + { + MESSAGE (("\nERROR : Disk write operation failed on %s\n", + filename)); + fclose (fp); + return FALSE; + } + } + } + + MESSAGE (("READ : OK \n")); + + fclose (fp); + + if (verify) + return (cart_verify (filename)); + else + return TRUE; +} + + +#if 0 +int +cart_write (char *filename) +{ + DEBUG (("write_cart() called with <%s>\n\n", filename)); + return TRUE; +} +#endif + + +int +perform_test (char *testname) +{ + if (strcmp (testname, "LOOPBACK") == 0) + { + unsigned int loop; + + for (loop = 0; loop < 256; loop++) + { + lynxit_shift_out_byte ((unsigned char) loop); + lynxit_shift_out_byte (0); + lynxit_shift_out_byte (0); + lynxit_shift_out_byte (0); + + if (lynxit_shift_in_byte () != (unsigned char) loop) + { + MESSAGE (("LOOPBACK : FAILED\n")); + return FALSE; + } + } + MESSAGE (("LOOPBACK : OK\n")); + return TRUE; + } + else if (strncmp (testname, "SIZE", 4) == 0) + { + if (perform_test ("LOOPBACK") == FALSE) + return FALSE; + cart_analyse (BANK0); + cart_analyse (BANK1); + return TRUE; + } + else if (strncmp (testname, "CART", 4) == 0) + { + unsigned int page, offset; + unsigned char image1[CART_PAGE_128K]; + unsigned char image2[CART_PAGE_128K]; + int result = TRUE; + + if (perform_test ("LOOPBACK") == FALSE) + return FALSE; + cart_analyse (BANK0); + + for (page = 0; page < 256; page++) + { + MESSAGE (("Testing BANK0: Page <%03d> of <256>", page + 1)); + + cart_read_page (BANK0, page, CART_PAGE_128K, image1); + cart_read_page (BANK0, page, CART_PAGE_128K, image2); + + MESSAGE (("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b")); + + for (offset = 0; offset < CART_PAGE_128K; offset++) + { + if (image1[offset] != image2[offset]) + { + MESSAGE (("CARTRIDGE: Bad read on PAGE %02x \n", page)); + result = FALSE; + break; + } + } + } + if (!result) + { + MESSAGE (("CARTRIDGE: FAILED \n")); + return FALSE; + } + else + { + MESSAGE (("CARTRIDGE: OK \n")); + return TRUE; + } + } + else if (strcmp (testname, "INPORT") == 0) + { + do + { + MESSAGE (("INPORT : Read %02x\n", INPUT (print_stat))); + } + while (!kbhit ()); + return TRUE; + } + else if (strcmp (testname, "PAGE") == 0) + { + do + { + lynxit_write_page (0xaa); + MESSAGE (("PAGE : Wrote 0xAA to page\n")); + getch (); + lynxit_write_page (0x55); + MESSAGE (("PAGE : Wrote 0x55 to page\n")); + } + while (getch () != 'q'); + return TRUE; + } + else if (strcmp (testname, "COUNTER") == 0) + { + int stepmode = TRUE; + + MESSAGE (("\nPress key to step counter U5\n\n")); + MESSAGE ((" 'q' - Quit to DOS\n")); + MESSAGE ((" 's' - Step mode (default)\n")); + MESSAGE ((" 'r' - Run mode\n")); + MESSAGE ((" 'c' - Clear counter\n\n")); + + lynxit_counter_reset (); + for (;;) + { + lynxit_counter_increment (); + + MESSAGE (("COUNTER : increment\n")); + + if (kbhit () || stepmode) + { + switch (getch ()) + { + case 'q': + return TRUE; + case 's': + stepmode = TRUE; + break; + case 'r': + stepmode = FALSE; + break; + case 'c': + lynxit_counter_reset (); + MESSAGE (("COUNTER : reset\n")); + break; + default: + break; + } + } + } + return TRUE; + } + else + { + return FALSE; + } +} + + + +// +// +// MAIN CODE +// +// + +static char buf[FILENAME_MAX]; + + +int +lynxit_main (int argc, char **argv) +{ + int loop; + + // Handle argument list +#if 0 + for (loop = 0; loop < argc; loop++) + strupr (argv[loop]); +#endif + + for (loop = 1; loop < argc; loop++) + { + if (argv[loop][0] != '-') + { + break; + } + + if (strlen (argv[loop]) > 3) + { +// usage (); + printf ("\nInvalid argument %d <%s>\n", loop, argv[loop]); + exit (FALSE); + } + + switch (argv[loop][1]) + { + case 'P': + printer_port = (unsigned int) argv[loop][2] - '0'; + break; + case 'D': + debug = TRUE; + break; + case 'Q': + quiet = FALSE; + break; + case 'N': + verify = FALSE; + break; + case 'H': +// usage (); + exit (FALSE); + default: +// usage (); + printf ("\nUnrecognised argument %d <%s>\n", loop, argv[loop]); + exit (FALSE); + } + } + + // Check there are 2 spare arguments + + if (loop + 1 >= argc) + { +// usage (); + MESSAGE (("\nERROR : Missing command/filename\n")); + exit (FALSE); + } + + // Initialise the printer port + + if (!ptr_port_init (printer_port)) + { + MESSAGE (("ERROR : Couldn't initialise printer port LPT%d\n", + printer_port)); + exit (FALSE); + } + + // Perform loopback tests to prove operation + + if (!debug && strcmp (argv[loop], "TEST") != 0 + && !perform_test ("LOOPBACK")) + { + MESSAGE (("ERROR : LYNXIT doesn't appear to be working ????\n")); + MESSAGE (("ERROR : (Check its plugged in and switched on)\n")); + exit (FALSE); + } + + if (strcmp (argv[loop], "READ") == 0) + { + if (loop + 3 >= argc) + { + MESSAGE (("ERROR : Missing Cartname/Manufacturer arguments\n")); + exit (FALSE); + } + strcpy (cartname, argv[argc - 2]); + strcpy (manufname, argv[argc - 1]); + if (strlen (cartname) > 32 || strlen (manufname) > 16) + { + MESSAGE (("ERROR : Cartname/Manufacturer arguments too long (32/16)\n")); + exit (FALSE); + } + + strcpy (buf, argv[++loop]); + if (cart_read (buf) == FALSE) + { + MESSAGE (("ERROR : Cartridge read failed\n")); + exit (FALSE); + } + } + else if (strcmp (argv[loop], "WRITE") == 0) + { + strcpy (buf, argv[++loop]); +// cart_write (buf); // warning remover + } + else if (strcmp (argv[loop], "VERIFY") == 0) + { + cart_verify (argv[++loop]); + } + else if (strcmp (argv[loop], "TEST") == 0) + { + perform_test (argv[++loop]); + } + else + { +// usage (); + printf ("\nInvalid command argument - Use READ/WRITE/VERIFY/TEST\n"); + exit (FALSE); + } + + exit (TRUE); +} + + +int +lynxit_read_rom (const char *filename, unsigned int parport) +{ + char *argv[128]; + + print_data = parport; + parport_print_info (); + + argv[0] = "ucon64"; + argv[1] = "READ"; + strcpy (buf, filename); + argv[2] = buf; + + if (lynxit_main (3, argv) != 0) + { + fprintf (stderr, ucon64_msg[PARPORT_ERROR]); + exit (1); + } + + return 0; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/lynxit.h b/ucon64/2.0/src/backup/lynxit.h new file mode 100644 index 0000000..1f062c5 --- /dev/null +++ b/ucon64/2.0/src/backup/lynxit.h @@ -0,0 +1,31 @@ +/* +lynxit.h - lynxit support for uCON64 + +Copyright (c) 1997 - ???? K. Wilkins +Copyright (c) 2002 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef LYNXIT_H +#define LYNXIT_H + +extern const st_getopt2_t lynxit_usage[]; + +#ifdef USE_PARALLEL +extern int lynxit_read_rom (const char *filename, unsigned int parport); +#endif + +#endif diff --git a/ucon64/2.0/src/backup/mccl.c b/ucon64/2.0/src/backup/mccl.c new file mode 100644 index 0000000..e6023b3 --- /dev/null +++ b/ucon64/2.0/src/backup/mccl.c @@ -0,0 +1,158 @@ +/* +mccl.c - Mad Catz Camera Link (Game Boy Camera) support for uCON64 + +Copyright (c) 2002 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* +This cable is made by Mad Catz, Inc. and has a Game Boy link connector on one +end and a parallel port connector on the other. It is designed to interface +with the Game Boy Camera cart and comes with included software for this. It +works by simulating the Game Boy Printer with a PIC chip inside the parallel +connector shell. It doesn't do a particularly good job at that so it pretty +much only works with the Game Boy Camera. + +Mad Catz Camera Link Communications Protocol + +Printer IO Ports: +Base+0: Data Port +Base+1: Status Port +Base+2: Control + +Reset Procedure: +1. Output 0x24 to control (tristate data and set control to 0100) +2. Wait for bit 5 of status port to become 1 +3. Read lower 4 bits of data port +4. If read data != 4, then go to step 1. +5. (Useless read of control port?) +6. Output 0x22 to control (tristate data and set control to 0010) +7. Wait for bit 5 of status port to become 0 +8. Output 0x26 to control (tristate data and set control to 0110) + +Data Read Procedure: +1. Output 0x26 to control (tristate data and set control to 0110) +2. Wait for bit 5 of status port to become 1 +3. Read lower 4 bits of data port, store to lower 4 bits of received byte +4. (Useless read of control port?) +5. Output 0x22 to control (tristate data and set control to 0010) +6. Wait for bit 5 of status port to become 0 +7. Output 0x26 to control (tristate data and set control to 0110) +8. Wait for bit 5 of status port to become 1 +9. Read lower 4 bits of data port, store to upper 4 bits of received byte +10. (Useless read of control port?) +11. Output 0x22 to control (tristate data and set control to 0010) +12. Wait for bit 5 of status port to become 0 +13. Go to step 1 +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include "misc/misc.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "mccl.h" +#include "misc/parallel.h" + + +const st_getopt2_t mccl_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Mad Catz Camera Link (Game Boy Camera)"/*"XXXX Mad Catz Inc. http://www.madcatz.com"*/, + NULL + }, +#ifdef USE_PARALLEL + { + "xmccl", 0, 0, UCON64_XMCCL, + NULL, "receives from Mad Catz Camera Link; " OPTION_LONG_S "port=PORT", + &ucon64_wf[WF_OBJ_GB_DEFAULT_STOP_NO_ROM] + }, +#endif + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +#ifdef USE_PARALLEL + +#define DATA ((unsigned short) (parport + PARPORT_DATA)) +#define STATUS ((unsigned short) (parport + PARPORT_STATUS)) +#define CONTROL ((unsigned short) (parport + PARPORT_CONTROL)) + + +int +mccl_read (const char *filename, unsigned int parport) +{ + unsigned char buffer[0x1760]; + char dest_name[FILENAME_MAX]; + int inbyte, count = 0; + time_t starttime; + + parport_print_info (); + puts ("Resetting device"); + do + { + outportb (CONTROL, 0x24); + while ((inportb (STATUS) & 0x20) == 0) + ; + } + while ((inportw (DATA) & 0xf) != 4); + outportb (CONTROL, 0x22); + while ((inportb (STATUS) & 0x20) != 0) + ; + outportb (CONTROL, 0x26); + + printf ("Receive: %d Bytes (%.4f Mb)\n\n", 0x1760, (float) 0x1760 / MBIT); + starttime = time (NULL); + do + { + outportb (CONTROL, 0x26); + while ((inportb (STATUS) & 0x20) == 0) + ; + inbyte = inportw (DATA) & 0xf; + outportb (CONTROL, 0x22); + while ((inportb (STATUS) & 0x20) != 0) + ; + outportb (CONTROL, 0x26); + while ((inportb (STATUS) & 0x20) == 0) + ; + inbyte |= (inportw (DATA) & 0xf) << 4; + outportb (CONTROL, 0x22); + while ((inportb (STATUS) & 0x20) != 0) + ; + buffer[count++] = inbyte; + if ((count & 0x1f) == 0) + ucon64_gauge (starttime, count, 0x1760); + } + while (count < 0x1760); + + strcpy (dest_name, filename); + ucon64_file_handler (dest_name, NULL, 0); + ucon64_fwrite (buffer, 0, count, dest_name, "wb"); + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/mccl.h b/ucon64/2.0/src/backup/mccl.h new file mode 100644 index 0000000..7d3be04 --- /dev/null +++ b/ucon64/2.0/src/backup/mccl.h @@ -0,0 +1,30 @@ +/* +mccl.h - Mad Catz Camera Link (Game Boy Camera) support for uCON64 + +Copyright (c) 2002 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef MCCL_H +#define MCCL_H + +extern const st_getopt2_t mccl_usage[]; + +#ifdef USE_PARALLEL +extern int mccl_read (const char *filename, unsigned int parport); +#endif + +#endif // MCCL_H diff --git a/ucon64/2.0/src/backup/mccl.png b/ucon64/2.0/src/backup/mccl.png new file mode 100644 index 0000000..078645e Binary files /dev/null and b/ucon64/2.0/src/backup/mccl.png differ diff --git a/ucon64/2.0/src/backup/mcd.c b/ucon64/2.0/src/backup/mcd.c new file mode 100644 index 0000000..f060f6d --- /dev/null +++ b/ucon64/2.0/src/backup/mcd.c @@ -0,0 +1,144 @@ +/* +mcd.c - Mike Pavone's Genesis/Sega CD transfer cable support for uCON64 + +Copyright (c) 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include "misc/misc.h" +#include "misc/parallel.h" +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "mcd.h" + + +const st_getopt2_t mcd_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Mike Pavone's Genesis/Sega CD transfer cable", + NULL + }, +#ifdef USE_PARALLEL + { + "xmcd", 0, 0, UCON64_XMCD, + NULL, "receive ROM from Genesis/Sega CD; " OPTION_LONG_S "port=PORT", + &ucon64_wf[WF_OBJ_GEN_DEFAULT_STOP_NO_SPLIT_NO_ROM] + }, +#endif + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +#ifdef USE_PARALLEL + +#define BUFFERSIZE 8192 + + +static void +checkabort (int status) +{ + if (((!ucon64.frontend) ? kbhit () : 0) && getch () == 'q') + { + puts ("\nProgram aborted"); + exit (status); + } +} + + +static void +read_block (unsigned char *buffer, int size, unsigned short parport) +{ + int i; + + for (i = 0; i < size; i++) + { + while (inportb ((unsigned short) (parport + PARPORT_STATUS)) & 0x08) + ; + outportb ((unsigned short) (parport + PARPORT_CONTROL), 0xa2); + + while (!(inportb ((unsigned short) (parport + PARPORT_STATUS)) & 0x08)) + ; + buffer[i] = inportb ((unsigned short) (parport + PARPORT_DATA)) & 0x0f; + outportb ((unsigned short) (parport + PARPORT_CONTROL), 0xa0); + + while (inportb ((unsigned short) (parport + PARPORT_STATUS)) & 0x08) + ; + outportb ((unsigned short) (parport + PARPORT_CONTROL), 0xa2); + + while (!(inportb ((unsigned short) (parport + PARPORT_STATUS)) & 0x08)) + ; + buffer[i] |= + (inportb ((unsigned short) (parport + PARPORT_DATA)) & 0x0f) << 4; + outportb ((unsigned short) (parport + PARPORT_CONTROL), 0xa0); + } +} + + +int +mcd_read_rom (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char buffer[BUFFERSIZE]; + int n_bytes = 0, size; + time_t starttime; + +#if (defined __unix__ || defined __BEOS__) && !defined __MSDOS__ + init_conio (); +#endif + parport_print_info (); + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + + read_block (buffer, 1, (unsigned short) parport); + size = (buffer[0] + 1) * 64 * 1024; + printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + puts ("Press q to abort\n"); + + starttime = time (NULL); + while (n_bytes < size) + { + read_block (buffer, BUFFERSIZE, (unsigned short) parport); + fwrite (buffer, 1, BUFFERSIZE, file); + n_bytes += BUFFERSIZE; + ucon64_gauge (starttime, n_bytes, size); + checkabort (2); + } + + fclose (file); +#if (defined __unix__ || defined __BEOS__) && !defined __MSDOS__ + deinit_conio (); +#endif + + return 0; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/mcd.h b/ucon64/2.0/src/backup/mcd.h new file mode 100644 index 0000000..10f8108 --- /dev/null +++ b/ucon64/2.0/src/backup/mcd.h @@ -0,0 +1,30 @@ +/* +mcd.h - Mike Pavone's Genesis/Sega CD transfer cable support for uCON64 + +Copyright (c) 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef MCD_H +#define MCD_H + +extern const st_getopt2_t mcd_usage[]; + +#ifdef USE_PARALLEL +extern int mcd_read_rom (const char *filename, unsigned int parport); +#endif + +#endif diff --git a/ucon64/2.0/src/backup/md-pro.c b/ucon64/2.0/src/backup/md-pro.c new file mode 100644 index 0000000..8660750 --- /dev/null +++ b/ucon64/2.0/src/backup/md-pro.c @@ -0,0 +1,395 @@ +/* +md-pro.c - MD-PRO flash card programmer support for uCON64 + +Copyright (c) 2003 - 2004 dbjh +Copyright (c) 2003 NoisyB + +Based on Delphi source code by ToToTEK Multi Media. Information in that source +code has been used with permission. However, ToToTEK Multi Media explicitly +stated that the information in that source code may be freely distributed. + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include "misc/parallel.h" +#include "misc/itypes.h" +#include "misc/misc.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "misc/file.h" +#include "ucon64.h" +#include "ucon64_misc.h" +#include "tototek.h" +#include "md-pro.h" + + +const st_getopt2_t mdpro_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "MD-PRO flash card programmer"/*"2003 ToToTEK Multi Media http://www.tototek.com"*/, + NULL + }, +#ifdef USE_PARALLEL + { + "xmd", 0, 0, UCON64_XMD, + NULL, "send/receive ROM to/from MD-PRO flash card programmer\n" OPTION_LONG_S "port=PORT\n" + "receives automatically (32/64 Mbits) when ROM does not exist", + &ucon64_wf[WF_OBJ_GEN_DEFAULT_STOP_NO_SPLIT_NO_ROM] + }, + { + "xmds", 0, 0, UCON64_XMDS, + NULL, "send/receive SRAM to/from MD-PRO flash card programmer\n" OPTION_LONG_S "port=PORT\n" + "receives automatically when SRAM does not exist", + &ucon64_wf[WF_OBJ_GEN_STOP_NO_ROM] + }, + { + "xmdb", 1, 0, UCON64_XMDB, + "BANK", "send/receive SRAM to/from MD-PRO BANK\n" + "BANK can be a number from 1 to 4; " OPTION_LONG_S "port=PORT\n" + "receives automatically when SRAM does not exist", + &ucon64_wf[WF_OBJ_GEN_STOP_NO_ROM] + }, +#endif + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +#ifdef USE_PARALLEL + +static void eep_reset (void); +static void write_rom_by_byte (int *addr, unsigned char *buf); +static void write_rom_by_page (int *addr, unsigned char *buf); +static void write_ram_by_byte (int *addr, unsigned char *buf); +static void write_ram_by_page (int *addr, unsigned char *buf); + +static unsigned short int md_id; + + +void +eep_reset (void) +{ + ttt_rom_enable (); + ttt_write_mem (0x400000, 0xff); // reset EEP chip 2 + ttt_write_mem (0, 0xff); // reset EEP chip 1 + ttt_write_mem (0x600000, 0xff); // reset EEP chip 2 + ttt_write_mem (0x200000, 0xff); // reset EEP chip 1 + ttt_rom_disable (); +} + + +void +write_rom_by_byte (int *addr, unsigned char *buf) +{ + int x; + + for (x = 0; x < 0x4000; x++) + { + if (md_id == 0xb0d0) + ttt_write_byte_sharp (*addr, buf[*addr & 0x3fff]); + else if (md_id == 0x8916 || md_id == 0x8917) + ttt_write_byte_intel (*addr, buf[*addr & 0x3fff]); + (*addr)++; + } +} + + +void +write_rom_by_page (int *addr, unsigned char *buf) +{ + int x; + + for (x = 0; x < 0x200; x++) + { + ttt_write_page_rom (*addr, buf); + (*addr) += 0x20; + } +} + + +void +write_ram_by_byte (int *addr, unsigned char *buf) +{ + int x, i = *addr & 0x3fff; + + for (x = 0; x < 0x4000; x++, i = (i + 1) & 0x3fff) + { + ttt_write_byte_ram (*addr, buf[i]); + (*addr)++; + // Send the same byte again => SRAM files needn't store redundant data + ttt_write_byte_ram (*addr, buf[i]); + (*addr)++; + } +} + + +void +write_ram_by_page (int *addr, unsigned char *buf) +{ + int x; + + for (x = 0; x < 0x80; x++) + { + ttt_write_page_ram2 (*addr, buf); + (*addr) += 0x80; + } +} + + +int +md_read_rom (const char *filename, unsigned int parport, int size) +{ + FILE *file; + unsigned char buffer[0x100]; + int blocksleft, address = 0, id; + time_t starttime; + void (*read_block) (int, unsigned char *) = ttt_read_rom_w; // ttt_read_rom_b + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + ttt_init_io (parport); + + eep_reset (); + id = ttt_get_id (); + if ((id == 0xb0d0 || id == 0x8916) && size > 32 * MBIT) + size = 32 * MBIT; // Sharp or Intel 32 Mbit flash card +#if 0 + // size is set to 64 * MBIT "by default" (in ucon64_opts.c) + else if (id == 0x8917 && size > 64 * MBIT) + size = 64 * MBIT; // Intel 64 Mbit flash card +#endif + + printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + + blocksleft = size >> 8; + eep_reset (); + ttt_rom_enable (); + if (read_block == ttt_read_rom_w) + ttt_set_ai_data (6, 0x94); // rst=1, wei=0(dis.), rdi=0(dis.), inc mode, rom_CS + starttime = time (NULL); + while (blocksleft-- > 0) + { + read_block (address, buffer); // 0x100 bytes read + if (read_block == ttt_read_rom_b) + ucon64_bswap16_n (buffer, 0x100); + fwrite (buffer, 1, 0x100, file); + address += 0x100; + if ((address & 0x3fff) == 0) + ucon64_gauge (starttime, address, size); + } + // original code doesn't call ttt_rom_disable() when byte-size function is + // used (ttt_read_rom_b() calls it) + if (read_block == ttt_read_rom_w) + ttt_rom_disable (); + + fclose (file); + ttt_deinit_io (); + + return 0; +} + + +int +md_write_rom (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char buffer[0x4000]; + int size, address = 0, bytesread, bytessend = 0; + time_t starttime; + void (*write_block) (int *, unsigned char *) = write_rom_by_page; // write_rom_by_byte + (void) write_rom_by_byte; + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + ttt_init_io (parport); + + size = fsizeof (filename); + printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + + eep_reset (); + md_id = ttt_get_id (); + if ((md_id != 0xb0d0) && (md_id != 0x8916) && (md_id != 0x8917)) // Sharp 32M, Intel 64J3 + { + fputs ("ERROR: MD-PRO flash card (programmer) not detected\n", stderr); + fclose (file); + ttt_deinit_io (); + exit (1); + } + + starttime = time (NULL); + eep_reset (); + while ((bytesread = fread (buffer, 1, 0x4000, file))) + { + ucon64_bswap16_n (buffer, 0x4000); + if ((((address & 0xffff) == 0) && (md_id == 0xb0d0)) || + (((address & 0x1ffff) == 0) && (md_id == 0x8916 || md_id == 0x8917))) + ttt_erase_block (address); + write_block (&address, buffer); + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, size); + } + + fclose (file); + ttt_deinit_io (); + + return 0; +} + + +int +md_read_sram (const char *filename, unsigned int parport, int start_bank) +/* + The MD-PRO has 256 kB of SRAM. However, the SRAM dumps of all games that have + been tested had each byte doubled. In order to make it possible to easily + obtain the SRAM data for use in an emulator, or to send an emulator SRAM file + to the MD-PRO, we remove the redundant data when receiving/dumping and double + the data when sending. + It could be that this approach causes trouble for some games. However, when + looking at ToToTEK's own code in ttt_write_page_ram2() this seems unlikely + (data is doubled in that function). Note that write_sram_by_byte() is a + function written by us, and so does the doubling of data, but it doesn't mean + it should work for all games. +*/ +{ + FILE *file; + unsigned char buffer[0x100]; + int blocksleft, address, bytesreceived = 0, size, i; + time_t starttime; + void (*read_block) (int, unsigned char *) = ttt_read_ram_b; // ttt_read_ram_w + // This function does not seem to work if ttt_read_ram_w() is used, but see + // note below + + if (start_bank == -1) + { + address = 0; + size = 128 * 1024; + } + else + { + if (start_bank < 1 || start_bank > 4) + { + fputs ("ERROR: Bank must be a value 1 - 4\n", stderr); + exit (1); + } + address = (start_bank - 1) * 32 * 1024; + size = 32 * 1024; + } + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + + ttt_init_io (parport); + printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + + if (read_block == ttt_read_ram_w) + { +// address *= 2; + ttt_ram_enable (); +#if 0 + // According to JohnDie, disabling this statement should make it possible + // to use ttt_read_ram_w(). + ttt_set_ai_data (6, 0x98); // rst=1, wei=0(dis.), rdi=0(dis.), inc mode, rom_CS +#endif + } +// else +// ttt_set_ai_data (6, 0x94); // rst=1, wei=0(dis.), rdi=0(dis.), inc mode, rom_CS + + blocksleft = size >> 7; + starttime = time (NULL); + while (blocksleft-- > 0) + { + read_block (address, buffer); // 0x100 bytes read + for (i = 0; i < 0x80; i++) + buffer[i] = buffer[2 * i]; // data is doubled => no problems with endianess + fwrite (buffer, 1, 0x80, file); + address += 0x100; + bytesreceived += 0x80; + if ((address & 0x3fff) == 0) + ucon64_gauge (starttime, bytesreceived, size); + } + if (read_block == ttt_read_ram_w) + ttt_ram_disable (); + + fclose (file); + ttt_deinit_io (); + + return 0; +} + + +int +md_write_sram (const char *filename, unsigned int parport, int start_bank) +{ + FILE *file; + unsigned char buffer[0x4000]; + int size, bytesread, bytessend = 0, address; + time_t starttime; + void (*write_block) (int *, unsigned char *) = write_ram_by_byte; // write_ram_by_page + (void) write_ram_by_page; + + size = fsizeof (filename); + if (start_bank == -1) + address = 0; + else + { + if (start_bank < 1 || start_bank > 4) + { + fputs ("ERROR: Bank must be a value 1 - 4\n", stderr); + exit (1); + } + address = (start_bank - 1) * 32 * 1024; + } + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + + ttt_init_io (parport); + printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + + starttime = time (NULL); + while ((bytesread = fread (buffer, 1, 0x4000, file))) + { + write_block (&address, buffer); + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, size); + } + + fclose (file); + ttt_deinit_io (); + + return 0; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/md-pro.h b/ucon64/2.0/src/backup/md-pro.h new file mode 100644 index 0000000..ca9d801 --- /dev/null +++ b/ucon64/2.0/src/backup/md-pro.h @@ -0,0 +1,37 @@ +/* +md-pro.h - MD-PRO flash card programmer support for uCON64 + +Copyright (c) 2003 dbjh + +Based on Delphi source code by ToToTEK Multi Media. Information in that source +code has been used with permission. However, ToToTEK Multi Media explicitly +stated that the information in that source code may be freely distributed. + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef MD_PRO_H +#define MD_PRO_H + +extern const st_getopt2_t mdpro_usage[]; + +#ifdef USE_PARALLEL +extern int md_read_rom (const char *filename, unsigned int parport, int size); +extern int md_write_rom (const char *filename, unsigned int parport); +extern int md_read_sram (const char *filename, unsigned int parport, int start_bank); +extern int md_write_sram (const char *filename, unsigned int parport, int start_bank); +#endif + +#endif diff --git a/ucon64/2.0/src/backup/mgd.c b/ucon64/2.0/src/backup/mgd.c new file mode 100644 index 0000000..fe02b43 --- /dev/null +++ b/ucon64/2.0/src/backup/mgd.c @@ -0,0 +1,466 @@ +/* +mgd.c - Multi Game Doctor/Hunter support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2001 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include "misc/misc.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "misc/string.h" +#include "mgd.h" + + +const st_getopt2_t mgd_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Multi Game Doctor (2)/Multi Game Hunter/MGH" + /*"19XX Bung Enterprises Ltd http://www.bung.com.hk\n" "?Makko Toys Co., Ltd.?"*/, + NULL + }, +#if 0 + { + "xmgd", 0, 0, UCON64_XMGD, + NULL, "(TODO) send/receive ROM to/from Multi Game* /MGD2/MGH; " OPTION_LONG_S "port=PORT\n" + "receives automatically when " OPTION_LONG_S "rom does not exist", + NULL + }, +#endif + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +// the following four functions are used by non-transfer code in genesis.c +void +mgd_interleave (unsigned char **buffer, int size) +{ + int n; + unsigned char *src = *buffer; + + if (!(*buffer = (unsigned char *) malloc (size))) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], size); + exit (1); + } + for (n = 0; n < size / 2; n++) + { + (*buffer)[n] = src[n * 2 + 1]; + (*buffer)[size / 2 + n] = src[n * 2]; + } + free (src); +} + + +void +mgd_deinterleave (unsigned char **buffer, int data_size, int buffer_size) +{ + int n = 0, offset; + unsigned char *src = *buffer; + + if (!(*buffer = (unsigned char *) malloc (buffer_size))) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], buffer_size); + exit (1); + } + for (offset = 0; offset < data_size / 2; offset++) + { + (*buffer)[n++] = src[data_size / 2 + offset]; + (*buffer)[n++] = src[offset]; + } + free (src); +} + + +int +fread_mgd (void *buffer, size_t size, size_t number, FILE *fh) +/* + This function is used to handle a Genesis MGD file as if it wasn't + interleaved, without the overhead of reading the entire file into memory. + This is important for genesis_init(). When the file turns out to be a Genesis + dump in MGD format it is much more efficient for compressed files to read the + entire file into memory and then deinterleave it (as load_rom() does). + In order to speed this function up a bit ucon64.file_size is used. That means + it can't be used for an arbitrary file. +*/ +{ + int n = 0, bpos = 0, fpos, fpos_org, block_size, bytesread = 0, + len = number * size, fsize = ucon64.file_size /* fsizeof (filename) */; + unsigned char tmp1[MAXBUFSIZE], tmp2[MAXBUFSIZE]; + + fpos = fpos_org = ftell (fh); + if (fpos >= fsize) + return 0; + + if (len == 0) + return 0; + else if (len == 1) + { + if (fpos_org & 1) + { + fseek (fh, fpos / 2, SEEK_SET); + *((unsigned char *) buffer) = fgetc (fh); + } + else + { + fseek (fh, fpos / 2 + fsize / 2, SEEK_SET); + *((unsigned char *) buffer) = fgetc (fh); + } + fseek (fh, fpos_org + 1, SEEK_SET); + return 1; + } + + while (len > 0 && !feof (fh)) + { + block_size = len > MAXBUFSIZE ? MAXBUFSIZE : len; + + fseek (fh, fpos / 2, SEEK_SET); + bytesread += fread (tmp1, 1, block_size / 2, fh); // read odd bytes + fseek (fh, (fpos + 1) / 2 + fsize / 2, SEEK_SET); + bytesread += fread (tmp2, 1, block_size / 2, fh); // read even bytes + + if (fpos_org & 1) + for (n = 0; n < block_size / 2; n++) + { + ((unsigned char *) buffer)[bpos + n * 2] = tmp1[n]; + ((unsigned char *) buffer)[bpos + n * 2 + 1] = tmp2[n]; + } + else + for (n = 0; n < block_size / 2; n++) + { + ((unsigned char *) buffer)[bpos + n * 2] = tmp2[n]; + ((unsigned char *) buffer)[bpos + n * 2 + 1] = tmp1[n]; + } + fpos += block_size; + bpos += block_size; + len -= block_size; + } + fseek (fh, fpos_org + bytesread, SEEK_SET); + return bytesread / size; +} + + +int +q_fread_mgd (void *buffer, size_t start, size_t len, const char *filename) +{ + int result; + FILE *fh; + + if ((fh = fopen (filename, "rb")) == NULL) + return -1; + fseek (fh, start, SEEK_SET); + result = (int) fread_mgd (buffer, 1, len, fh); + fclose (fh); + + return result; +} + + +static void +remove_mgd_id (char *name, const char *id) +{ + char *p = name; + while ((p = strstr (p, id))) + { + *p = 'X'; + *(p + 1) = 'X'; + p += 2; + } +} + + +void +mgd_make_name (const char *filename, int console, int size, char *name) +// these characters are also valid in MGD file names: !@#$%^&_ +{ + char *prefix = 0, *p, *size_str = 0, *suffix = 0; + const char *fname; + int n; + + switch (console) + { + default: // falling through + case UCON64_SNES: + prefix = "SF"; + suffix = ".048"; + if (size <= 1 * MBIT) + size_str = "1"; + else if (size <= 2 * MBIT) + size_str = "2"; + else if (size <= 4 * MBIT) + size_str = "4"; + else if (size <= 8 * MBIT) + { + size_str = "8"; + suffix = ".058"; + } + else + { + suffix = ".078"; + if (size <= 10 * MBIT) + size_str = "10"; + else if (size <= 12 * MBIT) + size_str = "12"; + else if (size <= 16 * MBIT) + size_str = "16"; + else if (size <= 20 * MBIT) + size_str = "20"; + else if (size <= 24 * MBIT) + size_str = "24"; + else // MGD supports SNES games with sizes up to 32 Mbit + size_str = "32"; + } + break; + case UCON64_GEN: + prefix = "MD"; + suffix = ".000"; + if (size <= 1 * MBIT) + size_str = "1"; + else if (size <= 2 * MBIT) + size_str = "2"; + else if (size <= 4 * MBIT) + size_str = "4"; + else + { + if (size <= 8 * MBIT) + { + size_str = "8"; + suffix = ".008"; + } + else if (size <= 16 * MBIT) + { + size_str = "16"; + suffix = ".018"; + } + else + { + suffix = ".038"; + if (size <= 20 * MBIT) + size_str = "20"; + else if (size <= 24 * MBIT) + size_str = "24"; + else // MGD supports Genesis games with sizes up to 32 Mbit + size_str = "32"; + } + } + break; + case UCON64_PCE: + prefix = "PC"; + suffix = ".040"; + if (size <= 1 * MBIT) + size_str = "1"; + else if (size <= 2 * MBIT) + size_str = "2"; + else if (size <= 3 * MBIT) + { + size_str = "3"; + suffix = ".030"; + } + else if (size <= 4 * MBIT) + { + size_str = "4"; + suffix = ".048"; + } + else + { + suffix = ".058"; + if (size <= 6 * MBIT) + size_str = "6"; + else // MGD supports PC-Engine games with sizes up to 8 Mbit + size_str = "8"; + } + break; + case UCON64_SMS: + prefix = "GG"; + suffix = ".060"; + if (size < 1 * MBIT) + size_str = "0"; + else if (size == 1 * MBIT) + size_str = "1"; + else if (size <= 2 * MBIT) + size_str = "2"; + else + { + suffix = ".078"; + if (size <= 3 * MBIT) + size_str = "3"; + else if (size <= 4 * MBIT) + size_str = "4"; + else if (size <= 6 * MBIT) + size_str = "6"; + else // MGD supports Sega Master System games with sizes up to 8 Mbit + size_str = "8"; + } + break; + case UCON64_GAMEGEAR: + prefix = "GG"; + suffix = ".040"; + if (size < 1 * MBIT) + size_str = "0"; + else if (size == 1 * MBIT) + size_str = "1"; + else if (size <= 2 * MBIT) + size_str = "2"; + else + { + suffix = ".048"; + if (size <= 3 * MBIT) + size_str = "3"; + else if (size <= 4 * MBIT) + size_str = "4"; + else + { + suffix = ".078"; + if (size <= 6 * MBIT) + size_str = "6"; + else // MGD supports Game Gear games with sizes up to 8 Mbit + size_str = "8"; + } + } + break; + case UCON64_GB: + prefix = "GB"; + /* + What is the maximum game size the MGD2 supports for GB (color) games? + At least one 64 Mbit game exists, Densha De Go! 2 (J) [C][!]. + */ + suffix = ".040"; + if (size < 1 * MBIT) + size_str = "0"; + else if (size == 1 * MBIT) + size_str = "1"; + else if (size <= 2 * MBIT) + size_str = "2"; + else if (size <= 3 * MBIT) + { + size_str = "3"; + suffix = ".030"; + } + else if (size <= 4 * MBIT) + { + size_str = "4"; + suffix = ".048"; + } + else + { + suffix = ".058"; + if (size <= 6 * MBIT) + size_str = "6"; + else + size_str = "8"; + } + break; + } + + fname = basename2 (filename); + // Do NOT mess with prefix (strupr()/strlwr()). See below (remove_mgd_id()). + sprintf (name, "%s%s%s", prefix, size_str, fname); + if (size >= 10 * MBIT) + { + if (!strnicmp (name, fname, 4)) + strcpy (name, fname); + } + else + { + if (!strnicmp (name, fname, 3)) + strcpy (name, fname); + } + if ((p = strchr (name, '.'))) + *p = 0; + n = strlen (name); + if (size >= 10 * MBIT) + { + if (n < 7) + strcat (name, "XXX"); // in case fname is 1 character long + n = 7; + } + else + { + if (n < 6) + strcat (name, "XX"); + n = 6; + } + name[n] = '0'; // last character must be a number + name[n + 1] = 0; + for (n = 3; n < 8; n++) // we can skip the prefix + if (name[n] == ' ') + name[n] = 'X'; + + /* + the transfer program "pclink" contains a bug in that it looks at the + entire file name for an ID string (it should look only at the first 2 + characters). + */ + strupr (name); + remove_mgd_id (name + 3, "SF"); + remove_mgd_id (name + 3, "MD"); + remove_mgd_id (name + 3, "PC"); + remove_mgd_id (name + 3, "GG"); + remove_mgd_id (name + 3, "GB"); + + set_suffix (name, suffix); +} + + +void +mgd_write_index_file (void *ptr, int n_names) +{ + char buf[100 * 10], *p, name[16], dest_name[FILENAME_MAX]; + // one line in the index file takes 10 bytes at max (name (8) + "\r\n" (2)), + // so buf is large enough for 44 files of 1/4 Mbit (max for 1 diskette) + + if (n_names == 1) + { + strcpy (name, (char *) ptr); + if ((p = strrchr (name, '.'))) + *p = 0; + sprintf (buf, "%s\r\n", name); // DOS text file format + } + else if (n_names > 1) + { + int n = 0, offset = 0; + + for (; n < n_names; n++) + { + strcpy (name, ((char **) ptr)[n]); + if ((p = strrchr (name, '.'))) + *p = 0; + sprintf (buf + offset, "%s\r\n", name); + offset += strlen (name) + 2; // + 2 for "\r\n" + } + } + else // n_names <= 0 + return; + + strcpy (dest_name, "MULTI-GD"); + ucon64_file_handler (dest_name, NULL, OF_FORCE_BASENAME); + ucon64_fwrite (buf, 0, strlen (buf), dest_name, "wb"); + printf (ucon64_msg[WROTE], dest_name); +} diff --git a/ucon64/2.0/src/backup/mgd.h b/ucon64/2.0/src/backup/mgd.h new file mode 100644 index 0000000..591dd31 --- /dev/null +++ b/ucon64/2.0/src/backup/mgd.h @@ -0,0 +1,185 @@ +/* +mgd.h - Multi Game Doctor/Hunter support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2002 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef MGD_H +#define MGD_H + +extern const st_getopt2_t mgd_usage[]; + +/* +The MGD2 only accepts certain filenames, and these filenames +must be specified in an index file, "MULTI-GD", otherwise the +MGD2 will not recognize the file. In the case of multiple games +being stored in a single disk, simply enter its corresponding +MULTI-GD index into the "MULTI-GD" file. + +Thanks to acem77 for the (verified) list below. + +Super Famicom: + +game size # of files names MULTI-GD +================================================================ +1M 1 SF1XXX#.048 SF1XXX# +2M 1 SF2XXX#.048 SF2XXX# +4M 1 SF4XXX#.048 SF4XXX# +4M 2 SF4XXXxA.078 SF4XXXxA + SF4XXXxB.078 SF4XXXxB +8M 1 SF8XXX#.058 SF8XXX# + 2 SF8XXXxA.078 SF8XXXxA + SF8XXXxB.078 SF8XXXxB +10M 1 SF10XXX#.078 SF10XXX# + 2 SF10XXX#.078 SF10XXX# + SF10XXX#.078 SF10XXX# +12M 1 SF12XXX#.078 SF12XXX# + 2 SF12XXX#A.078 SF12XXX#B + SF12XXX#B.078 SF12XXX#A +16M 1 SF16XXX#.078 SF16XXX# +16M 2 SF16XXX#A.078 SF16XXX#A + SF16XXX#B.078 SF16XXX#B +20M 1 SF20XXX#.078 SF20XXX# + 3 SF20XXX#A.078 SF20XXX#A + SF20XXX#B.078 SF20XXX#B + SF20XXX#C.078 SF20XXX#C +24M 1 SF24XXX#.078 SF24XXX# +24M 3 SF24XXX#A.078 SF24XXX#A + SF24XXX#B.078 SF24XXX#B + SF24XXX#C.078 SF24XXX#C +32M 1 SF32XXX#.078 SF32XXX# +32M 4 SF32XXX#A.078 SF32XXX#A + SF32XXX#B.078 SF32XXX#B + SF32XXX#C.078 SF32XXX#C + SF32XXX#D.078 SF32XXX#D + +Mega Drive: + +game size # of files names MULTI-GD +================================================================ +1M 1 MD1XXX#.000 MD1XXX# +2M 1 MD2XXX#.000 MD2XXX# +4M 1 MD4XXX#.000 MD4XXX# +8M 1 MD8XXX#.008 MD8XXX# +16M 2 MD16XXX#A.018 MD16XXX#A + MD16XXX#B.018 MD16XXX#B +20M 3 MD20XXX#A.038 MD20XXX#A + MD20XXX#B.038 MD20XXX#B + MD20XXX#C.038 MD20XXX#C +24M 3 MD24XXX#A.038 MD24XXX#A + MD24XXX#B.038 MD24XXX#B + MD24XXX#C.038 MD24XXX#C +32M 4 MD32XXX#A.038 MD32XXX#A + MD32XXX#B.038 MD32XXX#B + MD32XXX#C.038 MD32XXX#C + MD32XXX#D.038 MD32XXX#D + +PC-Engine: + +game size # of files names MULTI-GD +================================================================ +1M 1 PC1XXX#.040 PC1XXX# +2M 1 PC2XXX#.040 PC2XXX# +3M 1 PC3XXX#.030 PC2XXX# +4M 1 PC4XXX#.048 PC4XXX# +6M 1 PC6XXX#.058 PC6XXX# +8M 1 PC8XXX#.058 PC8XXX# + +Sega Master System: + +game size # of files names MULTI-GD +================================================================ +less than 1M 1 GG0XXX#.060 GG0XXX# +1M 1 GG1XXX#.060 GG1XXX# +2M 1 GG2XXX#.060/70 GG2XXX# +3M 1 GG3XXX#.078 GG2XXX# +4M 1 GG4XXX#.078/68 GG4XXX# +6M 1 GG6XXX#.078 GG6XXX# +8M 1 GG8XXX#.078 GG8XXX# + +Game Gear: + +game size # of files names MULTI-GD +================================================================ +less than 1M 1 GG0XXX#.040 GG0XXX# +1M 1 GG1XXX#.040 GG1XXX# +2M 1 GG2XXX#.050/40 GG2XXX# +3M 1 GG3XXX#.048 GG2XXX# +4M 1 GG4XXX#.058/48 GG4XXX# +6M 1 GG6XXX#.078 GG6XXX# +8M 1 GG8XXX#.078 GG8XXX# + +Game Boy: + +game size # of files names MULTI-GD +================================================================ +less than 1M 1 GB0XXX#.040 GB0XXX# +1M 1 GB1XXX#.040 GB1XXX# +2M 1 GB2XXX#.040/60 GB2XXX# +3M 1 GB3XXX#.030 GB2XXX# +4M 1 GB4XXX#.048 GB4XXX# +6M 1 GB6XXX#.058 GB6XXX# +8M 1 GB8XXX#.058 GB8XXX# + + +Contrary to popular belief the Game Doctor SF3/SF6/SF7 *does* +use a 512 byte header like the SWC, but it also accepts +headerless files. +A header is necessary when things like SRAM size must be made +known to the Game Doctor. The Game Doctor also uses specially +designed filenames to distinguish between multi files. + +Usually, the filename is in the format of: SFXXYYYZ.078 + +Where SF means Super Famicom, XX refers to the size of the +image in Mbit. If the size is only one character (i.e. 2, 4 or +8 Mbit) then no leading "0" is inserted. + +YYY refers to a catalogue number in Hong Kong shops +identifying the game title. (0 is Super Mario World, 1 is F- +Zero, etc). I was told that the Game Doctor copier produces a +random number when backing up games. + +Z indicates a multi file. Like XX, if it isn't used it's +ignored. + +A would indicate the first file, B the second, etc. I am told +078 is not needed, but is placed on the end of the filename by +systems in Asia. + +e.g. The first 8 Mbit file of Donkey Kong Country (assuming it +is cat. no. 475) would look like: SF32475A.078 +*/ + +#ifdef USE_PARALLEL +#endif // USE_PARALLEL + +// the following four functions are used by non-transfer code in genesis.c +extern void mgd_interleave (unsigned char **buffer, int size); +extern void mgd_deinterleave (unsigned char **buffer, int data_size, + int buffer_size); +extern int fread_mgd (void *buffer, size_t size, size_t number, FILE *fh); +extern int q_fread_mgd (void *buffer, size_t start, size_t len, + const char *filename); +extern void mgd_make_name (const char *filename, int console, int size, + char *name); +extern void mgd_write_index_file (void *ptr, int n_names); + +#define MGD_HEADER_START 0 +#define MGD_HEADER_LEN 512 +#endif // MGD_H diff --git a/ucon64/2.0/src/backup/msg.c b/ucon64/2.0/src/backup/msg.c new file mode 100644 index 0000000..3bab807 --- /dev/null +++ b/ucon64/2.0/src/backup/msg.c @@ -0,0 +1,250 @@ +/* +msg.c - Magic Super Griffin support for uCON64 + +Copyright (c) 2003 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "misc/file.h" +#include "misc/misc.h" +#include "ucon64.h" +#include "ucon64_misc.h" +#include "ffe.h" +#include "msg.h" + + +const st_getopt2_t msg_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Magic Super Griffin/MSG"/*"1993/1994/1995/19XX Front Far East/FFE http://www.front.com.tw"*/, + NULL + }, +#ifdef USE_PARALLEL + { + "xmsg", 0, 0, UCON64_XMSG, + NULL, "send/receive ROM to/from Magic Super Griffin/MSG; " OPTION_LONG_S "port=PORT\n" + "receives automatically when ROM does not exist", + &ucon64_wf[WF_OBJ_PCE_DEFAULT_STOP_NO_SPLIT_NO_ROM] + }, +#endif + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +#ifdef USE_PARALLEL + +#define BUFFERSIZE 8192 // don't change, only 8192 works! + + +static void set_header (unsigned char *buffer); +static int check (unsigned char *info_block, int index1, int index2, int size); + + +#if BUFFERSIZE < 512 +#error receive_rom_info() expects BUFFERSIZE to be at least 512 bytes. +#endif +void +set_header (unsigned char *buffer) +{ + int n, m = 0; + unsigned char sizes[] = "\x10\x20\x30\x30\x40\x40\x60\x80"; + + for (n = 0; n < 128; n++) + { + ffe_send_command (5, (unsigned short) n, 0); + buffer[n] = ffe_send_command1 (0xa0a0); + wait2 (1); + if (buffer[n] != 0xff) + m = 1; + } + if (m == 0) + { // no cartridge in Magic Super Griffin + buffer[0] = 0; + return; + } + + m = 0; + if (!check (buffer, 0, 0x40, 0x20)) + m |= 2; + if (check (buffer, 0, 0x20, 0x20)) + { + if (!check (buffer, 0, 0x10, 0x10)) + m |= 1; + } + else + { + m |= 4; + if (!check (buffer, 0x40, 0x60, 0x20)) + m |= 1; + } + + memset (buffer, 0, MSG_HEADER_LEN); + buffer[0] = sizes[m]; + if (buffer[0] == 0x30) + buffer[1] = 1; + buffer[8] = 0xaa; + buffer[9] = 0xbb; + buffer[10] = 2; +} + + +int +check (unsigned char *info_block, int index1, int index2, int size) +{ + int n; + + for (n = 0; n < size; n++) + if (info_block[n + index1] != info_block[n + index2]) + return 0; + + return 1; +} + + +int +msg_read_rom (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer; + int size, blocksleft, blocksdone = 0, bytesreceived = 0, emu_mode_select; + time_t starttime; + + ffe_init_io (parport); + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + set_header (buffer); + if (buffer[0] == 0) + { + fprintf (stderr, "ERROR: There is no cartridge present in the Magic Super Griffin\n"); + fclose (file); + remove (filename); + exit (1); + } + blocksleft = buffer[0]; + size = buffer[0] * 8192; + printf ("Receive: %d Bytes (%.4f Mb)\n", size, (float) size / MBIT); + + fwrite (buffer, 1, MSG_HEADER_LEN, file); // write header + emu_mode_select = buffer[1]; + + ffe_send_command (5, 0, 0); + ffe_send_command0 (0xbff0, 0); + + printf ("Press q to abort\n\n"); + + starttime = time (NULL); + while (blocksleft > 0) + { + ffe_send_command (5, (unsigned short) blocksdone, 0); + if (emu_mode_select && blocksdone >= 32) + ffe_send_command (5, (unsigned short) (blocksdone + 32), 0); + ffe_receive_block (0xa000, buffer, BUFFERSIZE); + // vgs aborts if the checksum doesn't match the data, we let the user decide + blocksleft--; + blocksdone++; + fwrite (buffer, 1, BUFFERSIZE, file); + + bytesreceived += BUFFERSIZE; + ucon64_gauge (starttime, bytesreceived, size); + ffe_checkabort (2); + } + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + + +int +msg_write_rom (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer; + int bytesread, bytessend = 0, blocksdone = 0, emu_mode_select, size; + time_t starttime; + + ffe_init_io (parport); + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + size = fsizeof (filename) - MSG_HEADER_LEN; + printf ("Send: %d Bytes (%.4f Mb)\n", size, (float) size / MBIT); + + fread (buffer, 1, MSG_HEADER_LEN, file); + emu_mode_select = buffer[1]; // this byte is needed later + + ffe_send_command0 (0xe008, 0); + printf ("Press q to abort\n\n"); + + starttime = time (NULL); + while ((bytesread = fread (buffer, 1, BUFFERSIZE, file))) + { + ffe_send_command (5, (unsigned short) blocksdone, 0); + ffe_send_block (0x8000, buffer, bytesread); + blocksdone++; + + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, size); + ffe_checkabort (2); + } + + if (emu_mode_select & 1) + ffe_send_command (4, 0xff00, 0); + else + ffe_send_command (4, 0xff03, 0); + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/msg.h b/ucon64/2.0/src/backup/msg.h new file mode 100644 index 0000000..a5a46cc --- /dev/null +++ b/ucon64/2.0/src/backup/msg.h @@ -0,0 +1,46 @@ +/* +msg.h - Magic Super Griffin support for uCON64 + +Copyright (c) 2003 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef MSG_H +#define MSG_H + +extern const st_getopt2_t msg_usage[]; + +// For the header format, see ffe.h +typedef struct st_msg_header +{ + unsigned char size; + unsigned char emulation; + unsigned char pad[6]; + unsigned char id1; + unsigned char id2; + unsigned char type; + unsigned char pad2[501]; +} st_msg_header_t; + +#define MSG_HEADER_START 0 +#define MSG_HEADER_LEN (sizeof (st_msg_header_t)) + +#ifdef USE_PARALLEL +extern int msg_read_rom (const char *filename, unsigned int parport); +extern int msg_write_rom (const char *filename, unsigned int parport); +#endif + +#endif // MSG_H diff --git a/ucon64/2.0/src/backup/pce-pro.c b/ucon64/2.0/src/backup/pce-pro.c new file mode 100644 index 0000000..6e0cfaa --- /dev/null +++ b/ucon64/2.0/src/backup/pce-pro.c @@ -0,0 +1,204 @@ +/* +pce-pro.c - PCE-PRO flash card programmer support for uCON64 + +Copyright (c) 2004 dbjh + +Based on Delphi source code by ToToTEK Multi Media. Information in that source +code has been used with permission. However, ToToTEK Multi Media explicitly +stated that the information in that source code may be freely distributed. + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include "misc/parallel.h" +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "misc/file.h" +#include "misc/misc.h" +#include "ucon64.h" +#include "ucon64_misc.h" +#include "tototek.h" +#include "pce-pro.h" + + +const st_getopt2_t pcepro_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "PCE-PRO flash card programmer"/*"2004 ToToTEK Multi Media http://www.tototek.com"*/, + NULL + }, +#ifdef USE_PARALLEL + { + "xpce", 0, 0, UCON64_XPCE, + NULL, "send/receive ROM to/from PCE-PRO flash card programmer\n" OPTION_LONG_S "port=PORT\n" + "receives automatically (32 Mbits) when ROM does not exist", + &ucon64_wf[WF_OBJ_PCE_DEFAULT_STOP_NO_SPLIT_NO_ROM] + }, +#endif + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +#ifdef USE_PARALLEL + +static void eep_reset (void); +static void write_rom_by_byte (int *addr, unsigned char *buf); +static void write_rom_by_page (int *addr, unsigned char *buf); + + +void +eep_reset (void) +{ + ttt_rom_enable (); + ttt_write_mem (0x000000, 0xff); // reset EEP + ttt_write_mem (0x200000, 0xff); // reset EEP + ttt_rom_disable (); +} + + +void +write_rom_by_byte (int *addr, unsigned char *buf) +{ + int x; + + for (x = 0; x < 0x4000; x++) + { + ttt_write_byte_sharp (*addr, buf[*addr & 0x3fff]); + (*addr)++; + } +} + + +void +write_rom_by_page (int *addr, unsigned char *buf) +{ + int x; + + for (x = 0; x < 0x200; x++) + { + ttt_write_page_rom (*addr, buf); + (*addr) += 0x20; + } +} + + +int +pce_read_rom (const char *filename, unsigned int parport, int size) +{ + FILE *file; + unsigned char buffer[0x100]; + int blocksleft, address = 0; + time_t starttime; + void (*read_block) (int, unsigned char *) = ttt_read_rom_w; // ttt_read_rom_b + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + ttt_init_io (parport); + + printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + + blocksleft = size >> 8; + eep_reset (); + ttt_rom_enable (); + if (read_block == ttt_read_rom_w) + ttt_set_ai_data (6, 0x94); // rst=1, wei=0(dis.), rdi=0(dis.), inc mode, rom_CS + starttime = time (NULL); + while (blocksleft-- > 0) + { + read_block (address, buffer); // 0x100 bytes read + fwrite (buffer, 1, 0x100, file); + address += 0x100; + if ((address & 0x3fff) == 0) + ucon64_gauge (starttime, address, size); + } + // original code doesn't call ttt_rom_disable() when byte-size function is + // used (ttt_read_rom_b() calls it) + if (read_block == ttt_read_rom_w) + ttt_rom_disable (); + + fclose (file); + ttt_deinit_io (); + + return 0; +} + + +int +pce_write_rom (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char buffer[0x4000]; + int size, fsize, address = 0, bytesread, bytessend = 0; + time_t starttime; + void (*write_block) (int *, unsigned char *) = write_rom_by_page; // write_rom_by_byte + (void) write_rom_by_byte; + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + ttt_init_io (parport); + + size = fsize = fsizeof (filename); + if (fsize == 4 * MBIT) + size += 2 * MBIT; + printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + + eep_reset (); + if (ttt_get_id () != 0xb0d0) + { + fputs ("ERROR: PCE-PRO flash card (programmer) not detected\n", stderr); + fclose (file); + ttt_deinit_io (); + exit (1); + } + + starttime = time (NULL); + eep_reset (); + while ((bytesread = fread (buffer, 1, 0x4000, file))) + { + if ((address & 0xffff) == 0) + ttt_erase_block (address); + write_block (&address, buffer); + if ((fsize == 3 * MBIT) && (address == 2 * MBIT)) + address += 2 * MBIT; + if ((fsize == 4 * MBIT) && (address == 4 * MBIT)) + fseek (file, 2 * MBIT, SEEK_SET); + + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, size); + } + + fclose (file); + ttt_deinit_io (); + + return 0; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/pce-pro.h b/ucon64/2.0/src/backup/pce-pro.h new file mode 100644 index 0000000..98a6868 --- /dev/null +++ b/ucon64/2.0/src/backup/pce-pro.h @@ -0,0 +1,35 @@ +/* +pce-pro.h - PCE-PRO flash card programmer support for uCON64 + +Copyright (c) 2004 dbjh + +Based on Delphi source code by ToToTEK Multi Media. Information in that source +code has been used with permission. However, ToToTEK Multi Media explicitly +stated that the information in that source code may be freely distributed. + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef PCE_PRO_H +#define PCE_PRO_H + +extern const st_getopt2_t pcepro_usage[]; + +#ifdef USE_PARALLEL +extern int pce_read_rom (const char *filename, unsigned int parport, int size); +extern int pce_write_rom (const char *filename, unsigned int parport); +#endif + +#endif diff --git a/ucon64/2.0/src/backup/pl.c b/ucon64/2.0/src/backup/pl.c new file mode 100644 index 0000000..1de4120 --- /dev/null +++ b/ucon64/2.0/src/backup/pl.c @@ -0,0 +1,782 @@ +/* +pl.c - Pocket Linker support for uCON64 + +Copyright (c) 2004 Walter van Niftrik +Partly based on PokeLink - Copyright (c) Dark Fader / BlackThunder + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "misc/misc.h" +#include "misc/parallel.h" +#include "misc/file.h" +#include "ucon64.h" +#include "ucon64_misc.h" +#include "pl.h" + + +const st_getopt2_t pl_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Pocket Linker"/*"19XX Bung Enterprises Ltd"*/, + NULL + }, +#ifdef USE_PARALLEL + { + "xpl", 0, 0, UCON64_XPL, + NULL, "send/receive ROM to/from Pocket Linker; " OPTION_LONG_S "port=PORT\n" + "receives automatically when ROM does not exist", + &ucon64_wf[WF_OBJ_NGP_DEFAULT_STOP_NO_ROM] + }, + { + "xpli", 0, 0, UCON64_XPLI, + NULL, "show information about inserted cartridge; " OPTION_LONG_S "port=PORT", + &ucon64_wf[WF_OBJ_NGP_STOP_NO_ROM] + }, + { + "xplm", 0, 0, UCON64_XPLM, + NULL, "try to enable EPP mode, default is SPP mode", + &ucon64_wf[WF_OBJ_NGP_SWITCH] + }, +#endif // USE_PARALLEL + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +#ifdef USE_PARALLEL + +// flash unlock addresses with chip select +#define fx0002aa(A) (((A) & 0x200000) | 0x0002aa) +#define fx000555(A) (((A) & 0x200000) | 0x000555) +#define fx000aaa(A) (((A) & 0x200000) | 0x000aaa) +#define fx002aaa(A) (((A) & 0x200000) | 0x002aaa) +#define fx005555(A) (((A) & 0x200000) | 0x005555) + +#define set_data_read outportb (port_a, 0); +#define set_data_write outportb (port_a, 1); +#define reset_port outportb (port_a, 4); +#define clear_timeout outportb (port_9, 1); + +#define CMD_READ 0xf0 +#define CMD_INFO 0x90 +#define CMD_ERASE 0x80 + +#define TYPE_BW 0x00 +#define TYPE_COLOR 0x01 +#define TYPE_MULTI 0x02 + +static short int port_8, port_9, port_a, port_b, port_c; +static parport_mode_t port_mode; +static int current_ai; +static unsigned char ai_value[4]; + + +static void +epp_write_data (unsigned char data) +{ + outportb (port_c, data); +} + + +static void +spp_write_data (unsigned char data) +{ + outportb (port_8, data); + outportb (port_a, 3); + outportb (port_a, 1); +} + + +static void +write_data (unsigned char data) +{ + ai_value[current_ai] = data; + if (port_mode == UCON64_SPP) + spp_write_data (data); + else + epp_write_data (data); +} + + +static unsigned char +epp_read_data (void) +{ + return inportb (port_c); +} + + +static unsigned char +spp_read_data (void) +{ + unsigned char byte; + + outportb (port_a, 2); + byte = (inportb (port_9) >> 3) & 0x0f; + outportb (port_a, 6); + byte |= (inportb (port_9) << 1) & 0xf0; + outportb (port_a, 0); + + return byte; +} + + +static unsigned char +read_data (void) +{ + if (port_mode == UCON64_SPP) + return spp_read_data (); + else + return epp_read_data (); +} + + +static void +epp_set_ai (unsigned char ai) +{ + current_ai = ai; + outportb (port_b, ai); +} + + +static void +spp_set_ai (unsigned char ai) +{ + current_ai = ai; + outportb (port_8, ai); + outportb (port_a, 9); + outportb (port_a, 1); +} + + +static void +set_ai (unsigned char ai) +{ + if (port_mode == UCON64_SPP) + spp_set_ai (ai); + else + epp_set_ai (ai); +} + + +static void +epp_set_ai_data (unsigned char ai, unsigned char data) +{ + epp_set_ai (ai); + epp_write_data (data); +} + + +static void +spp_set_ai_data (unsigned char ai, unsigned char data) +{ + spp_set_ai (ai); + spp_write_data (data); +} + + +static void +set_ai_data (unsigned char ai, unsigned char data) +{ + if (port_mode == UCON64_SPP) + spp_set_ai_data (ai, data); + else + epp_set_ai_data (ai, data); +} + + +static void +init_port (void) +{ +#ifndef USE_PPDEV + clear_timeout +#endif + set_data_write + set_ai_data (2, 0); +} + + +static void +end_port (void) +{ + set_data_write + set_ai_data (2, 0); + reset_port +} + + +static int +detect_linker (void) +{ + init_port (); + set_data_write + set_ai_data (1, 0x12); + set_ai_data (0, 0x34); + set_ai (1); + set_data_read + if (read_data () != 0x12) + return 0; + set_data_write + set_ai (0); + set_data_read + if (read_data () != 0x34) + return 0; + end_port (); + return 1; +} + + +static void +select_address (unsigned int addr, int inc) +{ + unsigned char data = (((addr >> 14) & 0x3c) | ((addr >> 13) & 0x80) | + (inc ? 0x01 : 0x00)); // a[20..16], auto-increment + if (data != ai_value[2]) + set_ai_data (2, data); + set_ai_data (1, (unsigned char) ((addr >> 8) & 0xff)); // a[15..8] + set_ai_data (0, (unsigned char) (addr & 0xff)); // a[7..0] +} + + +static void +write_address_data (unsigned int addr, unsigned char data) +{ + select_address (addr, 0); + set_ai_data (3, data); +} + + +static void +send_command (unsigned char cmd) +{ + set_data_write + write_address_data (0x5555, 0xaa); + write_address_data (0x2aaa, 0x55); + write_address_data (0x5555, cmd); +} + + +static void +reset_read (void) +{ + send_command (CMD_READ); +} + + +static unsigned char +read_ai3_data (void) +{ + set_ai (3); + set_data_read + return read_data (); +} + + +static int +detect_chip (void) +{ + int ai; + unsigned char info[4]; + + reset_read (); + reset_read (); + reset_read (); + send_command (CMD_INFO); + for (ai = 0; ai < 4; ai++) + { + set_data_write + select_address (ai, 0); + info[ai] = read_ai3_data (); + } + reset_read (); + if (((info[0] & 0x90) == 0x90) && (info[2] == 0x01) && (info[3] & 0x80)) + return 1; + else + return 0; +} + + +static void +select_chip (unsigned int addr) +{ + set_data_write + set_ai_data (2, 2); + set_ai_data (3, (unsigned char) ((addr & 0x200000) ? 1 : 2)); + set_ai_data (2, 0); +} + + +static unsigned int +cart_size (void) +{ + select_chip (0x000000); + if (!detect_chip ()) + return 0; // no cartridge found + select_chip (0x200000); + if (!detect_chip ()) + return 0x200000; // 16 megabit + return 0x400000; // 32 megabit +} + + +static void +read_blocks (unsigned int addr, unsigned char *buf, int blocks) +{ + int block, i, offset = 0; + + select_chip (addr); + for (block = 0; block < blocks; block++) + { + set_data_write + select_address (addr | (block << 8), 1); + set_ai (3); + set_data_read + for (i = 0; i < 0x100; i++) + buf[offset++] = read_data (); + } +} + + +static int +is_erased (unsigned char *buf, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len; i++) + if (buf[i] != 0xff) + return 0; + + return 1; +} + + +static int +is_header (unsigned char *buf) +{ + char msg[0x1d]; + + strncpy (msg, (char *) buf, 0x1c); + msg[0x1c] = '\0'; + + if (strstr (msg, "COPYRIGHT") || strstr (msg, "SNK") || + strstr (msg, "LICENSED") || strstr (msg, "CORPORATION")) + return 1; // header found + + return 0; // other data found +} + + +static int +same_header (unsigned char *header, unsigned char *buf) +{ + return (!memcmp (header, buf, 0x100)); +} + + +static unsigned int +game_info (unsigned int cart_size, char name[13], int *type) +{ + unsigned char header[0x100]; + unsigned char buf[0x8000]; + + select_chip (0x000000); + read_blocks (0x000000, header, 1); + + if (!is_header (header)) + return 0; // no game found + + if (name) + { + strncpy (name, (char *) (header + 0x24), 12); + name[12] = '\0'; + } + + if (type) + { + if (strstr (name, "Multi")) + *type = TYPE_MULTI; + else if (header[0x23] == 0x10) + *type = TYPE_COLOR; + else + *type = TYPE_BW; + } + + read_blocks (0x080000, buf, 128); + if ((same_header (buf, header)) || is_erased (buf, 0x8000)) + return 0x080000; // 4 megabit + + read_blocks (0x100000, buf, 128); + if ((same_header (buf, header)) || is_erased (buf, 0x8000)) + return 0x100000; // 8 megabit + + if (cart_size == 0x400000) + { + read_blocks (0x200000, buf, 128); + if (is_erased (buf, 0x8000)) + return 0x200000; // 16 megabit + return 0x400000; // 32 megabit + } + return 0x200000; // 16 megabit +} + + +static void +deinit_io (void) +{ + end_port (); +} + + +static void +init_io (unsigned int port) +{ +#ifndef USE_PPDEV + outportb ((unsigned short) (port_8 + 0x402), 0); // Set EPP mode for ECP chipsets +#endif + + port_8 = port; + port_9 = port + 1; + port_a = port + 2; + port_b = port + 3; + port_c = port + 4; + + parport_print_info (); + + if (ucon64.parport_mode == UCON64_EPP && port_8 != 0x3bc) + port_mode = UCON64_EPP; // if port == 0x3bc => no EPP available + else + port_mode = UCON64_SPP; + + if (!detect_linker ()) + { + port_mode = UCON64_SPP; + if (!detect_linker ()) + { + fputs ("ERROR: Pocket Linker not found or not turned on\n", stderr); + deinit_io (); + exit (1); + } + } + + // If we get here, a Pocket Linker was detected + if (port_mode == UCON64_EPP) + puts ("Pocket Linker found. EPP found"); + else + puts ("Pocket Linker found. EPP not found or not enabled - SPP used"); +} + + +static void +set_address (unsigned int addr, int inc) +{ + set_ai_data (0, (unsigned char) (addr & 0xff)); // a[7..0] + set_ai_data (1, (unsigned char) ((addr >> 8) & 0xff)); // a[15..8] + set_ai_data (2, 0x02); // latch chip select + set_ai_data (3, (unsigned char) ~(1 << (addr >> 21))); // a[23..21] + set_ai_data (2, (unsigned char) ((((addr >> 16) & 0x0f) << 2) | // a[20..16], auto-increment + (((addr >> 20) & 0x01) << 7) | + (inc ? 0x01 : 0x00))); + set_ai (3); +} + + +static int +program (unsigned int addr, unsigned char data, int retries) +{ + int to = 10000; + + set_data_write + set_address (fx005555 (addr), 0); // program byte + write_data (0xaa); + set_address (fx002aaa (addr), 0); + write_data (0x55); + set_address (fx005555 (addr), 0); + write_data (0xa0); + set_address (addr, 0); + write_data (data); + + set_data_read + while (to--) + { + unsigned char s = read_data (); + if ((s & 128) == 0) + return 0; // ok + if (s & 32) + { + int s = read_data (); + if ((s & 128) == 0) + return 0; // ok + if (data == read_data ()) + return 0; // ok + } + } + + set_data_write + set_address (addr, 0); + set_data_read + if (data == read_data ()) + return 0; // ok + if (retries == 0) + return 1; // error + return program (addr, data, retries - 1); +} + + +static int +write_block (unsigned int addr, unsigned char *buf) +{ + int count; + select_address (addr, 1); + for (count = 0; count < 0x100; count++) + { + if (buf[count] == 0xff) + continue; // nothing to write + if (program (addr + count, buf[count], 3)) + { + select_address (addr + count, 0); + set_data_read + fprintf (stderr, "\nERROR: Programming failed at 0x%06x (w:0x%02x, " + "r:0x%02x)\n", addr + count, buf[count], read_data ()); + return 1; + } + } + return 0; +} + + +#if 0 +static int +wait_erased (void) +{ + int i = 0; + unsigned char cur_byte, prev_byte; + + prev_byte = read_ai3_data () & 0x40; + while (++i < 0x7ffffff) + { + cur_byte = read_data () & 0x40; + if (cur_byte == prev_byte) + return 0; // ok + prev_byte = cur_byte; + } + return 1; // erase error +} + + +static int +erase_chip (void) +{ + reset_read (); + send_command (CMD_ERASE); + write_address_data (0x5555, 0xaa); + write_address_data (0x2aaa, 0x55); + write_address_data (0x5555, 0x10); + return wait_erased (); +} + + +static int +erase_cart (unsigned int size) +{ + unsigned int addr; + unsigned char buf[0x8000]; + + for (addr = 0x000000; addr < size; addr += 0x200000) + { + select_chip (addr); + if (erase_chip ()) + { + fprintf (stderr, "ERROR: Erase chip %d failed\n", addr >> 21); + return 1; + } + + read_blocks (0x000000, buf, 128); + if (!is_erased (buf, 0x8000)) + { + fprintf (stderr, "ERROR: Erase chip %d verify failed\n", addr >> 21); + return 1; + } + } +} +#endif + + +static int +erase (void) +{ + unsigned int addr; + + for (addr = 0; addr < 0x400000; addr += 0x200000) + { + set_data_write + set_address (fx005555 (addr), 0); + write_data (0xaa); + set_address (fx002aaa (addr), 0); + write_data (0x55); + set_address (fx005555 (addr), 0); + write_data (0x80); + set_address (fx005555 (addr), 0); + write_data (0xaa); + set_address (fx002aaa (addr), 0); + write_data (0x55); + set_address (fx005555 (addr), 0); + write_data (0x10); + + set_data_read + while (~read_data () & 0x80) // wait for completion + ; + } + return 0; +} + + +int +pl_read_rom (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned int blocksleft, c_size, size, address = 0; + unsigned char buffer[0x8000]; + time_t starttime; + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + init_io (parport); + + c_size = cart_size (); + + if (c_size == 0x00) // Check for cartridge + { + fputs ("ERROR: No cartridge detected\n", stderr); + deinit_io (); + exit (1); + } + + size = game_info (c_size, NULL, NULL); + + if (size == 0x00) + { + fputs ("ERROR: No game detected\n", stderr); + deinit_io (); + exit (1); + } + + reset_read (); + + printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + + blocksleft = size >> 15; + starttime = time (NULL); + + while (blocksleft-- > 0) + { + read_blocks (address, buffer, 128); + fwrite (buffer, 1, 0x8000, file); + address += 0x8000; + ucon64_gauge (starttime, address, size); + } + + fclose (file); + deinit_io (); + + return 0; +} + + +int +pl_write_rom (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned int size, bytesread, bytessent = 0, address = 0; + unsigned char buffer[0x100]; + time_t starttime; + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + init_io (parport); + size = fsizeof (filename); + + erase (); + reset_read (); + + printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + + starttime = time (NULL); + + while ((bytesread = fread (buffer, 1, 0x100, file))) + { + if (write_block (address, buffer)) + break; // write failed + bytessent += bytesread; + address += 0x100; + ucon64_gauge (starttime, bytessent, size); + } + + fclose (file); + deinit_io (); + + return 0; +} + + +int +pl_info (unsigned int parport) +{ + unsigned int c_size, g_size; + int g_type; + char g_name[13]; + + init_io (parport); + + c_size = cart_size (); + if (c_size == 0) + { + printf ("No cartridge found\n"); + deinit_io (); + return 0; + } + + g_size = game_info (c_size, g_name, &g_type); + + if (g_size == 0) + printf ("No game found\n"); + else + { + printf ("Game name: \"%s\"\n", g_name); + printf ("Game type: %s\n", (g_type == TYPE_COLOR) ? "Color" : "B&W"); + printf ("Game size: %d Bytes (%.4f Mb)\n", g_size, (float) g_size / MBIT); + } + deinit_io (); + + return 0; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/pl.h b/ucon64/2.0/src/backup/pl.h new file mode 100644 index 0000000..9db0534 --- /dev/null +++ b/ucon64/2.0/src/backup/pl.h @@ -0,0 +1,33 @@ +/* +pl.h - Pocket Linker support for uCON64 + +Copyright (c) 2004 Walter van Niftrik +Partly based on PokeLink - Copyright (c) Dark Fader / BlackThunder + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef PL_H +#define PL_H + +extern const st_getopt2_t pl_usage[]; + +#ifdef USE_PARALLEL +extern int pl_info (unsigned int parport); +extern int pl_read_rom (const char *filename, unsigned int parport); +extern int pl_write_rom (const char *filename, unsigned int parport); +#endif + +#endif diff --git a/ucon64/2.0/src/backup/psxpblib.c b/ucon64/2.0/src/backup/psxpblib.c new file mode 100644 index 0000000..1ca2d5f --- /dev/null +++ b/ucon64/2.0/src/backup/psxpblib.c @@ -0,0 +1,1068 @@ +/* + * + * PSX Peripheral Bus Library v1.4 17/01/00 Richard Davies + * + * + * Revision History: + * v1.4 - 17/01/00 Win32 / Win32 DLL support, rewrite and bug fixes + * v1.3 - 21/12/99 Linux support and bug fixes + * v1.1 - 26/09/99 Minor Controller detection improvements. + * v1.0 - 17/07/99 Initial release (based on PSXTest v1.1 by me). + * + * see psxpblib.h for details. + * + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#ifdef HAVE_DIRENT_H +#include +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "misc/misc.h" +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "psxpblib.h" +#include "misc/parallel.h" + + +#ifdef USE_PARALLEL + +static unsigned char psx_parallel_out_0 = 0xff; +static unsigned char psx_parallel_out_2 = 0x00; + + +/* + * + * sets clock for conport connected to parallel port base + * + */ +void +psx_clk (int base, int conport, int on) +{ + if (conport == 8) + { + if (on) + { + /* set controller clock high */ + psx_parallel_out_2 |= LPT_STR; + } + else + { + /* set controller clock low */ + psx_parallel_out_2 &= ~LPT_STR; + } + + psx_outportb (base + 2, psx_parallel_out_2); + } + else + { + if (on) + { + /* set controller clock high */ + psx_parallel_out_0 |= LPT_D2; + } + else + { + /* set controller clock low */ + psx_parallel_out_0 &= ~LPT_D2; + } + + psx_outportb (base + 0, psx_parallel_out_0); + } +} + + +/* + * + * sets att for conport connected to parallel port base + * + */ +void +psx_att (int base, int conport, int on) +{ + /* bits 3-7 base + 0 (pins 5 to 9 parallel port) */ + const int power = LPT_D3 | LPT_D4 | LPT_D5 | LPT_D6 | LPT_D7; + /* bits 1-6 base + 0 (pins 3, 5, 6, 7 and 8 parallel port) */ + unsigned char att_array[] = + { LPT_D1, LPT_D1, LPT_D3, LPT_D4, LPT_D5, LPT_D6, LPT_D7 }; + unsigned char att; + + if (conport == 8) + { + if (on) + { + /* set controller att high */ + psx_parallel_out_2 |= LPT_INI; + } + else + { + /* set controller att low */ + psx_parallel_out_2 &= ~LPT_INI; + } + + psx_outportb (base + 2, psx_parallel_out_2); + } + else + { + /* powers up all parallel port driven conports */ + psx_parallel_out_0 |= power; + + att = att_array[conport - 1]; + + if (on) + { + /* set controller att high */ + psx_parallel_out_0 |= att; + } + else + { + /* set controller att low */ + psx_parallel_out_0 &= ~att; + } + + psx_outportb (base + 0, psx_parallel_out_0); + } +} + + +/* + * + * sets command for conport connected to parallel port base + * + */ +void +psx_cmd (int base, int conport, int on) +{ + if (conport == 8) + { + if (on) + { + /* set controller cmd high */ + psx_parallel_out_2 |= LPT_AUT; + } + else + { + /* set controller cmd low */ + psx_parallel_out_2 &= ~LPT_AUT; + } + + psx_outportb (base + 2, psx_parallel_out_2); + } + else + { + if (on) + { + /* set controller cmd high */ + psx_parallel_out_0 |= LPT_D0; + } + else + { + /* set controller cmd low */ + psx_parallel_out_0 &= ~LPT_D0; + } + + psx_outportb (base + 0, psx_parallel_out_0); + } +} + + +/* + * + * tests data for conport connected to parallel port base, returns 1 if high + * + */ +int +psx_dat (int base, int conport) +{ + if (conport == 2) + { + if (psx_inportb (base + 1) & LPT_SEL) + { + return 1; + } + else + { + return 0; + } + } + else + { + if (psx_inportb (base + 1) & LPT_ACK) + { + return 1; + } + else + { + return 0; + } + } +} + + +/* + * + * tests ack for conport connected to parallel port base, returns 1 if high + * + */ +int +psx_ack (int base, int conport) +{ + if (conport == 2) + { + if (psx_inportb (base + 2) & LPT_ERR) + { + return 1; + } + else + { + return 0; + } + } + else if (conport == 8) + { + if (psx_inportb (base + 1) & LPT_SEL) + { + return 1; + } + else + { + return 0; + } + } + else + { + if (psx_inportb (base + 1) & LPT_PAP) + { + return 1; + } + else + { + return 0; + } + } +} + + +/* + * + * wait for delay * (psx_outportb() execution time) + * + */ +void +psx_delay (int base, int delay) +{ + int i; + + for (i = 0; i < delay; i++) + { + psx_outportb (base + 0, psx_parallel_out_0); + } +} + + +/* + * + * send byte as a command to conport connected to parallel port base + * assumes clock high and the attention for conport + * + */ +unsigned char +psx_sendbyte (int base, int conport, int delay, unsigned char byte, int wait) +{ + int i; + unsigned char data; + + data = 0; + + /* for each bit in byte */ + for (i = 0; i < 8; i++) + { + psx_delay (base, delay); + psx_cmd (base, conport, byte & (1 << i)); /* send the (i+1)th bit of byte to any listening controller */ + psx_clk (base, conport, 0); /* clock low */ + psx_delay (base, delay); + data |= (psx_dat (base, conport) ? (1 << i) : 0); /* read the (i+1)th bit of data from conport */ + psx_clk (base, conport, 1); /* clock high */ + } + + /* wait for controller ack */ + for (i = 0; wait && i < 10240 && psx_ack (base, conport); i++) + ; + + return data; +} + + +/* + * + * sets clock high and gets the attention of conport, use before psx_sendbyte() + * + */ +void +psx_sendinit (int base, int conport, int delay) +{ +// psx_obtain_io_permission (base);// uCON64 already enabled access to I/O ports + psx_att (base, conport, 1); /* set att on for conport */ + psx_clk (base, conport, 1); /* clock high */ + psx_cmd (base, conport, 1); /* set command on for conport */ + psx_delay (base, delay); + psx_delay (base, delay); + psx_delay (base, delay); + psx_delay (base, delay); + psx_delay (base, delay); + psx_att (base, conport, 0); /* set att off for conport */ + psx_delay (base, delay); + psx_delay (base, delay); + psx_delay (base, delay); + psx_delay (base, delay); +} + + +/* + * + * use after psx_sendbyte() + * + */ +void +psx_sendclose (int base, int conport, int delay) +{ + psx_delay (base, delay); + psx_delay (base, delay); + psx_att (base, conport, 1); /* set att on for conport */ + psx_cmd (base, conport, 1); /* set command on for conport */ + psx_clk (base, conport, 1); /* clock high */ + psx_delay (base, delay); +} + + +/* + * + * send string as a series of commands to conport connected to parallel port base + * + */ +void +psx_sendstring (int base, int conport, int delay, int string[]) +{ + int i; + + psx_sendinit (base, conport, delay); + + /* for each byte in string */ + for (i = 0; string[i + 1] != -1; i++) + { + /* send byte i and wait for conport ack */ + psx_sendbyte (base, conport, delay, (unsigned char) string[i], 0); + psx_delay (base, delay); + } + + /* send the last byte in string and don't wait for ack */ + psx_sendbyte (base, conport, delay, (unsigned char) string[i], 0); + + psx_sendclose (base, conport, delay); +} + + +/* + * + * tests for the presence of a controller on conport:tap connected to base + * returns the type if present, otherwise -1 + * + */ +int +psx_controller_detect (int base, int conport, int tap, int delay) +{ + unsigned char ack; + int type, length; + + psx_sendinit (base, conport, delay); + + psx_sendbyte (base, conport, delay, (unsigned char) tap, 0); + psx_delay (base, delay); + psx_delay (base, delay); + psx_delay (base, delay); + psx_delay (base, delay); + ack = psx_sendbyte (base, conport, delay, 0x42, 0); + psx_delay (base, delay); + + psx_sendclose (base, conport, delay); + + type = (ack & 0xf0) >> 4; + length = 2 * (ack & 0x0f); + + /* check the controller has a legal packet length */ + if (!((length > 0) && (length < PSX_MAX_DATA))) + return -1; + + /* check the controller has a legal id */ + if (!((type > 0) && (type < 0x0f) && (type != 0x08))) + return -1; + + return type; +} + + +/* + * + * reads a controller on conport:tap connected to base returns the data + * if present, otherwise -1 + * + */ +PSX_CON_BUF * +psx_controller_read (int base, int conport, int tap, int delay) +{ + unsigned char ack; + int i; + static PSX_CON_BUF psx_con_buf; + + psx_sendinit (base, conport, delay); + + psx_sendbyte (base, conport, delay, (unsigned char) tap, 0); + psx_delay (base, delay); + psx_delay (base, delay); + psx_delay (base, delay); + psx_delay (base, delay); + ack = psx_sendbyte (base, conport, delay, 0x42, 0); + psx_delay (base, delay); + + psx_con_buf.type = (ack & 0xf0) >> 4; + psx_con_buf.length = 2 * (ack & 0x0f); + + /* check the controller has a legal packet length */ + if (!((psx_con_buf.length > 0) && (psx_con_buf.length < PSX_MAX_DATA))) + { + psx_sendclose (base, conport, delay); + return NULL; + } + + /* check the controller has a legal id */ + if (! + ((psx_con_buf.type > 0) && (psx_con_buf.type < 0x0f) + && (psx_con_buf.type != 0x08))) + { + psx_sendclose (base, conport, delay); + return NULL; + } + + psx_sendbyte (base, conport, delay, 0x00, 0); + psx_delay (base, delay); + for (i = 0; i < psx_con_buf.length; i++) + { + psx_con_buf.data[i] = psx_sendbyte (base, conport, delay, 0x00, 0); + psx_delay (base, delay); + } + psx_sendclose (base, conport, delay); + + return &psx_con_buf; +} + + +/* + * + * sends force feedback/shock init command sequence to conport:tap on port base + * (also initialises crash protection for some controllers) + * + */ +void +psx_controller_vinit (int base, int conport, int tap, int delay) +{ + int i, vibrate_init_string[3][11] = + { + {tap, 0x43, 0x00, 0x01, 0x00, 0x01, -1}, + {tap, 0x4d, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x01, -1}, + {tap, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -1}, + }; + + for (i = 0; i < 3; i++) + { + psx_delay (base, delay); + psx_delay (base, delay); + psx_delay (base, delay); + psx_sendstring (base, conport, delay, vibrate_init_string[i]); + } +} + + +/* + * + * sends the dual shock command sequence to conport:tap on port base + * + */ +void +psx_controller_vshock (int base, int conport, int tap, int delay, int shock, + int rumble) +{ + int dualshock_string[7] = { tap, 0x42, 0x00, shock, rumble, 0x01, -1 }; + + psx_controller_vinit (base, conport, tap, delay); + + psx_delay (base, delay); + psx_delay (base, delay); + psx_delay (base, delay); + psx_sendstring (base, conport, delay, dualshock_string); +} + + +/* + * + * Reads a single frame (128 bytes) from Memory Card on conport base:tap + * + */ +unsigned char * +psx_memcard_read_frame (int base, int conport, int tap, int delay, int frame) +{ + int i, xor_val; + static unsigned char data[128], c_data; + unsigned char cmd_rstring_hdr[4] = { (0x80 + (unsigned char) tap), 0x52, 0x00, 0x00 }, + chk_rstring_hdr[2] = { 0x5a, 0x5d }, + cmd_rstring_adr[2] = { (frame >> 8) & 0xff, frame & 0xff }, + chk_rstring_ack[1] = { 0x5c }, + chk_rstring_sfl[1] = { 0x5d }, + chk_rstring_efl[1] = { 0x47 }; + + psx_sendinit (base, conport, delay); + + /* send header */ + for (i = 0; i < 2; i++) + { + psx_sendbyte (base, conport, delay, cmd_rstring_hdr[i], 0); + psx_delay (base, delay); + psx_delay (base, delay); + } + + for (; i < 4; i++) + { + c_data = psx_sendbyte (base, conport, delay, cmd_rstring_hdr[i], 0); + psx_delay (base, delay); + psx_delay (base, delay); + + if (c_data != chk_rstring_hdr[i - 2]) + { + psx_sendclose (base, conport, delay); + return NULL; + } + } + + /* send read address */ + for (i = 0; i < 2; i++) + { + psx_sendbyte (base, conport, delay, cmd_rstring_adr[i], 0); + psx_delay (base, delay); + } + + /* receive command ack (have to wait for this) */ + c_data = psx_sendbyte (base, conport, delay, 0x00, 1); + + if (c_data != chk_rstring_ack[0]) + { + psx_sendclose (base, conport, delay); + return NULL; + } + + /* receive start of data flag */ + i = 0; + + while (c_data != chk_rstring_sfl[0]) + { + c_data = psx_sendbyte (base, conport, delay, 0x00, 0); + psx_delay (base, delay); + + i++; + + if (i > 255) + { + psx_sendclose (base, conport, delay); + return NULL; + } + } + + /* receive read address */ + for (i = 0; i < 2; i++) + { + c_data = psx_sendbyte (base, conport, delay, 0x00, 0); + psx_delay (base, delay); + + if (c_data != cmd_rstring_adr[i]) + { + psx_sendclose (base, conport, delay); + return NULL; + } + } + + /* receive data */ + for (i = 0; i < 128; i++) + { + data[i] = psx_sendbyte (base, conport, delay, 0x00, 0); + psx_delay (base, delay); + } + + /* receive xor */ + c_data = psx_sendbyte (base, conport, delay, 0x00, 0); + psx_delay (base, delay); + + /* test xor */ + xor_val = 0; + + xor_val ^= cmd_rstring_adr[0]; + xor_val ^= cmd_rstring_adr[1]; + + for (i = 0; i < 128; i++) + xor_val ^= data[i]; + + if (xor_val != c_data) + { + psx_sendclose (base, conport, delay); + return NULL; + } + + /* receive end of data flag */ + c_data = psx_sendbyte (base, conport, delay, 0x00, 0); + psx_delay (base, delay); + + if (c_data != chk_rstring_efl[0]) + { + psx_sendclose (base, conport, delay); + return NULL; + } + + psx_sendclose (base, conport, delay); + + return data; +} + + +/* + * + * Writes a single frame (128 bytes) to Memory Card on conport base:tap + * + */ +int +psx_memcard_write_frame (int base, int conport, int tap, int delay, int frame, + unsigned char *data_f) +{ + int i, xor_val; + unsigned char c_data, + cmd_wstring_hdr[4] = { (0x80 + (unsigned char) tap), 0x57, 0x00, 0x00 }, + chk_wstring_hdr[2] = { 0x5a, 0x5d }, + cmd_wstring_adr[2] = { (frame >> 8) & 0xff, frame & 0xff }, + chk_wstring_emk[2] = { 0x5c, 0x5d }, + chk_wstring_efl[1] = { 0x47 }; + + psx_sendinit (base, conport, delay); + + /* send header (have to wait for this) */ + for (i = 0; i < 2; i++) + { + psx_sendbyte (base, conport, delay, cmd_wstring_hdr[i], 1); + } + + for (; i < 4; i++) + { + c_data = psx_sendbyte (base, conport, delay, cmd_wstring_hdr[i], 1); + + if (c_data != chk_wstring_hdr[i - 2]) + { + psx_sendclose (base, conport, delay); + return -1; + } + } + + /* send write address */ + for (i = 0; i < 2; i++) + { + psx_sendbyte (base, conport, delay, cmd_wstring_adr[i], 0); + psx_delay (base, delay); + } + /* send data */ + for (i = 0; i < 128; i++) + { + psx_sendbyte (base, conport, delay, data_f[i], 0); + psx_delay (base, delay); + } + + /* calculate xor */ + xor_val = 0; + + xor_val ^= cmd_wstring_adr[0]; + xor_val ^= cmd_wstring_adr[1]; + + for (i = 0; i < 128; i++) + xor_val ^= data_f[i]; + + /* send xor */ + psx_sendbyte (base, conport, delay, (unsigned char) xor_val, 0); + psx_delay (base, delay); + + /* receive end mark */ + for (i = 0; i < 2; i++) + { + c_data = psx_sendbyte (base, conport, delay, 0x00, 1); + + if (c_data != chk_wstring_emk[i]) + { + psx_sendclose (base, conport, delay); + return -1; + } + } + + /* receive end of data flag */ + c_data = psx_sendbyte (base, conport, delay, 0x00, 1); + + if (c_data != chk_wstring_efl[0]) + { + psx_sendclose (base, conport, delay); + return -1; + } + + psx_sendclose (base, conport, delay); + + return (int) ((unsigned char) xor_val); +} + + +/* + * + * Reads a single block (64 frames) from Memory Card on conport base:tap + * + */ +unsigned char * +psx_memcard_read_block (int base, int conport, int tap, int delay, int block) +{ + int i, j; + static unsigned char data_b[8192], *data_f; + + for (i = 0; i < 64; i++) + { + data_f = + psx_memcard_read_frame (base, conport, tap, delay, (block * 64) + i); + + if (data_f != NULL) + { + for (j = 0; j < 128; j++) + data_b[(i * 128) + j] = data_f[j]; + } + else + { + return NULL; + } + } + + return data_b; +} + + +/* + * + * Writes a single block (64 frames) to Memory Card on conport base:tap + * + */ +int +psx_memcard_write_block (int base, int conport, int tap, int delay, int block, + unsigned char *data_b) +{ + int i, xor_val; + + for (i = 0; i < 64; i++) + { + xor_val = + psx_memcard_write_frame (base, conport, tap, delay, (block * 64) + i, + &(data_b[128 * i])); + + if (xor_val == -1) + { + return -1; + } + } + + return 1; +} + + +/* + * + * Reads the info associated with block from the directory + * + */ +PSX_MCB_INFO_DIR * +psx_mcb_read_dir (int base, int conport, int tap, int delay, int block) +{ + int i, xor_val; + unsigned char *data_f; + static PSX_MCB_INFO_DIR mcb_info_dir; + + mcb_info_dir.read = 1; + + /* check memory card state */ + data_f = psx_memcard_read_frame (base, conport, tap, delay, 0); + + if (data_f == NULL) + { + mcb_info_dir.read = 0; + return &mcb_info_dir; + } + + if ((data_f[0] != 'M') && (data_f[1] != 'C')) + { + mcb_info_dir.read = 0; + return &mcb_info_dir; + } + + for (i = 2; i < 127; i++) + { + if (data_f[i] != 0x00) + { + mcb_info_dir.read = 0; + return &mcb_info_dir; + } + } + + if (data_f[127] != 0x0e) + { + mcb_info_dir.read = 0; + return &mcb_info_dir; + } + + /* read block's directory */ + data_f = psx_memcard_read_frame (base, conport, tap, delay, block); + + if (data_f == NULL) + { + mcb_info_dir.read = 0; + return &mcb_info_dir; + } + + xor_val = 0; + + for (i = 0; i < 127; i++) + xor_val ^= data_f[i]; + + if (xor_val != data_f[127]) + { + mcb_info_dir.read = 0; + return &mcb_info_dir; + } + + mcb_info_dir.linktype = data_f[0] & 0x0f; + + /* Only test if first link */ + if (mcb_info_dir.linktype == PSX_MCB_LTYPE_FIRST) + { + if (data_f[10] != 'B') + { + mcb_info_dir.read = 0; + return &mcb_info_dir; + } + } + + mcb_info_dir.state = (data_f[0] >> 4) & 0x0f; + + mcb_info_dir.bytes = (data_f[4] << 16) + (data_f[5] << 8) + data_f[6]; + + mcb_info_dir.next = data_f[8]; /* 0 to 14 */ + + mcb_info_dir.territory = data_f[11]; /* E, A or I */ + + for (i = 0; i < 10; i++) + mcb_info_dir.code[i] = (char) data_f[12 + i]; + + mcb_info_dir.code[i] = '\0'; + + for (i = 0; i < 8; i++) + mcb_info_dir.filename[i] = data_f[22 + i]; + + mcb_info_dir.filename[i] = '\0'; + + return &mcb_info_dir; +} + + +/* + * + * Reads the info associated with block from it's data + * + */ +PSX_MCB_INFO_DAT * +psx_mcb_read_dat (int base, int conport, int tap, int delay, int block) +{ + int i, j; + unsigned char *data_f; + static PSX_MCB_INFO_DAT mcb_info_dat; + + mcb_info_dat.read = 1; + + if ((block < 1) || (block > 15)) + { + mcb_info_dat.read = 0; + return &mcb_info_dat; + } + + data_f = + psx_memcard_read_frame (base, conport, tap, delay, (block * 64) + 0); + + if (!data_f) + { + mcb_info_dat.read = 0; + return &mcb_info_dat; + } + + if ((data_f[0] != 'S') || (data_f[1] != 'C')) + { + mcb_info_dat.read = 0; + return &mcb_info_dat; + } + + mcb_info_dat.icon_valid = (data_f[2] >> 4) & 0x0f; + mcb_info_dat.icon_frames = data_f[2] & 0x0f; + mcb_info_dat.blocks = data_f[3]; + + /* bodged character set conversion */ + j = 0; + for (i = 0; i < 91; i += 2) + { + if (data_f[4 + i] != 0x00) + { + if (data_f[4 + i] == 0x81) + { + if (data_f[5 + i] == 0x7c) + mcb_info_dat.name[j] = '-'; + else if (data_f[5 + i] == 0x40) + mcb_info_dat.name[j] = ' '; + else if (data_f[5 + i] == 0x46) + mcb_info_dat.name[j] = ':'; + else if (data_f[5 + i] == 0x5e) + mcb_info_dat.name[j] = '/'; + else if (data_f[5 + i] == 0x49) + mcb_info_dat.name[j] = '!'; + else if (data_f[5 + i] == 0x93) + mcb_info_dat.name[j] = '%'; + else if (data_f[5 + i] == 0x68) + mcb_info_dat.name[j] = '\"'; + else if (data_f[5 + i] == 0x44) + mcb_info_dat.name[j] = '.'; + else if (data_f[5 + i] == 0x6d) + mcb_info_dat.name[j] = '['; + else if (data_f[5 + i] == 0x6e) + mcb_info_dat.name[j] = ']'; + else if (data_f[5 + i] == 0x69) + mcb_info_dat.name[j] = '('; + else if (data_f[5 + i] == 0x6a) + mcb_info_dat.name[j] = ')'; + else + mcb_info_dat.name[j] = '?'; + } + else if (data_f[4 + i] == 0x82) + { + if ((data_f[5 + i] > 0x4e) && (data_f[5 + i] < 0x80)) + mcb_info_dat.name[j] = data_f[5 + i] - 0x1f; + else if ((data_f[4 + i] > 0x80) && (data_f[5 + i] < 0x9a)) + mcb_info_dat.name[j] = data_f[5 + i] - 0x20; + else + mcb_info_dat.name[j] = '?'; + } + else + { + mcb_info_dat.name[j] = data_f[4 + i]; + + j++; + if (data_f[5 + i] != 0x00) + mcb_info_dat.name[j] = data_f[5 + i]; + else + { + mcb_info_dat.name[j] = '\0'; + i = 91; + } + } + } + else + { + mcb_info_dat.name[j] = '\0'; + i = 91; + } + j++; + } + + return &mcb_info_dat; +} + + +/* + * + * Merges the info associated with block from the directory and it's data + * + */ +PSX_MCB_INFO * +psx_mcb_info_merge (PSX_MCB_INFO_DIR mcb_info_dir, + PSX_MCB_INFO_DAT mcb_info_dat, PSX_MCB_INFO * mcb_info) +{ + mcb_info->read = 1; + + if (!mcb_info_dir.read) + { + mcb_info->read = 0; + return mcb_info; + } + + if ((mcb_info_dir.linktype == PSX_MCB_LTYPE_FIRST) && (!mcb_info_dat.read)) + { + mcb_info->read = 0; + return mcb_info; + } + + strcpy (mcb_info->filename, mcb_info_dir.filename); + strcpy (mcb_info->code, mcb_info_dir.code); + mcb_info->territory = mcb_info_dir.territory; + mcb_info->bytes = mcb_info_dir.bytes; + mcb_info->state = mcb_info_dir.state; + mcb_info->linktype = mcb_info_dir.linktype; + mcb_info->next = mcb_info_dir.next; + if (mcb_info_dir.linktype == PSX_MCB_LTYPE_FIRST) + { + strcpy (mcb_info->name, mcb_info_dat.name); + mcb_info->blocks = mcb_info_dat.blocks; + mcb_info->icon_valid = mcb_info_dat.icon_valid; + mcb_info->icon_frames = mcb_info_dat.icon_frames; + } + else + { + mcb_info->name[0] = '\0'; + mcb_info->blocks = 0; + mcb_info->icon_valid = 0; + mcb_info->icon_frames = 0; + } + + return mcb_info; +} + + +/* + * + * Reads the info associated with block from the directory and it's data + * + */ +PSX_MCB_INFO * +psx_mcb_read_info (int base, int conport, int tap, int delay, int block) +{ + PSX_MCB_INFO_DIR *mcb_info_dir; + PSX_MCB_INFO_DAT *mcb_info_dat; + static PSX_MCB_INFO mcb_info; + + mcb_info_dir = psx_mcb_read_dir (base, conport, tap, delay, block); + mcb_info_dat = psx_mcb_read_dat (base, conport, tap, delay, block); + return psx_mcb_info_merge (*mcb_info_dir, *mcb_info_dat, &mcb_info); +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/psxpblib.h b/ucon64/2.0/src/backup/psxpblib.h new file mode 100644 index 0000000..d87f967 --- /dev/null +++ b/ucon64/2.0/src/backup/psxpblib.h @@ -0,0 +1,208 @@ +/* + * + * PSX Peripheral Bus Library v1.4 17/01/00 Richard Davies + * + * + * + * Revision History: + * v1.4 - 17/01/00 Win32 / Win32 DLL support and bug fixes + * v1.3 - 21/12/99 Linux support and bug fixes + * v1.1 - 26/09/99 Minor Controller detection improvements. + * v1.0 - 17/07/99 Initial release (based on PSXTest v1.1 by me). + * + * see psxpblib.txt for details. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef USE_PARALLEL + +// outportb() and inportb() are only present in uCON64 if USE_PARALLEL is defined +#define psx_outportb(P, B) outportb((unsigned short) (P), (unsigned char) (B)) +#define psx_inportb(P) inportb((unsigned short) (P)) + +#ifdef __MSDOS__ +#include +#include +#endif + +#define LPT1_BASE 0x378 +#define LPT2_BASE 0x278 +#define LPT3_BASE 0x3bc + +#define LPT_D0 0x01 /* pin 2 */ +#define LPT_D1 0x02 /* pin 3 */ +#define LPT_D2 0x04 /* pin 4 */ +#define LPT_D3 0x08 /* pin 5 */ +#define LPT_D4 0x10 /* pin 6 */ +#define LPT_D5 0x20 /* pin 7 */ +#define LPT_D6 0x40 /* pin 8 */ +#define LPT_D7 0x80 /* pin 9 */ +#define LPT_AUT 0x08 /* pin 14 */ +#define LPT_SEL 0x10 /* pin 13 */ +#define LPT_PAP 0x20 /* pin 12 */ +#define LPT_ACK 0x40 /* pin 10 */ +#define LPT_ERR 0x02 /* pin 15 */ +#define LPT_INI 0x04 /* pin 16 */ +#define LPT_STR 0x08 /* pin 1 */ + +#define PSX_MAX_CONPORTS 8 +#define PSX_MAX_TAPS 4 +#define PSX_MAX_DELAY 10 + +#define PSX_MAX_DATA 30 /* maximum possible length of controller packet in bytes */ + + /* JAP code EUR code Name */ + +#define PSX_MOUSE 1 /* SCPH-1030 SCPH-???? Mouse */ +#define PSX_NEGCON 2 /* SLPH-0001 SLEH-0003 Namco neGcon + SLPH-0007 Nasca Pachinco Handle (untested); Twist = Twist, TW = B + SLPH-0015 ? Volume Controller (untested); Rotation = Twist, A = Start, B = A + Puchi Carat paddle controller (not working!); Rotation = Twist, A = Start, B = A + SLPH-???? SLEH-0005 MadKatz Steering Wheel (twitchy) */ +#define PSX_KONAMIGUN 3 /* SLPH-???? SLPH-???? Konami Lightgun (untested) */ +#define PSX_DIGITAL 4 /* SCPH-1010 SCPH 1080 E Controller + SCPH-1110 SCPH-???? Analog Joystick - Digital Mode + SCPH-???? SCPH-1180 E Analog Controller - Digital Mode + SCPH-1150 SCPH-1200 E Dual Shock Analog Controller - Digital Mode + SLPH-???? SLEH-0011 Ascii Resident Evil Pad + SLPH-???? SLEH-0004 Namco Arcade Stick (untested) + Twin Stick + Blaze Pro-Shock Arcade Stick */ +#define PSX_ANALOG_GREEN 5 /* SCPH-1110 SCPH-???? Analog Joystick - Analog Mode (untested) + SCPH-???? SCPH-1180 E Analog Controller - Analog Green Mode */ +#define PSX_GUNCON 6 /* SLPH-???? SLEH-0007 Namco G-con45 */ +#define PSX_ANALOG_RED 7 /* SCPH-1150 SCPH-1200 E Dual Shock Analog Controller - Analog Red Mode */ + /* SCPH-???? SCPH-1180 E Analog Controller - Analog Red Mode */ +#define PSX_JOGCON 14 /* SLPH-???? SLEH-0020 Namco Jogcon */ +/*#define PSX_MULTITAP 17 SCPH-1070 SCPH-1070 E Multi tap */ + +#define PSX_MCB_STATE_OK 0x05 +#define PSX_MCB_STATE_DELETED 0x0a +#define PSX_MCB_STATE_RESERVED 0x0f + +#define PSX_MCB_LTYPE_NONE 0x00 +#define PSX_MCB_LTYPE_FIRST 0x01 +#define PSX_MCB_LTYPE_MIDDLE 0x02 +#define PSX_MCB_LTYPE_LAST 0x03 +#define PSX_MCB_LTYPE_RESERVED 0x0f + +#define PSX_MCB_ICON_VALID 0x01 + + + +typedef struct +{ + unsigned char type; + unsigned char length; + unsigned char data[PSX_MAX_DATA]; +} PSX_CON_BUF; + +typedef struct +{ + unsigned char read; + char filename[9]; + char code[11]; + char territory; /* E, A or I */ + int bytes; + unsigned char state; /* PSX_MCB_STAT_* or unknown */ + unsigned char linktype; /* PSX_MCB_LTYPE_* or unknowm */ + unsigned char next; /* 0 to 14 */ +} PSX_MCB_INFO_DIR; + +typedef struct +{ + unsigned char read; + char name[92]; + unsigned char blocks; + unsigned char icon_valid; + unsigned char icon_frames; +} PSX_MCB_INFO_DAT; + +typedef struct +{ + unsigned char scanned; + unsigned char read; + char name[92]; + char filename[9]; + char code[11]; + char territory; + unsigned char state; + unsigned char blocks; + int bytes; + unsigned char linktype; + unsigned char next; + unsigned char icon_valid; + unsigned char icon_frames; +} PSX_MCB_INFO; + + +/* sets clock for conport connected to parallel port base */ +void psx_clk (int base, int conport, int on); +/* sets att for conport connected to parallel port base */ +void psx_att (int base, int conport, int on); +/* sets command for conport connected to parallel port base */ +void psx_cmd (int base, int conport, int on); +/* tests data for conport connected to parallel port base, returns 1 if high */ +int psx_dat (int base, int conport); +/* tests ack for conport connected to parallel port base, returns 1 if high */ +int psx_ack (int base, int conport); +/* wait for delay * (outportb() execution time) */ +void psx_delay (int base, int delay); + +/* send byte as a command to conport connected to parallel port base + * assumes clock high and the attention of conport */ +unsigned char psx_sendbyte (int base, int conport, int delay, + unsigned char byte, int wait); +/* get io permissions under linux*/ +int psx_obtain_io_permission (int base); +/* sets clock high and gets the attention of conport, use before psx_sendbyte() */ +void psx_sendinit (int base, int conport, int delay); +/* use after psx_sendbyte() */ +void psx_sendclose (int base, int conport, int delay); +/* send string as a series of commands to conport connected to parallel port base */ +void psx_sendstring (int base, int conport, int delay, int string[]); + +/* tests for the presence of a controller on conport:tap connected to base + * returns the type if present, otherwise -1 */ +int psx_controller_detect (int base, int conport, int tap, int delay); +/* reads a controller on conport:tap connected to base returns the data + * if present, otherwise -1 */ +PSX_CON_BUF *psx_controller_read (int base, int conport, int tap, int delay); +/* sends force feedback/shock init command sequence to conport:tap on port base + * (also initialises crash protection for some controllers) */ +void psx_controller_vinit (int base, int conport, int tap, int delay); +/* sends the dual shock command sequence to conport:tap on port base */ +void psx_controller_vshock (int base, int conport, int tap, int delay, + int shock, int rumble); + +/* Reads a single frame (128 bytes) from Memory Card on conport base:tap */ +unsigned char *psx_memcard_read_frame (int base, int conport, int tap, int delay, + int frame); +/* Writes a single frame (128 bytes) to Memory Card on conport base:tap */ +int psx_memcard_write_frame (int base, int conport, int tap, int delay, + int frame, unsigned char *data_f); +/* Reads a single block (64 frames) from Memory Card on conport base:tap */ +unsigned char *psx_memcard_read_block (int base, int conport, int tap, int delay, + int block); +/* Writes a single block (64 frames) to Memory Card on conport base:tap */ +int psx_memcard_write_block (int base, int conport, int tap, int delay, + int block, unsigned char *data_b); + +/* Reads the info associated with block from the directory */ +PSX_MCB_INFO_DIR *psx_mcb_read_dir (int base, int conport, int tap, int delay, + int block); +/* Prints the info associated with block from it's data */ +PSX_MCB_INFO_DAT *psx_mcb_read_dat (int base, int conport, int tap, int delay, + int block); +/* Merges the info associated with block from the directory and it's data */ +PSX_MCB_INFO *psx_mcb_info_merge (PSX_MCB_INFO_DIR mcb_info_dir, + PSX_MCB_INFO_DAT mcb_info_dat, + PSX_MCB_INFO * mcb_info); +/* Reads the info associated with block from the directory and it's data */ +PSX_MCB_INFO *psx_mcb_read_info (int base, int conport, int tap, int delay, + int block); + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/sflash.c b/ucon64/2.0/src/backup/sflash.c new file mode 100644 index 0000000..b92c069 --- /dev/null +++ b/ucon64/2.0/src/backup/sflash.c @@ -0,0 +1,371 @@ +/* +sflash.h - Super Flash flash card programmer support for uCON64 + +Copyright (c) 2004 JohnDie + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include "misc/parallel.h" +#include "misc/itypes.h" +#include "misc/misc.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "misc/file.h" +#include "ucon64.h" +#include "ucon64_misc.h" +#include "tototek.h" +#include "sflash.h" + + +const st_getopt2_t sflash_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Super Flash flash card programmer"/*"2004 ToToTEK Multi Media http://www.tototek.com"*/, + NULL + }, +#ifdef USE_PARALLEL + { + "xsf", 0, 0, UCON64_XSF, + NULL, "send/receive ROM to/from Super Flash flash card programmer\n" OPTION_LONG_S "port=PORT\n" + "receives automatically (64 Mbits) when ROM does not exist", + &ucon64_wf[WF_OBJ_SNES_DEFAULT_STOP_NO_SPLIT_NO_ROM] + }, + { + "xsfs", 0, 0, UCON64_XSFS, + NULL, "send/receive SRAM to/from Super Flash flash card programmer\n" OPTION_LONG_S "port=PORT\n" + "receives automatically when SRAM does not exist", + &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM] + }, +#endif + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +#ifdef USE_PARALLEL + +static void eep_reset (void); +static void write_rom_by_byte (int *addr, unsigned char *buf); +static void write_rom_by_page (int *addr, unsigned char *buf); +static void write_ram_by_byte (int *addr, unsigned char *buf); +static void write_ram_by_page (int *addr, unsigned char *buf); + + +void +eep_reset (void) +{ + ttt_rom_enable (); + ttt_write_mem (0x400000, 0xff); // reset EEP chip 2 + ttt_write_mem (0, 0xff); // reset EEP chip 1 + ttt_write_mem (0x600000, 0xff); // reset EEP chip 2 + ttt_write_mem (0x200000, 0xff); // reset EEP chip 1 + ttt_rom_disable (); +} + + +void +write_rom_by_byte (int *addr, unsigned char *buf) +{ + int x; + + for (x = 0; x < 0x4000; x++) + { + ttt_write_byte_intel (*addr, buf[*addr & 0x3fff]); + (*addr)++; + } +} + + +void +write_rom_by_page (int *addr, unsigned char *buf) +{ + int x; + + for (x = 0; x < 0x200; x++) + { + ttt_write_page_rom (*addr, buf); + (*addr) += 0x20; + } +} + + +void +write_ram_by_byte (int *addr, unsigned char *buf) +{ + int x, i = *addr & 0x3fff; + + for (x = 0; x < 0x4000; x++, i = (i + 1) & 0x3fff) + { + ttt_write_byte_ram (*addr, buf[i]); + (*addr)++; + } +} + + +void +write_ram_by_page (int *addr, unsigned char *buf) +{ + int x; + + for (x = 0; x < 0x40; x++) + { + ttt_write_page_ram (*addr, buf); + (*addr) += 0x100; + } +} + + +int +sf_read_rom (const char *filename, unsigned int parport, int size) +{ + FILE *file; + unsigned char buffer[0x100]; + int blocksleft, address = 0; + time_t starttime; + void (*read_block) (int, unsigned char *) = ttt_read_rom_w; // ttt_read_rom_b + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + ttt_init_io (parport); + + printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + + blocksleft = size >> 8; + eep_reset (); + ttt_rom_enable (); + if (read_block == ttt_read_rom_w) + ttt_set_ai_data (6, 0x94); // rst=1, wei=0(dis.), rdi=0(dis.), inc mode, rom_CS + starttime = time (NULL); + while (blocksleft-- > 0) + { + read_block (address, buffer); // 0x100 bytes read + if (read_block == ttt_read_rom_b) + ucon64_bswap16_n (buffer, 0x100); + fwrite (buffer, 1, 0x100, file); + address += 0x100; + if ((address & 0x3fff) == 0) + ucon64_gauge (starttime, address, size); + } + // ttt_read_rom_b() calls ttt_rom_disable() + if (read_block == ttt_read_rom_w) + ttt_rom_disable (); + + fclose (file); + ttt_deinit_io (); + + return 0; +} + + +int +sf_write_rom (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char buffer[0x4000], game_table[0x80]; + int game_no, romsize, size, address = 0, bytesread, bytessend = 0; + time_t starttime; + void (*write_block) (int *, unsigned char *) = write_rom_by_page; // write_rom_by_byte + (void) write_rom_by_byte; + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + ttt_init_io (parport); + + size = fsizeof (filename); + printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + + eep_reset (); + if (ttt_get_id () != 0x8917) // Intel 64J3 + { + fputs ("ERROR: Super Flash flash card (programmer) not detected\n", stderr); + fclose (file); + ttt_deinit_io (); + exit (1); + } + + starttime = time (NULL); + + // Erase last block now, because we have to write to it anyway later. Erasing + // it later could erase part of a game. + ttt_erase_block (0x7e0000); + + fseek (file, 0x4000, SEEK_SET); + bytesread = fread (game_table, 1, 0x80, file); + if (bytesread != 0x80) + { + fputs ("ERROR: Could not read game table from file\n", stderr); + fclose (file); + ttt_deinit_io (); + return 0; + } + + fseek (file, 0x8000, SEEK_SET); + + for (game_no = 0; game_no < 4; game_no++) + { + if (game_table[game_no * 0x20] == 0) + continue; + + romsize = game_table[game_no * 0x20 + 0x1f] * 0x8000; + + switch (game_table[game_no * 0x20 + 0x1d] & 0x60) + { + case 0x00: + address = 0x000000; + break; + case 0x40: + address = 0x200000; + break; + case 0x20: + address = 0x400000; + break; + case 0x60: + address = 0x600000; + break; + // no default case because we handled all possible cases + } + + eep_reset (); + + while (romsize && (bytesread = fread (buffer, 1, 0x4000, file))) + { + if ((address & 0x1ffff) == 0) + ttt_erase_block (address); + if (address < 0x7f8000) // We mustn't write to the loader space + write_block (&address, buffer); + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, size); + romsize -= 0x4000; + } + } + + fseek (file, 0, SEEK_SET); + romsize = 0x8000; + address = 0x7f8000; + while (romsize && (bytesread = fread (buffer, 1, 0x4000, file))) + { + write_block (&address, buffer); + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, size); + romsize -= 0x4000; + } + + fclose (file); + ttt_deinit_io (); + + return 0; +} + + +int +sf_read_sram (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char buffer[0x100]; + int blocksleft, address, bytesreceived = 0, size; + time_t starttime; + void (*read_block) (int, unsigned char *) = ttt_read_ram_w; // ttt_read_ram_w + + address = 0xfe0000; // SRAM is stored at 0xfe0000 + size = 0x020000; // SRAM size is 1024 kbit + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + + ttt_init_io (parport); + printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + +// ttt_ram_enable (); // The next ttt_set_ai_data also enables ram access + ttt_set_ai_data (6, 0x98); // Enable cartridge access, auto address increment + + blocksleft = size >> 8; + starttime = time (NULL); + while (blocksleft-- > 0) + { + read_block (address, buffer); // 0x100 bytes read + fwrite (buffer, 1, 0x100, file); + address += 0x100; + bytesreceived += 0x100; + if ((address & 0x3fff) == 0) + ucon64_gauge (starttime, bytesreceived, size); + } + + ttt_ram_disable (); + + fclose (file); + ttt_deinit_io (); + + return 0; +} + + +int +sf_write_sram (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char buffer[0x4000]; + int size, bytesread, bytessend = 0, address; + time_t starttime; + void (*write_block) (int *, unsigned char *) = write_ram_by_byte; // write_ram_by_page + (void) write_ram_by_page; + + size = fsizeof (filename); + address = 0xfe0000; + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + + ttt_init_io (parport); + printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + + ttt_ram_enable (); + + starttime = time (NULL); + while ((bytesread = fread (buffer, 1, 0x4000, file))) + { + write_block (&address, buffer); // 0x4000 bytes write + bytessend += bytesread; + if ((address & 0x3fff) == 0) + ucon64_gauge (starttime, bytessend, size); + } + + fclose (file); + ttt_deinit_io (); + + return 0; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/sflash.h b/ucon64/2.0/src/backup/sflash.h new file mode 100644 index 0000000..b8787ad --- /dev/null +++ b/ucon64/2.0/src/backup/sflash.h @@ -0,0 +1,33 @@ +/* +sflash.h - Super Flash flash card programmer support for uCON64 + +Copyright (c) 2004 JohnDie + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef SFLASH_H +#define SFLASH_H + +extern const st_getopt2_t sflash_usage[]; + +#ifdef USE_PARALLEL +extern int sf_read_rom (const char *filename, unsigned int parport, int size); +extern int sf_write_rom (const char *filename, unsigned int parport); +extern int sf_read_sram (const char *filename, unsigned int parport); +extern int sf_write_sram (const char *filename, unsigned int parport); +#endif + +#endif diff --git a/ucon64/2.0/src/backup/smc.c b/ucon64/2.0/src/backup/smc.c new file mode 100644 index 0000000..6b38f39 --- /dev/null +++ b/ucon64/2.0/src/backup/smc.c @@ -0,0 +1,407 @@ +/* +smc.c - Super Magic Card support for uCON64 + +Copyright (c) 2003 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include "misc/misc.h" +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "misc/file.h" +#include "ucon64.h" +#include "ucon64_misc.h" +#include "ffe.h" +#include "smc.h" + + +const st_getopt2_t smc_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Super Magic Card"/*"1993/1994/1995/19XX Front Far East/FFE http://www.front.com.tw"*/, + NULL + }, +#ifdef USE_PARALLEL + { + "xsmc", 0, 0, UCON64_XSMC, // send only + NULL, "send ROM (in FFE format) to Super Magic Card; " OPTION_LONG_S "port=PORT", + &ucon64_wf[WF_OBJ_NES_DEFAULT_STOP_NO_SPLIT] + }, + { + "xsmcr", 0, 0, UCON64_XSMCR, + NULL, "send/receive RTS data to/from Super Magic Card; " OPTION_LONG_S "port=PORT\n" + "receives automatically when RTS file does not exist", + &ucon64_wf[WF_OBJ_NES_STOP_NO_ROM] + }, +#endif + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +#ifdef USE_PARALLEL + +#define BUFFERSIZE 8192 + + +static int get_blocks1 (unsigned char *header); +static int get_blocks2 (unsigned char *header); +static int get_blocks3 (unsigned char *header); + + +int +get_blocks1 (unsigned char *header) +{ + if (header[7] == 0xaa) + return header[3]; + if (header[0] & 0x30) + return header[0] & 0x20 ? 32 : 16; // 0x10 => 16; 0x20/0x30 => 32 + else + switch (header[1] >> 5) + { + case 0: + case 4: + return 16; + case 1: + case 2: + case 3: + return 32; + default: // 5/6/7 + return 4; + } +} + + +int +get_blocks2 (unsigned char *header) +{ + if (header[0] & 0x30) + return header[0] & 0x10 ? 32 : 16; // 0x10/0x30 => 32; 0x20 => 16 + else + return 0; +} + + +int +get_blocks3 (unsigned char *header) +{ + if (header[7] == 0xaa) + return header[4]; + if (header[0] & 0x30) + return 0; + else + switch (header[1] >> 5) + { + default: // 0/1/2/3 + return 0; + case 4: + case 5: + return 4; + case 6: + return 2; + case 7: + return 1; + } +} + + +int +smc_write_rom (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer; + int bytesread, bytessend, size, offset, n_blocks1, n_blocks2, n_blocks3, n; + time_t starttime; + + ffe_init_io (parport); + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + ffe_send_command0 (0x4500, 2); + ffe_send_command0 (0x42fd, 0x20); + ffe_send_command0 (0x43fc, 0); + + fread (buffer, 1, SMC_HEADER_LEN, file); + + n_blocks1 = get_blocks1 (buffer); + n_blocks2 = get_blocks2 (buffer); + n_blocks3 = get_blocks3 (buffer); + + size = (n_blocks1 + n_blocks2 + n_blocks3) * 8 * 1024 + 8 + + (buffer[0] & SMC_TRAINER ? 512 : 0); + printf ("Send: %d Bytes (%.4f Mb)\n", size, (float) size / MBIT); + + ffe_send_block (0x5020, buffer, 8); // send "file control block" + bytessend = 8; + + if (buffer[1] >> 5 > 4) + offset = 12; + else + offset = 0; + + if (buffer[0] & SMC_TRAINER) // send trainer if present + { + fread (buffer, 1, 512, file); + ffe_send_block (0x600, buffer, 512); + bytessend += 512; + } + + printf ("Press q to abort\n\n"); + starttime = time (NULL); + + for (n = 0; n < n_blocks1; n++) + { + ffe_send_command0 (0x4507, (unsigned char) (n + offset)); + if ((bytesread = fread (buffer, 1, BUFFERSIZE, file)) == 0) + break; + ffe_send_block (0x6000, buffer, bytesread); + + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, size); + ffe_checkabort (2); + } + + for (n = 0; n < n_blocks2; n++) + { + ffe_send_command0 (0x4507, (unsigned char) (n + 32)); + if ((bytesread = fread (buffer, 1, BUFFERSIZE, file)) == 0) + break; + ffe_send_block (0x6000, buffer, bytesread); + + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, size); + ffe_checkabort (2); + } + + ffe_send_command0 (0x2001, 0); + + for (n = 0; n < n_blocks3; n++) + { + if (n == 0) + { + ffe_send_command0 (0x4500, 0x22); + ffe_send_command0 (0x42ff, 0x30); + if ((bytesread = fread (buffer, 1, BUFFERSIZE, file)) == 0) + break; + ffe_send_block (0x6000, buffer, bytesread); + } + else + { + int m; + + ffe_send_command0 (0x4500, 7); + for (m = 0; m < 8; m++) + ffe_send_command0 ((unsigned short) (0x4510 + m), (unsigned char) (n * 8 + m)); + if ((bytesread = fread (buffer, 1, BUFFERSIZE, file)) == 0) + break; + ffe_send_block2 (0, buffer, bytesread); + } + + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, size); + ffe_checkabort (2); + } + + for (n = 0x4504; n < 0x4508; n++) + ffe_send_command0 ((unsigned short) n, 0); + for (n = 0x4510; n < 0x451c; n++) + ffe_send_command0 ((unsigned short) n, 0); + + ffe_send_command (5, 1, 0); + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + + +int +smc_read_rts (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer; + int bytesreceived = 0, size, n; + time_t starttime; + + ffe_init_io (parport); + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + size = 0x68 + 4 * 1024 + 5 * 8 * 1024; + printf ("Receive: %d Bytes\n", size); + memset (buffer, 0, SMC_HEADER_LEN); + buffer[8] = 0xaa; + buffer[9] = 0xbb; + buffer[10] = 1; + + printf ("Press q to abort\n\n"); + starttime = time (NULL); + + ffe_send_command (5, 3, 0); + ffe_receive_block (0x5840, buffer + 0x100, 0x68); + fwrite (buffer, 1, SMC_HEADER_LEN, file); + bytesreceived += 0x68; + + ffe_send_command0 (0x4500, 0x32); + ffe_send_command0 (0x42ff, 0x30); + ffe_receive_block (0x6000, buffer, BUFFERSIZE / 2); // 0x1000 + fwrite (buffer, 1, BUFFERSIZE / 2, file); + + bytesreceived += BUFFERSIZE / 2; + ucon64_gauge (starttime, bytesreceived, size); + ffe_checkabort (2); + + for (n = 2; n <= 0x22; n += 0x20) + { + ffe_send_command0 (0x4500, (unsigned char) n); + ffe_receive_block (0x6000, buffer, BUFFERSIZE); // 0x2000 + fwrite (buffer, 1, BUFFERSIZE, file); + + bytesreceived += BUFFERSIZE; + ucon64_gauge (starttime, bytesreceived, size); + ffe_checkabort (2); + } + + for (n = 1; n <= 3; n++) + { + ffe_send_command0 (0x43fc, (unsigned char) n); + if (n == 1) + ffe_send_command0 (0x2001, 0); + ffe_receive_block2 (0, buffer, BUFFERSIZE); // 0x2000 + fwrite (buffer, 1, BUFFERSIZE, file); + + bytesreceived += BUFFERSIZE; + ucon64_gauge (starttime, bytesreceived, size); + ffe_checkabort (2); + } + + ffe_send_command0 (0x43fc, 0); + ffe_send_command0 (0x2001, 0x6b); + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + + +int +smc_write_rts (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer; + int bytessend = 0, size, n; + time_t starttime; + + ffe_init_io (parport); + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + size = 0x68 + 4 * 1024 + 5 * 8 * 1024; + printf ("Send: %d Bytes\n", size); + fread (buffer, 1, SMC_HEADER_LEN, file); + + printf ("Press q to abort\n\n"); + starttime = time (NULL); + + ffe_send_command (5, 3, 0); + ffe_send_block (0x5840, buffer + 0x100, 0x68); + bytessend += 0x68; + + ffe_send_command0 (0x4500, 0x32); + ffe_send_command0 (0x42ff, 0x30); + fread (buffer, 1, BUFFERSIZE / 2, file); + ffe_send_block (0x6000, buffer, BUFFERSIZE / 2); // 0x1000 + + bytessend += BUFFERSIZE / 2; + ucon64_gauge (starttime, bytessend, size); + ffe_checkabort (2); + + for (n = 2; n <= 0x22; n += 0x20) + { + ffe_send_command0 (0x4500, (unsigned char) n); + fread (buffer, 1, BUFFERSIZE, file); + ffe_send_block (0x6000, buffer, BUFFERSIZE); // 0x2000 + + bytessend += BUFFERSIZE; + ucon64_gauge (starttime, bytessend, size); + ffe_checkabort (2); + } + + for (n = 1; n <= 3; n++) + { + ffe_send_command0 (0x43fc, (unsigned char) n); + if (n == 1) + ffe_send_command0 (0x2001, 0); + fread (buffer, 1, BUFFERSIZE, file); + ffe_send_block2 (0, buffer, BUFFERSIZE); // 0x2000 + + bytessend += BUFFERSIZE; + ucon64_gauge (starttime, bytessend, size); + ffe_checkabort (2); + } + + ffe_send_command0 (0x43fc, 0); + ffe_send_command0 (0x2001, 0x6b); + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/smc.h b/ucon64/2.0/src/backup/smc.h new file mode 100644 index 0000000..e412410 --- /dev/null +++ b/ucon64/2.0/src/backup/smc.h @@ -0,0 +1,47 @@ +/* +smc.h - Super Magic Card support for uCON64 + +Copyright (c) 2003 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef SMC_H +#define SMC_H + +extern const st_getopt2_t smc_usage[]; + +typedef struct st_smc_header +{ + unsigned char emulation1; + unsigned char emulation2; + unsigned char pad[6]; + unsigned char id1; + unsigned char id2; + unsigned char type; + unsigned char pad2[501]; +} st_smc_header_t; + +#define SMC_HEADER_START 0 +#define SMC_HEADER_LEN (sizeof (st_smc_header_t)) +#define SMC_TRAINER 0x40 + +#ifdef USE_PARALLEL +extern int smc_write_rom (const char *filename, unsigned int parport); +extern int smc_read_rts (const char *filename, unsigned int parport); +extern int smc_write_rts (const char *filename, unsigned int parport); +#endif + +#endif // SMC_H diff --git a/ucon64/2.0/src/backup/smd.c b/ucon64/2.0/src/backup/smd.c new file mode 100644 index 0000000..8c84c79 --- /dev/null +++ b/ucon64/2.0/src/backup/smd.c @@ -0,0 +1,343 @@ +/* +smd.c - Super Magic Drive support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2001 - 2003 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include "misc/misc.h" +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "misc/file.h" +#include "ucon64.h" +#include "ucon64_misc.h" +#include "ffe.h" +#include "smd.h" + + +const st_getopt2_t smd_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Super Com Pro/Super Magic Drive/SMD"/*"19XX Front Far East/FFE http://www.front.com.tw"*/, + NULL + }, +#ifdef USE_PARALLEL + { + "xsmd", 0, 0, UCON64_XSMD, + NULL, "send/receive ROM to/from Super Magic Drive/SMD; " OPTION_LONG_S "port=PORT\n" + "receives automatically when ROM does not exist", + &ucon64_wf[WF_OBJ_GEN_DEFAULT_STOP_NO_SPLIT_NO_ROM] + }, + { + "xsmds", 0, 0, UCON64_XSMDS, + NULL, "send/receive SRAM to/from Super Magic Drive/SMD; " OPTION_LONG_S "port=PORT\n" + "receives automatically when SRAM does not exist", + &ucon64_wf[WF_OBJ_GEN_STOP_NO_ROM] + }, +#endif // USE_PARALLEL + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +// the following two functions are used by non-transfer code in genesis.c and sms.c +void +smd_interleave (unsigned char *buffer, int size) +// Convert binary data to the SMD interleaved format +{ + int count, offset; + unsigned char block[16384]; + + for (count = 0; count < size / 16384; count++) + { + memcpy (block, &buffer[count * 16384], 16384); + for (offset = 0; offset < 8192; offset++) + { + buffer[(count * 16384) + 8192 + offset] = block[offset << 1]; + buffer[(count * 16384) + offset] = block[(offset << 1) + 1]; + } + } +} + + +void +smd_deinterleave (unsigned char *buffer, int size) +{ + int count, offset; + unsigned char block[16384]; + + for (count = 0; count < size / 16384; count++) + { + memcpy (block, &buffer[count * 16384], 16384); + for (offset = 0; offset < 8192; offset++) + { + buffer[(count * 16384) + (offset << 1)] = block[offset + 8192]; + buffer[(count * 16384) + (offset << 1) + 1] = block[offset]; + } + } +} + + +#ifdef USE_PARALLEL + +#define BUFFERSIZE 16384 + + +int +smd_read_rom (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer, byte; + int size, blocksdone = 0, blocksleft, bytesreceived = 0; + time_t starttime; + + ffe_init_io (parport); + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + ffe_send_command (1, 0xdff1, 1); + byte = ffe_receiveb (); + if ((0x81 ^ byte) != ffe_receiveb ()) + printf ("received data is corrupt\n"); + + blocksleft = 8 * byte; + if (blocksleft == 0) + { + fprintf (stderr, "ERROR: There is no cartridge present in the Super Magic Drive\n"); + fclose (file); + remove (filename); + exit (1); + } + + memset (buffer, 0, SMD_HEADER_LEN); + buffer[0] = blocksleft; + buffer[1] = 3; + buffer[8] = 0xaa; + buffer[9] = 0xbb; + buffer[10] = 6; + fwrite (buffer, 1, SMD_HEADER_LEN, file); // write header + + size = blocksleft * 16384; // size in bytes for ucon64_gauge() below + printf ("Receive: %d Bytes (%.4f Mb)\n", size, (float) size / MBIT); + + wait2 (32); + ffe_send_command0 (0x2001, 0); + + printf ("Press q to abort\n\n"); + + starttime = time (NULL); + while (blocksleft > 0) + { + ffe_send_command (5, (unsigned short) blocksdone, 0); + ffe_receive_block (0x4000, buffer, BUFFERSIZE); + blocksdone++; + blocksleft--; + fwrite (buffer, 1, BUFFERSIZE, file); + + bytesreceived += BUFFERSIZE; + ucon64_gauge (starttime, bytesreceived, size); + ffe_checkabort (2); + } + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + + +int +smd_write_rom (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer; + int bytesread, bytessend, blocksdone = 0, fsize; + time_t starttime; + + ffe_init_io (parport); + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + fsize = fsizeof (filename); + printf ("Send: %d Bytes (%.4f Mb)\n", fsize, (float) fsize / MBIT); + + fread (buffer, 1, SMD_HEADER_LEN, file); + ffe_send_block (0xdc00, buffer, SMD_HEADER_LEN); // send header + bytessend = SMD_HEADER_LEN; + + ffe_send_command0 (0x2001, 0); + + printf ("Press q to abort\n\n"); + + starttime = time (NULL); + while ((bytesread = fread (buffer, 1, BUFFERSIZE, file))) + { + ffe_send_command (5, (unsigned short) blocksdone, 0); + ffe_send_block (0x8000, buffer, bytesread); + blocksdone++; + + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, fsize); + ffe_checkabort (2); + } + + // ROM dump > 128 16 KB blocks? (=16 Mb (=2 MB)) + ffe_send_command0 (0x2001, (unsigned char) (blocksdone > 0x80 ? 7 : 3)); + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + + +int +smd_read_sram (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer; + int blocksleft, bytesreceived = 0; + unsigned short address; + time_t starttime; + + ffe_init_io (parport); + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + printf ("Receive: %d Bytes\n", 32 * 1024); + memset (buffer, 0, SMD_HEADER_LEN); + buffer[8] = 0xaa; + buffer[9] = 0xbb; + buffer[10] = 7; + fwrite (buffer, 1, SMD_HEADER_LEN, file); + + ffe_send_command0 (0x2001, 4); + + printf ("Press q to abort\n\n"); + + blocksleft = 2; // SRAM is 2*16 KB + address = 0x4000; + starttime = time (NULL); + while (blocksleft > 0) + { + ffe_receive_block (address, buffer, BUFFERSIZE); + blocksleft--; + address += 0x4000; + fwrite (buffer, 1, BUFFERSIZE, file); + + bytesreceived += BUFFERSIZE; + ucon64_gauge (starttime, bytesreceived, 32 * 1024); + ffe_checkabort (2); + } + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + + +int +smd_write_sram (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer; + int bytesread, bytessend = 0, size; + unsigned short address; + time_t starttime; + + ffe_init_io (parport); + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + size = fsizeof (filename) - SMD_HEADER_LEN; + printf ("Send: %d Bytes\n", size); + fseek (file, SMD_HEADER_LEN, SEEK_SET); // skip the header + + ffe_send_command0 (0x2001, 4); + + printf ("Press q to abort\n\n"); + + address = 0x4000; + starttime = time (NULL); + while ((bytesread = fread (buffer, 1, BUFFERSIZE, file))) + { + ffe_send_block (address, buffer, bytesread); + address += 0x4000; + + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, size); + ffe_checkabort (2); + } + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/smd.h b/ucon64/2.0/src/backup/smd.h new file mode 100644 index 0000000..5be3db5 --- /dev/null +++ b/ucon64/2.0/src/backup/smd.h @@ -0,0 +1,53 @@ +/* +smd.h - Super Magic Drive support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2002 - 2003 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef SMD_H +#define SMD_H + +extern const st_getopt2_t smd_usage[]; + +// For the header format, see ffe.h +typedef struct st_smd_header +{ + unsigned char size; + unsigned char id0; + unsigned char split; + char pad[5]; + unsigned char id1; + unsigned char id2; + unsigned char type; + char pad2[501]; +} st_smd_header_t; + + +#ifdef USE_PARALLEL +extern int smd_read_rom (const char *filename, unsigned int parport); +extern int smd_write_rom (const char *filename, unsigned int parport); +extern int smd_read_sram (const char *filename, unsigned int parport); +extern int smd_write_sram (const char *filename, unsigned int parport); +#endif +// the following two functions are used by non-transfer code in genesis.c and sms.c +extern void smd_interleave (unsigned char *buffer, int size); +extern void smd_deinterleave (unsigned char *buffer, int size); + +#define SMD_HEADER_LEN (sizeof (st_smd_header_t)) + +#endif // SMD_H diff --git a/ucon64/2.0/src/backup/smsgg-pro.c b/ucon64/2.0/src/backup/smsgg-pro.c new file mode 100644 index 0000000..d0b6fdc --- /dev/null +++ b/ucon64/2.0/src/backup/smsgg-pro.c @@ -0,0 +1,348 @@ +/* +smsgg-pro.c - SMS-PRO/GG-PRO flash card programmer support for uCON64 + +Copyright (c) 2004 dbjh + +Based on Delphi source code by ToToTEK Multi Media. Information in that source +code has been used with permission. However, ToToTEK Multi Media explicitly +stated that the information in that source code may be freely distributed. + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include "misc/parallel.h" +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "misc/file.h" +#include "misc/misc.h" +#include "ucon64.h" +#include "ucon64_misc.h" +#include "tototek.h" +#include "smsgg-pro.h" + + +const st_getopt2_t smsggpro_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "SMS-PRO/GG-PRO flash card programmer"/*"2004 ToToTEK Multi Media http://www.tototek.com"*/, + NULL + }, +#ifdef USE_PARALLEL + { + "xgg", 0, 0, UCON64_XGG, + NULL, "send/receive ROM to/from SMS-PRO/GG-PRO flash card programmer\n" OPTION_LONG_S "port=PORT\n" + "receives automatically (32 Mbits) when ROM does not exist", + &ucon64_wf[WF_OBJ_SMS_DEFAULT_STOP_NO_SPLIT_NO_ROM] + }, + { + "xggs", 0, 0, UCON64_XGGS, + NULL, "send/receive SRAM to/from SMS-PRO/GG-PRO flash card programmer\n" OPTION_LONG_S "port=PORT\n" + "receives automatically when SRAM does not exist", + &ucon64_wf[WF_OBJ_SMS_STOP_NO_ROM] + }, + { + "xggb", 1, 0, UCON64_XGGB, + "BANK", "send/receive SRAM to/from SMS-PRO/GG-PRO BANK\n" + "BANK can be a number from 1 to 4; " OPTION_LONG_S "port=PORT\n" + "receives automatically when SRAM does not exist", + &ucon64_wf[WF_OBJ_SMS_STOP_NO_ROM] + }, +#endif // USE_PARALLEL + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +#ifdef USE_PARALLEL + +static void eep_reset (void); +static void write_rom_by_byte (int *addr, unsigned char *buf); +static void write_rom_by_page (int *addr, unsigned char *buf); +static void write_ram_by_byte (int *addr, unsigned char *buf); +static void write_ram_by_page (int *addr, unsigned char *buf); + + +void +eep_reset (void) +{ + ttt_rom_enable (); + ttt_write_mem (0x000000, 0xff); // reset EEP + ttt_write_mem (0x200000, 0xff); // reset EEP + ttt_rom_disable (); +} + + +void +write_rom_by_byte (int *addr, unsigned char *buf) +{ + int x; + + for (x = 0; x < 0x4000; x++) + { + ttt_write_byte_sharp (*addr, buf[*addr & 0x3fff]); + (*addr)++; + } +} + + +void +write_rom_by_page (int *addr, unsigned char *buf) +{ + int x; + + for (x = 0; x < 0x200; x++) + { + ttt_write_page_rom (*addr, buf); + (*addr) += 0x20; + } +} + + +void +write_ram_by_byte (int *addr, unsigned char *buf) +{ + int x; + + for (x = 0; x < 0x4000; x++) + { + ttt_write_byte_ram (*addr, buf[*addr & 0x3fff]); + (*addr)++; + } +} + + +void +write_ram_by_page (int *addr, unsigned char *buf) +{ + int x; + + for (x = 0; x < 0x40; x++) + { + ttt_write_page_ram (*addr, buf); + (*addr) += 0x100; + } +} + + +int +smsgg_read_rom (const char *filename, unsigned int parport, int size) +{ + FILE *file; + unsigned char buffer[0x100]; + int blocksleft, address = 0; + time_t starttime; + void (*read_block) (int, unsigned char *) = ttt_read_rom_w; // ttt_read_rom_b + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + ttt_init_io (parport); + + printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + + blocksleft = size >> 8; + eep_reset (); + ttt_rom_enable (); + if (read_block == ttt_read_rom_w) + ttt_set_ai_data (6, 0x94); // rst=1, wei=0(dis.), rdi=0(dis.), inc mode, rom_CS + starttime = time (NULL); + while (blocksleft-- > 0) + { + read_block (address, buffer); // 0x100 bytes read + fwrite (buffer, 1, 0x100, file); + address += 0x100; + if ((address & 0x3fff) == 0) + ucon64_gauge (starttime, address, size); + } + // original code doesn't call ttt_rom_disable() when byte-size function is + // used (ttt_read_rom_b() calls it) + if (read_block == ttt_read_rom_w) + ttt_rom_disable (); + + fclose (file); + ttt_deinit_io (); + + return 0; +} + + +int +smsgg_write_rom (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char buffer[0x4000]; + int size, address = 0, bytesread, bytessend = 0; + time_t starttime; + void (*write_block) (int *, unsigned char *) = write_rom_by_page; // write_rom_by_byte + (void) write_rom_by_byte; + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + ttt_init_io (parport); + + size = fsizeof (filename); + printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + + eep_reset (); + if (ttt_get_id () != 0xb0d0) + { + fputs ("ERROR: SMS-PRO/GG-PRO flash card (programmer) not detected\n", stderr); + fclose (file); + ttt_deinit_io (); + exit (1); + } + + starttime = time (NULL); + eep_reset (); + while ((bytesread = fread (buffer, 1, 0x4000, file))) + { + if ((address & 0xffff) == 0) + ttt_erase_block (address); + write_block (&address, buffer); + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, size); + } + + fclose (file); + ttt_deinit_io (); + + return 0; +} + + +int +smsgg_read_sram (const char *filename, unsigned int parport, int start_bank) +{ + FILE *file; + unsigned char buffer[0x100]; + int blocksleft, address, size, bytesreceived = 0; + time_t starttime; + void (*read_block) (int, unsigned char *) = ttt_read_ram_b; // ttt_read_ram_w + + if (start_bank == -1) + { + address = 0; + size = 128 * 1024; + } + else + { + if (start_bank < 1 || start_bank > 4) + { + fputs ("ERROR: Bank must be a value 1 - 4\n", stderr); + exit (1); + } + address = (start_bank - 1) * 32 * 1024; + size = 32 * 1024; + } + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + + ttt_init_io (parport); + printf ("Receive: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + + if (read_block == ttt_read_ram_w) + { + ttt_ram_enable (); + ttt_set_ai_data (6, 0x98); // rst=1, wei=0(dis.), rdi=0(dis.), inc mode, rom_CS + } +// else +// ttt_set_ai_data (6, 0x94); // rst=1, wei=0(dis.), rdi=0(dis.), inc mode, rom_CS + + blocksleft = size >> 8; + starttime = time (NULL); + while (blocksleft-- > 0) + { + read_block (address, buffer); // 0x100 bytes read + fwrite (buffer, 1, 0x100, file); + address += 0x100; + bytesreceived += 0x100; + if ((address & 0x3fff) == 0) + ucon64_gauge (starttime, bytesreceived, size); + } + if (read_block == ttt_read_ram_w) + ttt_ram_disable (); + + fclose (file); + ttt_deinit_io (); + + return 0; +} + + +int +smsgg_write_sram (const char *filename, unsigned int parport, int start_bank) +{ + FILE *file; + unsigned char buffer[0x4000]; + int size, bytesread, bytessend = 0, address; + time_t starttime; + void (*write_block) (int *, unsigned char *) = write_ram_by_byte; // write_ram_by_page + (void) write_ram_by_page; + + size = fsizeof (filename); + if (start_bank == -1) + address = 0; + else + { + if (start_bank < 1 || start_bank > 4) + { + fputs ("ERROR: Bank must be a value 1 - 4\n", stderr); + exit (1); + } + address = (start_bank - 1) * 32 * 1024; + } + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + + ttt_init_io (parport); + printf ("Send: %d Bytes (%.4f Mb)\n\n", size, (float) size / MBIT); + + starttime = time (NULL); + while ((bytesread = fread (buffer, 1, 0x4000, file))) + { + write_block (&address, buffer); + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, size); + } + + fclose (file); + ttt_deinit_io (); + + return 0; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/smsgg-pro.h b/ucon64/2.0/src/backup/smsgg-pro.h new file mode 100644 index 0000000..0f26ce2 --- /dev/null +++ b/ucon64/2.0/src/backup/smsgg-pro.h @@ -0,0 +1,37 @@ +/* +smsgg-pro.h - SMS-PRO/GG-PRO flash card programmer support for uCON64 + +Copyright (c) 2004 dbjh + +Based on Delphi source code by ToToTEK Multi Media. Information in that source +code has been used with permission. However, ToToTEK Multi Media explicitly +stated that the information in that source code may be freely distributed. + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef SMSGG_PRO_H +#define SMSGG_PRO_H + +extern const st_getopt2_t smsggpro_usage[]; + +#ifdef USE_PARALLEL +extern int smsgg_read_rom (const char *filename, unsigned int parport, int size); +extern int smsgg_write_rom (const char *filename, unsigned int parport); +extern int smsgg_read_sram (const char *filename, unsigned int parport, int start_bank); +extern int smsgg_write_sram (const char *filename, unsigned int parport, int start_bank); +#endif + +#endif diff --git a/ucon64/2.0/src/backup/ssc.c b/ucon64/2.0/src/backup/ssc.c new file mode 100644 index 0000000..838259b --- /dev/null +++ b/ucon64/2.0/src/backup/ssc.c @@ -0,0 +1,46 @@ +/* +ssc.c - support for Super Smart Card + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include "misc/itypes.h" +#include "misc/misc.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ssc.h" + + +const st_getopt2_t ssc_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Super Smart Card/SSC", + NULL + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; diff --git a/ucon64/2.0/src/backup/ssc.h b/ucon64/2.0/src/backup/ssc.h new file mode 100644 index 0000000..e0b63c3 --- /dev/null +++ b/ucon64/2.0/src/backup/ssc.h @@ -0,0 +1,34 @@ +/* +ssc.h - minimal support for Super Smart Card/SSC + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef SCC_H +#define SCC_H + +extern const st_getopt2_t ssc_usage[]; + +typedef struct st_ssc_header +{ + char pad[512]; +} st_ssc_header_t; + + +#define SSC_HEADER_LEN (sizeof (st_ssc_header_t)) + +#endif diff --git a/ucon64/2.0/src/backup/swc.c b/ucon64/2.0/src/backup/swc.c new file mode 100644 index 0000000..1b6161e --- /dev/null +++ b/ucon64/2.0/src/backup/swc.c @@ -0,0 +1,1358 @@ +/* +swc.c - Super Wild Card support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2001 - 2004 dbjh +Copyright (c) 2001 Caz +Copyright (c) 2003 John Weidman +Copyright (c) 2004 JohnDie + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include "misc/misc.h" +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "misc/file.h" +#include "misc/parallel.h" +#include "ucon64.h" +#include "ucon64_misc.h" +#include "ffe.h" +#include "swc.h" +#include "console/snes.h" // for snes_get_file_type () + + +const st_getopt2_t swc_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Super Com Pro/Super Magicom/SMC/Super Wild Card (1.6XC/2.7CC/2.8CC/DX/DX2)/SWC" + /*"1993/1994/1995/19XX Front Far East/FFE http://www.front.com.tw"*/, + NULL + }, +#ifdef USE_PARALLEL + { + "xswc", 0, 0, UCON64_XSWC, + NULL, "send/receive ROM to/from Super Wild Card*/SWC; " OPTION_LONG_S "port=PORT\n" + "receives automatically when ROM does not exist", + &ucon64_wf[WF_OBJ_SNES_DEFAULT_STOP_NO_SPLIT_NO_ROM] + }, + { + "xswc2", 0, 0, UCON64_XSWC2, + NULL, "same as " OPTION_LONG_S "xswc, but enables Real Time Save mode (SWC only)", + &ucon64_wf[WF_OBJ_SNES_DEFAULT_STOP_NO_SPLIT_NO_ROM] + }, +#if 1 +/* + The following help text used to be hidden, because we wanted to avoid people + to "accidentally" create overdumps, bad dumps or report bugs that aren't bugs + (SA-1). However, now that ucon64.io_mode is useful for -xswcc I guess the + help should be complete. - dbjh +*/ + { + "xswc-io", 1, 0, UCON64_XSWC_IO, + "MODE", "specify SWC I/O mode; use with -xswc or -xswcc\n" + "MODE=0x001 force 32 Mbit dump\n" + "MODE=0x002 use alternative method for determining ROM size\n" + "MODE=0x004 Super FX\n" + "MODE=0x008 S-DD1\n" + "MODE=0x010 SA-1\n" + "MODE=0x020 SPC7110\n" + "MODE=0x040 DX2 trick (might work with other SWC models)\n" + "MODE=0x080 Mega Man X 2\n" + "MODE=0x100 dump BIOS\n" + "It is possible to combine flags. MODE=0x44 makes it possible\n" + "to dump for example Yoshi's Island", + &ucon64_wf[WF_OBJ_SNES_SWITCH] + }, +#endif + { + "xswcs", 0, 0, UCON64_XSWCS, + NULL, + "send/receive SRAM to/from Super Wild Card*/SWC; " OPTION_LONG_S "port=PORT\n" + "receives automatically when SRAM does not exist", + &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM] + }, + { + "xswcc", 0, 0, UCON64_XSWCC, + NULL, "send/receive SRAM to/from cartridge in Super Wild Card*/SWC;\n" + OPTION_LONG_S "port=PORT\n" "receives automatically when SRAM does not exist", + &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM] + }, + { + "xswcr", 0, 0, UCON64_XSWCR, + NULL, "send/receive RTS data to/from Super Wild Card*/SWC; " OPTION_LONG_S "port=PORT\n" + "receives automatically when RTS file does not exist", + &ucon64_wf[WF_OBJ_SNES_STOP_NO_ROM] + }, +#endif // USE_PARALLEL + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + +#ifdef USE_PARALLEL + +#define BUFFERSIZE 8192 // don't change, only 8192 works! + +/* + Some notes about dumping special chip cartridges (JohnDie): + The following defines enable code to dump cartridges containing special chips + like the SA-1, the S-DD1, the SPC7110 and the C4. However, enabling these + options is not all there is to successfully dump these cartridges. + The SPC7110 and SA-1 need several attempts to "boot up" in the copier. This + involves powering off and on until the chip comes out of reset and allows + access to the cartridge. And you need to make sure that pin 1 of the SNES + adapter of the SWC is not cut! The standard SWC DX2 adapter has this trace + cut, so you have to reconnect it somehow. + Dumping Super FX 2 cartridges is only possible when enabling the DX2 trick. + Otherwise uCON64 will not detect the cartridge. This is because of the way the + Super FX 2 cartridges have their ROM mapped into the SNES address space. +*/ +#define DUMP_MMX2 +#define DUMP_SA1 +#define DUMP_SDD1 +#define DUMP_SPC7110 + +static int receive_rom_info (unsigned char *buffer, int io_mode); +static int get_rom_size (unsigned char *info_block); +static int check1 (unsigned char *info_block, int index); +static int check2 (unsigned char *info_block, int index, unsigned char value); +static int check3 (unsigned char *info_block, int index1, int index2, int size); +static unsigned char get_emu_mode_select (unsigned char byte, int size); +static void handle_fig_header (unsigned char *header); +static void set_bank_and_page (unsigned char bank, unsigned char page); +static void read_cartridge (unsigned int address, unsigned char *buffer, + unsigned int length); +static unsigned char read_cartridge1 (unsigned int address); +static void write_cartridge (unsigned int address, unsigned char *buffer, + unsigned int length); +static void write_cartridge1 (unsigned int address, unsigned char byte); +static void dump_rom (FILE *file, int size, int numblocks, unsigned int mask1, + unsigned int mask2, unsigned int address); +static void dump_bios (FILE *file); +static int sub (void); +static int mram_helper (int x); +static int mram (void); + +static int hirom; // `hirom' was `special' +static int dx2_trick = 0; + +#ifdef DUMP_SA1 +static void set_sa1_map (unsigned short chunk); +static int snes_sa1 = 0; +#endif +#ifdef DUMP_SDD1 +static void set_sdd1_map (unsigned short chunk); +static int snes_sdd1 = 0; +#endif +#ifdef DUMP_SPC7110 +static void set_spc7110_map (unsigned short chunk); +static int snes_spc7110 = 0; +#endif + + +#if BUFFERSIZE < 512 +#error receive_rom_info() and swc_read_sram() expect BUFFERSIZE to be at least \ + 512 bytes. +#endif +int +receive_rom_info (unsigned char *buffer, int io_mode) +/* + - returns size of ROM in Mb (128 kB) units + - returns ROM header in buffer (index 2 (emulation mode select) is not yet + filled in) + - sets global `hirom' +*/ +{ + int n, size; + volatile int m; + unsigned char byte; + +#ifdef DUMP_MMX2 + if (io_mode & SWC_IO_MMX2) + { + /* + MMX2 can be dumped after writing a 0 to SNES register 0x7f52. Before we can + write to that register we have to enable cartridge page mapping. That is + done by writing to SWC register 0xe00c. When cartridge page mapping is + enabled we can access SNES registers by reading or writing to the SWC + address range 0x2000-0x3fff. Before reading or writing to an address in that + range we have to "announce" the address to the SWC (via command 5). Because + we access a SNES register we only set the page number bits (0-1). + */ + unsigned short address = 0x7f52; + ffe_send_command0 (0xe00c, 0); + + ffe_send_command (5, (unsigned short) (address / 0x2000), 0); + ffe_receive_block ((unsigned short) ((address & 0x1fff) + 0x2000), buffer, 8); + dumper (stdout, buffer, 8, address, DUMPER_HEX); + + ffe_send_command (5, (unsigned short) (address / 0x2000), 0); + ffe_send_command0 ((unsigned short) ((address & 0x1fff) + 0x2000), 0); + + ffe_send_command (5, (unsigned short) (address / 0x2000), 0); + ffe_receive_block ((unsigned short) ((address & 0x1fff) + 0x2000), buffer, 8); + dumper (stdout, buffer, 8, address, DUMPER_HEX); + } +#endif + + ffe_send_command0 (0xe00c, 0); + + if (UCON64_ISSET (ucon64.snes_hirom)) + hirom = ucon64.snes_hirom ? 1 : 0; + else + { + byte = read_cartridge1 (0x00ffd5); + hirom = ((byte & 1 && byte != 0x23) || byte == 0x3a) ? 1 : 0; // & 1 => 0x21, 0x31, 0x35 + } + + for (n = 0; n < (int) SWC_HEADER_LEN; n++) + { + for (m = 0; m < 65536; m++) // a delay is necessary here + ; + ffe_send_command (5, (unsigned short) (0x200 + n), 0); + buffer[n] = ffe_send_command1 (0xa0a0); + } + + if (io_mode & SWC_IO_FORCE_32MBIT) + { + if (!UCON64_ISSET (ucon64.snes_hirom)) + hirom = 1; // default to super HiROM dump + size = 32; // dump 32 Mbit + } + else + { + size = get_rom_size (buffer); +#ifdef DUMP_SA1 + if (!snes_sa1) +#endif + if (hirom) + size <<= 1; + } + + // Fix up ROM size for Super FX 2 cartridge, because get_rom_size() fails for + // Super FX 2 cartridges and returns 0. + if (io_mode & SWC_IO_SUPER_FX) + // 00:303b returns the GSU revision and is non-zero if there is a GSU + if (size == 0 && read_cartridge1 (0x00303b) != 0) + size = 16; + +#ifdef DUMP_SDD1 + // Adjust size to 48 Mbit for Star Ocean + if (snes_sdd1 && size == 32) + { + byte = read_cartridge1 (0x00ffd7); + if (byte == 0x0d) + size = 48; + } +#endif + +#ifdef DUMP_SA1 + // Fix up size for SA-1 chips + if (snes_sa1) + { + byte = read_cartridge1 (0x00ffd7); + switch (byte) + { + case 0x09: + size = 4; + break; + case 0x0a: + size = 8; + break; + case 0x0b: + size = 16; + break; + case 0x0c: + size = 32; + break; + default: + break; + } + } +#endif + +#ifdef DUMP_SPC7110 + // Fix up size for SPC7110 chips + if (snes_spc7110) + { + byte = read_cartridge1 (0x00ffd7); + switch (byte) + { + case 0x0c: + size = 24; + break; + case 0x0d: + size = 40; + break; + default: + break; + } + } +#endif + + memset (buffer, 0, SWC_HEADER_LEN); + buffer[0] = size << 4; // *16 for 8 kB units; low byte + buffer[1] = size >> 4; // *16 for 8 kB units /256 for high byte + buffer[8] = 0xaa; + buffer[9] = 0xbb; + buffer[10] = 4; + + return size; +} + + +int +get_rom_size (unsigned char *info_block) +// returns size of ROM in Mb units +{ + if (check1 (info_block, 0)) + return 0; + if (check2 (info_block, 0x10, 0x84)) + return 0; + if (check3 (info_block, 0, 0x20, 0x20)) + return 2; + if (check3 (info_block, 0, 0x40, 0x20)) + return 4; + if (check3 (info_block, 0x40, 0x60, 0x20)) + return 6; + if (check3 (info_block, 0, 0x80, 0x10)) + return 8; + if (check1 (info_block, 0x80)) + return 8; + if (check3 (info_block, 0x80, 0x90, 0x10)) + return 8; + if (check2 (info_block, 0x80, 0xa0)) + return 8; + if (check3 (info_block, 0x80, 0xa0, 0x20)) + return 10; + if (check1 (info_block, 0xc0)) + return 12; + if (check2 (info_block, 0xc0, 0xb0)) + return 12; + if (check3 (info_block, 0x80, 0xc0, 0x20)) + return 12; + if (check3 (info_block, 0x100, 0, 0x10)) + return 16; + if (check2 (info_block, 0x100, 0xc0)) + return 16; + if (check3 (info_block, 0x100, 0x120, 0x10)) + return 18; + if (check3 (info_block, 0x100, 0x140, 0x10)) + return 20; + if (check2 (info_block, 0x140, 0xd0)) + return 20; + if (check3 (info_block, 0x100, 0x180, 0x10)) + return 24; + if (check2 (info_block, 0x180, 0xe0)) + return 24; + if (check3 (info_block, 0x180, 0x1c0, 0x10)) + return 28; + if (check3 (info_block, 0x1f0, 0x1f0, 0x10)) + return 32; + + return 0; +} + + +int +check1 (unsigned char *info_block, int index) +{ + int n; + + for (n = 0; n < 16; n++) + if (info_block[n + index] != info_block[index]) + return 0; + + return 1; +} + + +int +check2 (unsigned char *info_block, int index, unsigned char value) +{ + int n; + + for (n = 0; n < 4; n++) + if (info_block[n + index] != value) + return 0; + + return 1; +} + + +int +check3 (unsigned char *info_block, int index1, int index2, int size) +{ + int n; + + for (n = 0; n < size; n++) + if (info_block[n + index1] != info_block[n + index2]) + return 0; + + return 1; +} + + +unsigned char +get_emu_mode_select (unsigned char byte, int size) +{ + int x; + unsigned char ems; + + if (byte == 0) + x = 0xc; + else if (byte == 1) + x = 8; + else if (byte == 3) + x = 4; + else + x = 0; + + if (hirom) + { + if (x == 0xc && size <= 0x1c) + ems = 0x1c; + else + ems = x + 0x30; + } + else + { + if (x == 0xc) + ems = 0x2c; + else + ems = x; + +// if (size <= 8) // This bit should always be 0 - JohnDie +// ems++; + } + + return ems; +} + + +void +handle_fig_header (unsigned char *header) +{ + if ((header[4] == 0x77 && header[5] == 0x83) || + (header[4] == 0xf7 && header[5] == 0x83) || + (header[4] == 0x47 && header[5] == 0x83)) + header[2] = 0x0c; // 0 kB + else if (header[4] == 0xfd && header[5] == 0x82) + header[2] = 0x08; // 2 kB + else if ((header[4] == 0xdd && header[5] == 0x82) || + (header[4] == 0x00 && header[5] == 0x80)) + /* + 8 kB *or* 2 kB (shortcoming of FIG header format). We give the emu mode + select byte a value as if the game uses 8 kB. At least this makes games + that use 8 kB work. + Users should not complain if the game doesn't work because of a SRAM + protection, because they should have converted the ROM to SWC format in + the first place. + */ + header[2] = 0x04; + else // if ((header[4] == 0xdd && header[5] == 0x02) || + // (header[4] == 0x00 && header[5] == 0x00) || + // (header[4] == 0x11 && header[5] == 0x02)) + header[2] = 0; // 32 kB + + if (header[3] & 0x80) // Pro Fighter (FIG) HiROM dump + header[2] |= 0x30; // set bit 5&4 (SRAM & DRAM mem map mode 21) +} + + +void +swc_unlock (unsigned int parport) +/* + "Unlock" the SWC. However, just starting to send, then stopping with ^C, + gives the same result. +*/ +{ + ffe_init_io (parport); + ffe_send_command (6, 0, 0); + ffe_deinit_io (); +} + + +#ifdef DUMP_SA1 +void +set_sa1_map (unsigned short chunk) +{ + volatile int m; + + // map the 8 Mbit ROM chunk specified by chunk into the F0 bank + write_cartridge1 (0x002223, (unsigned char) ((chunk & 0x07) | 0x80)); + for (m = 0; m < 65536; m++) + ; +} +#endif + + +#ifdef DUMP_SDD1 +void +set_sdd1_map (unsigned short chunk) +{ + volatile int m; + + // map the 8 Mbit ROM chunk specified by chunk into the F0 bank + write_cartridge1 (0x004807, (unsigned char) (chunk & 0x07)); + for (m = 0; m < 65536; m++) + ; +} +#endif + + +#ifdef DUMP_SPC7110 +void +set_spc7110_map (unsigned short chunk) +{ + volatile int m; + + // map the 8 Mbit ROM chunk specified by chunk into the F0 bank + write_cartridge1 (0x004834, 0xff); + write_cartridge1 (0x004833, (unsigned char) (chunk & 0x07)); + for (m = 0; m < 65536; m++) + ; +} +#endif + + +void +set_bank_and_page (unsigned char bank, unsigned char page) +{ + static unsigned char currentbank = 0, currentpage = 4; // Force update on first call + + page &= 3; + +#if 0 + // We only send a command to the SWC if the bank or page differs. + /* + In order to avoid problems with that no other code should change the bank or + page number. So, no calls to ffe_send_command(5, ...). I prefer to be able + to call ffe_send_command(5, ...) without breaking this function. Besides, + the benefit of this optimisation is rather small. - dbjh + */ + if (bank != currentbank || page != currentpage) +#endif + { + currentbank = bank; + currentpage = page; + + if (dx2_trick) + { + /* + The SWC DX2 does not allow to access banks 00-7f. But this is needed + to dump some cartridges (ToP, DKJM2 and Super FX 2). This trick + avoids using ffe_send_command(5, ...) to set the bank value and + writes the bank value directly into the SNES RAM where the DX2 BIOS + would store it. Note that this hack is specific to the SWC DX2 and + will probably not work with other copiers. - JohnDie + */ + ffe_send_command (5, currentpage, 0); + ffe_send_command0 (0x0007, currentbank); + } + else + ffe_send_command (5, (unsigned short) ((currentbank << 2) | currentpage), 0); + } +} + + +void +read_cartridge (unsigned int address, unsigned char *buffer, unsigned int length) +{ + address &= 0xffffff; + set_bank_and_page ((unsigned char) (address >> 16), + (unsigned char) ((address & 0x7fff) / 0x2000)); + + if ((address & 0x00ffff) < 0x8000) + ffe_receive_block ((unsigned short) (((address & 0x7fffff) < 0x400000 ? + 0x6000 : 0x2000) + (address & 0x001fff)), buffer, length); + else + ffe_receive_block ((unsigned short) (0xa000 + (address & 0x001fff)), + buffer, length); +} + + +unsigned char +read_cartridge1 (unsigned int address) +{ + unsigned char byte; + + read_cartridge (address, &byte, 1); + + return byte; +} + + +void +write_cartridge (unsigned int address, unsigned char *buffer, unsigned int length) +{ + address &= 0xffffff; + set_bank_and_page ((unsigned char) (address >> 16), + (unsigned char) ((address & 0x7fff) / 0x2000)); + + if ((address & 0x00ffff) < 0x8000) + ffe_send_block ((unsigned short) (((address & 0x7fffff) < 0x400000 ? + 0x6000 : 0x2000) + (address & 0x001fff)), buffer, length); + else + ffe_send_block ((unsigned short) (0xa000 + (address & 0x001fff)), + buffer, length); +} + + +void +write_cartridge1 (unsigned int address, unsigned char byte) +{ + write_cartridge (address, &byte, 1); +} + + +void +dump_rom (FILE *file, int size, int numblocks, unsigned int mask1, + unsigned int mask2, unsigned int address) +{ + int i, bytesreceived = 0; + unsigned char *buffer; + time_t starttime; +#if defined DUMP_SA1 || defined DUMP_SDD1 || defined DUMP_SPC7110 + unsigned short chunk_num = 0; // 0 = 1st 8 Mb ROM chunk, 1 = 2nd 8 Mb, ... +#endif + + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + starttime = time (NULL); + + for (i = 0; i < numblocks; i++) + { + address |= mask1; // make sure to stay in ROM areas + address &= mask2; + +#ifdef DUMP_SA1 + if (snes_sa1 && address == 0xf00000) + set_sa1_map (chunk_num++); +#endif +#ifdef DUMP_SDD1 + if (snes_sdd1 && address == 0xf00000) + set_sdd1_map (chunk_num++); +#endif +#ifdef DUMP_SPC7110 + if (snes_spc7110 && address == 0xf00000) + set_spc7110_map (chunk_num++); +#endif + + read_cartridge (address, buffer, BUFFERSIZE); + + fwrite (buffer, 1, BUFFERSIZE, file); + address += BUFFERSIZE; + + bytesreceived += BUFFERSIZE; + ucon64_gauge (starttime, bytesreceived, size); + ffe_checkabort (2); + } + + ffe_send_command (5, 0, 0); + + free (buffer); +} + + +void +dump_bios (FILE *file) +{ + unsigned short int address; + int bytesreceived = 0; + unsigned char *buffer; + time_t starttime; + + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + starttime = time (NULL); + for (address = 0; address < 0x080; address += 4) // banks 00-1f + { + ffe_send_command (5, address, 0); + ffe_receive_block (0xe000, buffer, BUFFERSIZE); + fwrite (buffer, 1, BUFFERSIZE, file); + + bytesreceived += BUFFERSIZE; + ucon64_gauge (starttime, bytesreceived, 0x20 * 0x2000); + ffe_checkabort (2); + } + + free (buffer); +} + + +int +swc_read_rom (const char *filename, unsigned int parport, int io_mode) +{ + FILE *file; + unsigned char buffer[SWC_HEADER_LEN], byte; + int size, blocksleft; + + ffe_init_io (parport); + +#ifdef DUMP_SA1 + if (io_mode & SWC_IO_SA1) + snes_sa1 = 1; +#endif +#ifdef DUMP_SDD1 + if (io_mode & SWC_IO_SDD1) + snes_sdd1 = 1; +#endif +#ifdef DUMP_SPC7110 + if (io_mode & SWC_IO_SPC7110) + snes_spc7110 = 1; +#endif + if (io_mode & SWC_IO_DX2_TRICK) + dx2_trick = 1; + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + + if (io_mode & SWC_IO_DUMP_BIOS) + { + puts ("Press q to abort\n"); + + dump_bios (file); + + ffe_deinit_io (); + fclose (file); + return 0; // skip the other code in this function + } + + size = receive_rom_info (buffer, io_mode); + if (size == 0) + { + fputs ("ERROR: There is no cartridge present in the Super Wild Card\n", stderr); + fclose (file); + remove (filename); + exit (1); + } + blocksleft = size * 16; // 1 Mb (128 kB) unit == 16 8 kB units + printf ("Receive: %d Bytes (%.4f Mb)\n", size * MBIT, (float) size); +#ifdef DUMP_SA1 + if (snes_sa1) + puts ("NOTE: Dumping SA-1 cartridge"); +#endif +#ifdef DUMP_SDD1 + if (snes_sdd1) + puts ("NOTE: Dumping S-DD1 cartridge"); +#endif +#ifdef DUMP_SPC7110 + if (snes_spc7110) + puts ("NOTE: Dumping SPC7110 cartridge"); +#endif + size *= MBIT; // size in bytes for ucon64_gauge() below + + ffe_send_command (5, 0, 0); + ffe_send_command0 (0xe00c, 0); + byte = ffe_send_command1 (0xbfd8); + buffer[2] = get_emu_mode_select (byte, blocksleft / 16); + fwrite (buffer, 1, SWC_HEADER_LEN, file); // write header (other necessary fields are + // filled in by receive_rom_info()) + + puts ("Press q to abort\n"); // print here, NOT before first SWC I/O, + // because if we get here q works ;-) +#ifdef DUMP_SA1 + if (snes_sa1) + dump_rom (file, size, blocksleft, 0xf00000, 0xffffff, 0xf00000); + else +#endif +#ifdef DUMP_SDD1 + if (snes_sdd1) + dump_rom (file, size, blocksleft, 0xf00000, 0xffffff, 0xf00000); + else +#endif +#ifdef DUMP_SPC7110 + if (snes_spc7110) + { + // First dump the 8 MBit P-ROM (program ROM) + dump_rom (file, 8 * MBIT, 8 * 16, 0xc00000, 0xcfffff, 0xc00000); + // Then dump the remaining amount of D-ROM (data ROM) + dump_rom (file, size - 8 * MBIT, blocksleft - 8 * 16, 0xf00000, 0xffffff, + 0xf00000); + } + else +#endif + if (io_mode & SWC_IO_SUPER_FX) + dump_rom (file, size, blocksleft, 0x008000, 0x7fffff, 0x008000); + else if (hirom) + dump_rom (file, size, blocksleft, 0x400000, 0xffffff, 0xc00000); + else + dump_rom (file, size, blocksleft, 0x008000, 0xffffff, 0x808000); + +#ifdef DUMP_SA1 + if (snes_sa1) + set_sa1_map (3); +#endif +#ifdef DUMP_SDD1 + if (snes_sdd1) + set_sdd1_map (3); +#endif + ffe_send_command (5, 0, 0); + + fclose (file); + ffe_deinit_io (); + + return 0; +} + + +int +swc_write_rom (const char *filename, unsigned int parport, int enableRTS) +{ + FILE *file; + unsigned char *buffer; + int bytesread, bytessend, totalblocks, blocksdone = 0, emu_mode_select, fsize; + unsigned short address; + time_t starttime; + + ffe_init_io (parport); + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + fsize = fsizeof (filename); + printf ("Send: %d Bytes (%.4f Mb)\n", fsize, (float) fsize / MBIT); + + ffe_send_command0 (0xc008, 0); + fread (buffer, 1, SWC_HEADER_LEN, file); + + if (snes_get_file_type () == FIG) + handle_fig_header (buffer); +#if 1 + /* + 0x0c == no SRAM & LoROM; we use the header, so that the user can override this + bit 4 == 0 => DRAM mode 20 (LoROM); disable SRAM by setting SRAM mem map mode 2 + */ + if ((buffer[2] & 0x1c) == 0x0c) + buffer[2] |= 0x20; +#else + // The code below doesn't work for some HiROM games that don't use SRAM. + if ((buffer[2] & 0x0c) == 0x0c) // 0x0c == no SRAM; we use the header, so + { // that the user can override this + if (buffer[2] & 0x10) // bit 4 == 1 => DRAM mode 21 (HiROM) + buffer[2] &= ~0x20; // disable SRAM by setting SRAM mem map mode 1 + else // bit 4 == 0 => DRAM mode 20 (LoROM) + buffer[2] |= 0x20; // disable SRAM by setting SRAM mem map mode 2 + } +#endif + emu_mode_select = buffer[2]; // this byte is needed later + +#if 1 // sending the header is not required + ffe_send_command (5, 0, 0); + ffe_send_block (0x400, buffer, SWC_HEADER_LEN); // send header +#endif + bytessend = SWC_HEADER_LEN; + + puts ("Press q to abort\n"); // print here, NOT before first SWC I/O, + // because if we get here q works ;-) + address = 0x200; // VGS '00 uses 0x200, VGS '96 uses 0, + starttime = time (NULL); // but then some ROMs don't work + while ((bytesread = fread (buffer, 1, BUFFERSIZE, file))) + { + ffe_send_command0 ((unsigned short) 0xc010, (unsigned char) (blocksdone >> 9)); + ffe_send_command (5, address, 0); + ffe_send_block (0x8000, buffer, bytesread); + address++; + blocksdone++; + + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, fsize); + ffe_checkabort (2); + } + + if (blocksdone > 0x200) // ROM dump > 512 8 kB blocks (=32 Mb (=4 MB)) + ffe_send_command0 (0xc010, 2); + + ffe_send_command (5, 0, 0); + totalblocks = (fsize - SWC_HEADER_LEN + BUFFERSIZE - 1) / BUFFERSIZE; // round up + ffe_send_command (6, (unsigned short) (5 | (totalblocks << 8)), (unsigned short) (totalblocks >> 8)); // bytes: 6, 5, #8 K L, #8 K H, 0 + ffe_send_command (6, (unsigned short) (1 | (emu_mode_select << 8)), (unsigned short) enableRTS); // last arg = 1 enables RTS + // mode, 0 disables it + ffe_wait_for_ready (); + outportb ((unsigned short) (parport + PARPORT_DATA), 0); + outportb ((unsigned short) (parport + PARPORT_CONTROL), + (unsigned char) (inportb ((unsigned short) // invert strobe + (parport + PARPORT_CONTROL)) ^ PARPORT_STROBE)); + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + + +int +swc_read_sram (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer; + int blocksleft, bytesreceived = 0; + unsigned short address; + time_t starttime; + + ffe_init_io (parport); + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + printf ("Receive: %d Bytes\n", 32 * 1024); + memset (buffer, 0, SWC_HEADER_LEN); + buffer[8] = 0xaa; + buffer[9] = 0xbb; + buffer[10] = 5; + fwrite (buffer, 1, SWC_HEADER_LEN, file); + + ffe_send_command (5, 0, 0); + ffe_send_command0 (0xe00d, 0); + ffe_send_command0 (0xc008, 0); + + puts ("Press q to abort\n"); // print here, NOT before first SWC I/O, + // because if we get here q works ;-) + blocksleft = 4; // SRAM is 4*8 kB + address = 0x100; + starttime = time (NULL); + while (blocksleft > 0) + { + ffe_send_command (5, address, 0); + ffe_receive_block (0x2000, buffer, BUFFERSIZE); + blocksleft--; + address++; + fwrite (buffer, 1, BUFFERSIZE, file); + + bytesreceived += BUFFERSIZE; + ucon64_gauge (starttime, bytesreceived, 32 * 1024); + ffe_checkabort (2); + } + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + + +int +swc_write_sram (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer; + int bytesread, bytessend = 0, size; + unsigned short address; + time_t starttime; + + ffe_init_io (parport); + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + size = fsizeof (filename) - SWC_HEADER_LEN; // SWC SRAM is 4*8 kB, emu SRAM often not + printf ("Send: %d Bytes\n", size); + fseek (file, SWC_HEADER_LEN, SEEK_SET); // skip the header + + ffe_send_command (5, 0, 0); + ffe_send_command0 (0xe00d, 0); + ffe_send_command0 (0xc008, 0); + + puts ("Press q to abort\n"); // print here, NOT before first SWC I/O, + // because if we get here q works ;-) + address = 0x100; + starttime = time (NULL); + while ((bytesread = fread (buffer, 1, BUFFERSIZE, file))) + { + ffe_send_command (5, address, 0); + ffe_send_block (0x2000, buffer, bytesread); + address++; + + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, size); + ffe_checkabort (2); + } + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + + +int +sub (void) +{ + ffe_send_command (5, 7 * 4, 0); + ffe_send_command0 (0xe00d, 0); + ffe_send_command0 (0xe003, 0); + + if (ffe_send_command1 (0xb080) != 'S') + return 0; + if (ffe_send_command1 (0xb081) != 'U') + return 0; + if (ffe_send_command1 (0xb082) != 'B') + return 0; + + return 1; +} + + +int +mram_helper (int x) +{ + ffe_send_command (5, (unsigned short) x, 0); + x = ffe_send_command1 (0x8000); + ffe_send_command0 (0x8000, (unsigned char) (x ^ 0xff)); + if (ffe_send_command1 (0x8000) != (unsigned char) (x ^ 0xff)) + return 0; + + ffe_send_command0 (0x8000, (unsigned char) x); + return 1; +} + + +int +mram (void) +{ + if (mram_helper (0x76 * 4)) + return 0x76 * 4; + if (mram_helper (0x56 * 4)) + return 0x56 * 4; + if (mram_helper (0x36 * 4)) + return 0x36 * 4; + return 0x16 * 4; +} + + +int +swc_read_rts (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer; + int blocksleft, bytesreceived = 0; + unsigned short address1, address2; + time_t starttime; + + ffe_init_io (parport); + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + printf ("Receive: %d Bytes\n", 256 * 1024); + memset (buffer, 0, SWC_HEADER_LEN); + buffer[8] = 0xaa; + buffer[9] = 0xbb; + buffer[10] = 8; + fwrite (buffer, 1, SWC_HEADER_LEN, file); + + puts ("Press q to abort\n"); + blocksleft = 32; // RTS data is 32*8 kB + + if (sub ()) + { + address1 = 0; + address2 = 0xa000; + } + else + { + address1 = mram (); + address2 = 0x8000; + } + + starttime = time (NULL); + while (blocksleft > 0) + { + ffe_send_command (5, address1, 0); + if (address2 == 0x8000) + ffe_send_command0 (0xc010, 1); + ffe_receive_block (address2, buffer, BUFFERSIZE); + + blocksleft--; + address1++; + fwrite (buffer, 1, BUFFERSIZE, file); + + bytesreceived += BUFFERSIZE; + ucon64_gauge (starttime, bytesreceived, 256 * 1024); + ffe_checkabort (2); + } + ffe_send_command (6, 3, 0); + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + + +int +swc_write_rts (const char *filename, unsigned int parport) +{ + FILE *file; + unsigned char *buffer; + int bytesread, bytessend = 0, size; + unsigned short address1, address2; + time_t starttime; + + ffe_init_io (parport); + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + size = fsizeof (filename) - SWC_HEADER_LEN; + printf ("Send: %d Bytes\n", size); + fseek (file, SWC_HEADER_LEN, SEEK_SET); // skip the header + + puts ("Press q to abort\n"); + if (sub ()) + { + address1 = 0; + address2 = 0xa000; + } + else + { + address1 = mram (); + address2 = 0x8000; + } + + starttime = time (NULL); + while ((bytesread = fread (buffer, 1, BUFFERSIZE, file))) + { + ffe_send_command (5, address1, 0); + if (address2 == 0x8000) + ffe_send_command0 (0xc010, 1); + ffe_send_block (address2, buffer, bytesread); + address1++; + + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, size); + ffe_checkabort (2); + } + ffe_send_command (6, 3, 0); + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + + +int +swc_read_cart_sram (const char *filename, unsigned int parport, int io_mode) +{ + FILE *file; + unsigned char *buffer, byte; + int bytesreceived = 0, size; + unsigned short address; + time_t starttime; + + ffe_init_io (parport); + + if (io_mode & SWC_IO_DX2_TRICK) + dx2_trick = 1; + + if ((file = fopen (filename, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + size = receive_rom_info (buffer, io_mode); + if (size == 0) + { + fputs ("ERROR: There is no cartridge present in the Super Wild Card\n", stderr); + fclose (file); + remove (filename); + exit (1); + } + + ffe_send_command (5, 3, 0); // detect cartridge SRAM size because + ffe_send_command0 (0xe00c, 0); // we don't want to read too few data + byte = read_cartridge1 (io_mode & SWC_IO_SUPER_FX ? 0x00ffbd : 0x00ffd8); + size = MAX ((byte ? 1 << (byte + 10) : 0), 32 * 1024); + printf ("Receive: %d Bytes\n", size); + + memset (buffer, 0, SWC_HEADER_LEN); + buffer[8] = 0xaa; + buffer[9] = 0xbb; + buffer[10] = 5; + fwrite (buffer, 1, SWC_HEADER_LEN, file); + + ffe_send_command (5, 0, 0); + ffe_send_command0 (0xe00c, 0); +// ffe_send_command0 (0xc008, 0); + + puts ("Press q to abort\n"); // print here, NOT before first SWC I/O, + // because if we get here q works ;-) + address = hirom ? 0x2c3 : 0x1c0; + + starttime = time (NULL); + while (bytesreceived < size) + { + set_bank_and_page ((unsigned char) (address >> 2), (unsigned char) (address & 3)); + ffe_receive_block ((unsigned short) (hirom ? 0x6000 : 0x2000), buffer, BUFFERSIZE); + fwrite (buffer, 1, BUFFERSIZE, file); + address += hirom ? 4 : 1; + + bytesreceived += BUFFERSIZE; + ucon64_gauge (starttime, bytesreceived, size); + ffe_checkabort (2); + } + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + + +int +swc_write_cart_sram (const char *filename, unsigned int parport, int io_mode) +{ + FILE *file; + unsigned char *buffer, byte; + int bytesread, bytessend = 0, size; + unsigned short address; + time_t starttime; + + ffe_init_io (parport); + + if (io_mode & SWC_IO_DX2_TRICK) + dx2_trick = 1; + + if ((file = fopen (filename, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename); + exit (1); + } + if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE); + exit (1); + } + + size = receive_rom_info (buffer, io_mode); + if (size == 0) + { + fputs ("ERROR: There is no cartridge present in the Super Wild Card\n", stderr); + fclose (file); + exit (1); + } + + ffe_send_command (5, 3, 0); // detect cartridge SRAM size because we don't + ffe_send_command0 (0xe00c, 0); // want to write more data than necessary + byte = read_cartridge1 (io_mode & SWC_IO_SUPER_FX ? 0x00ffbd : 0x00ffd8); + size = fsizeof (filename) - SWC_HEADER_LEN; // SWC SRAM is 4*8 kB, emu SRAM often not + size = MIN ((byte ? 1 << (byte + 10) : 0), size); + + printf ("Send: %d Bytes\n", size); + fseek (file, SWC_HEADER_LEN, SEEK_SET); // skip the header + + ffe_send_command (5, 0, 0); + ffe_send_command0 (0xe00c, 0); +// ffe_send_command0 (0xc008, 0); + + puts ("Press q to abort\n"); // print here, NOT before first SWC I/O, + // because if we get here q works ;-) + address = hirom ? 0x2c3 : 0x1c0; + + starttime = time (NULL); + while ((bytessend < size) && (bytesread = fread (buffer, 1, MIN (size, BUFFERSIZE), file))) + { + set_bank_and_page ((unsigned char) (address >> 2), (unsigned char) (address & 3)); + ffe_send_block ((unsigned short) (hirom ? 0x6000 : 0x2000), buffer, bytesread); + address += hirom ? 4 : 1; + + bytessend += bytesread; + ucon64_gauge (starttime, bytessend, size); + ffe_checkabort (2); + } + + free (buffer); + fclose (file); + ffe_deinit_io (); + + return 0; +} + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/swc.h b/ucon64/2.0/src/backup/swc.h new file mode 100644 index 0000000..24ce42d --- /dev/null +++ b/ucon64/2.0/src/backup/swc.h @@ -0,0 +1,72 @@ +/* +swc.h - Super Wild Card support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2001 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef SWC_H +#define SWC_H + +#define SWC_IO_FORCE_32MBIT 0x001 +#define SWC_IO_ALT_ROM_SIZE 0x002 +#define SWC_IO_SUPER_FX 0x004 +#define SWC_IO_SDD1 0x008 +#define SWC_IO_SA1 0x010 +#define SWC_IO_SPC7110 0x020 +#define SWC_IO_DX2_TRICK 0x040 +#define SWC_IO_MMX2 0x080 +#define SWC_IO_DUMP_BIOS 0x100 + +#define SWC_IO_MAX 0x1ff // highest valid dumping mode value + +extern const st_getopt2_t swc_usage[]; + +// For the header format, see ffe.h +typedef struct st_swc_header +{ +/* + Don't create fields that are larger than one byte! For example size_low and size_high + could be combined in one unsigned short int. However, this gives problems with little + endian vs. big endian machines (e.g. writing the header to disk). +*/ + unsigned char size_low; + unsigned char size_high; + unsigned char emulation; + unsigned char pad[5]; + unsigned char id1; + unsigned char id2; + unsigned char type; + unsigned char pad2[501]; +} st_swc_header_t; + +#define SWC_HEADER_START 0 +#define SWC_HEADER_LEN (sizeof (st_swc_header_t)) + +#ifdef USE_PARALLEL +extern int swc_read_rom (const char *filename, unsigned int parport, int io_mode); +extern int swc_write_rom (const char *filename, unsigned int parport, int enableRTS); +extern int swc_read_sram (const char *filename, unsigned int parport); +extern int swc_write_sram (const char *filename, unsigned int parport); +extern int swc_read_rts (const char *filename, unsigned int parport); +extern int swc_write_rts (const char *filename, unsigned int parport); +extern int swc_read_cart_sram (const char *filename, unsigned int parport, int io_mode); +extern int swc_write_cart_sram (const char *filename, unsigned int parport, int io_mode); +extern void swc_unlock (unsigned int parport); +#endif + +#endif // SWC_H diff --git a/ucon64/2.0/src/backup/tototek.c b/ucon64/2.0/src/backup/tototek.c new file mode 100644 index 0000000..33d9ce4 --- /dev/null +++ b/ucon64/2.0/src/backup/tototek.c @@ -0,0 +1,428 @@ +/* +tototek.c - General ToToTEK flash card programmer routines for uCON64 + +Copyright (c) 2004 dbjh + +Based on Delphi source code by ToToTEK Multi Media. Information in that source +code has been used with permission. However, ToToTEK Multi Media explicitly +stated that the information in that source code may be freely distributed. + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include "misc/bswap.h" +#include "misc/misc.h" +#include "misc/parallel.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "tototek.h" + + +#ifdef USE_PARALLEL + +static void set_data_read (void); +//static void set_data_write (void); +static void set_ai (unsigned char ai); +static void init_port (void); +static void deinit_port (void); +static void end_port (void); +static void set_addr_read (int addr); +static void set_addr_write (int addr); +static unsigned char get_id_byte (unsigned char addr); + +static short int port_8, port_9, port_a, port_b, port_c; + + +void +ttt_init_io (unsigned int port) +{ +#if (defined __unix__ || defined __BEOS__) && !defined __MSDOS__ + init_conio (); +#endif + + port_8 = port; // original code used 0x378 for port_8 + port_9 = port + 1; + port_a = port + 2; + port_b = port + 3; + port_c = port + 4; + + parport_print_info (); + + init_port (); +} + + +void +ttt_deinit_io (void) +{ +#if (defined __unix__ || defined __BEOS__) && !defined __MSDOS__ + deinit_conio (); +#endif + + end_port (); + deinit_port (); +} + + +void +set_data_read (void) // original name: set_data_read +{ + outportb (port_a, 0); // nastb=1,nib_sel=0,ndstb=1,nwrite=1 +} + + +#if 0 +void +set_data_write (void) // original name: set_data_write +{ + outportb (port_a, 1); // nastb=1,nib_sel=0,ndstb=1,nwrite=0 +} +#endif + + +void +set_ai (unsigned char ai) // original name: {epp_}set_ai +{ + outportb (port_a, 1); // set_data_write () + outportb (port_b, ai); +} + + +void +ttt_set_ai_data (unsigned char ai, unsigned char data) // original name: set_ai_data +{ + set_ai (ai); + outportb (port_a, 1); // set_data_write () + outportb (port_c, data); +} + + +void +init_port (void) // original name: init_port +{ +#ifndef USE_PPDEV + outportb ((unsigned short) (port_8 + 0x402), + (unsigned char) ((inportb ((unsigned short) (port_8 + 0x402)) & 0x1f) | 0x80)); + outportb (port_9, 1); // clear EPP time flag +#endif + ttt_set_ai_data (6, 0); // rst=0, wei=0(dis.), rdi=0(dis.) + ttt_set_ai_data (6, 0x84); // rst=1, wei=0(dis.), rdi=0(dis.) +} + + +void +deinit_port (void) // original name: Close_end_port +{ + outportb (port_a, 1); + outportb (port_b, 0); + outportb (port_a, 4); // port normal now +} + + +void +end_port (void) // original name: end_port +{ + ttt_set_ai_data (6, 0); // rst=0, wei=0(dis.), rdi=0(dis.) + outportb (port_a, 4); // set_normal ninit=1, nwrite=1 +} + + +void +ttt_rom_enable (void) // original name: romCS_on +{ + ttt_set_ai_data (6, 0x84); +} + + +void +ttt_rom_disable (void) // original name: romCS_off +{ + ttt_set_ai_data (6, 0x80); +} + + +void +ttt_ram_enable (void) // original name: ramCS_on +{ + ttt_set_ai_data (6, 0x88); +} + + +void +ttt_ram_disable (void) // original name: ramCS_off +{ + ttt_set_ai_data (6, 0x80); +} + + +void +set_addr_write (int addr) // original name: set_Long_adrw +{ + ttt_set_ai_data (0, (unsigned char) (addr & 0xff)); // a[7..0] + addr >>= 8; + ttt_set_ai_data (1, (unsigned char) (addr & 0xff)); // a[15..8] + addr >>= 8; + ttt_set_ai_data (2, (unsigned char) (addr & 0xff)); // a[23..16] + set_ai (3); +} + + +void +set_addr_read (int addr) // original name: set_Long_adr +{ + set_addr_write (addr); + set_data_read (); // ninit=0, nwrite=1 +} + + +void +ttt_write_mem (int addr, unsigned char b) // original name: WriteMEMb +{ + set_addr_write (addr); + outportb (port_c, b); +} + + +unsigned char +get_id_byte (unsigned char addr) // original name: GETID +{ + unsigned char byte; + + ttt_rom_enable (); + ttt_set_ai_data (0, addr); // a[7..0] = 0 + set_ai (3); + outportb (port_c, 0x90); // write_data () + + set_ai (3); + set_data_read (); // ninit=0, nwrite=1 + byte = inportb (port_c); // read_data () + ttt_rom_disable (); + + return byte; +} + + +unsigned short int +ttt_get_id (void) // original name: getIDword +{ + unsigned short int word; + +// eep_reset (); + word = get_id_byte (0); // msg + word <<= 8; + word |= get_id_byte (2); // ID + + return word; +} + + +void +ttt_read_rom_b (int addr, unsigned char *buf) // original name: read_buffb +{ + int count; + + ttt_rom_enable (); + for (count = 0; count < 0x100; count++) + { + set_addr_read (addr + count); + buf[count] = inportb (port_c); // read_data () + } + ttt_rom_disable (); +} + + +void +ttt_read_rom_w (int addr, unsigned char *buf) // original name: read_buff +{ + int count; + + set_addr_read (addr); + for (count = 0; count < 0x80; count++) +#ifdef WORDS_BIGENDIAN + ((unsigned short int *) buf)[count] = bswap_16 (inportw (port_c)); // read_dataw () +#else + ((unsigned short int *) buf)[count] = inportw (port_c); // read_dataw () +#endif +} + + +void +ttt_read_ram_b (int addr, unsigned char *buf) // original name: read_rambuff +{ + int count; + + ttt_ram_enable (); + for (count = 0; count < 0x100; count++) + { + set_addr_read (addr + count); + buf[count] = inportb (port_c); // read_data () + } + ttt_ram_disable (); +} + + +void +ttt_read_ram_w (int addr, unsigned char *buf) // original name: readpagerambuff +{ + int count; + + set_addr_read (addr); + for (count = 0; count < 0x80; count++) + ((unsigned short int *) buf)[count] = inportw (port_c); + // read_dataw (); data is doubled for MD-PRO => no problems with endianess +} + + +// Sharp function +void +ttt_erase_block (int addr) // original name: EraseBLOCK +{ + ttt_rom_enable (); + ttt_write_mem (addr, 0x50); + ttt_rom_disable (); + + ttt_rom_enable (); + ttt_write_mem (addr, 0x20); + outportb (port_c, 0xd0); + ttt_rom_disable (); + + ttt_rom_enable (); + set_ai (3); + set_data_read (); // set read mode + while (inportb (port_c) < 0x80) + ; + ttt_rom_disable (); +} + + +void +ttt_write_byte_sharp (int addr, unsigned char b) // original name: writeEEPDataB +{ + ttt_rom_enable (); + ttt_write_mem (addr, 0x40); + outportb (port_c, b); + ttt_rom_disable (); +} + + +void +ttt_write_byte_intel (int addr, unsigned char b) // original name: writeIntelEEPDataB +{ // MD-PRO function + ttt_rom_enable (); + ttt_write_mem (addr, 0x40); + outportb (port_c, b); + + set_data_read (); + while (inportb (port_c) < 0x80) + ; + ttt_rom_disable (); +} + + +void +ttt_write_page_rom (int addr, unsigned char *buf) // original name: writeEEPDataPAGE +{ + int i; + + // send command 0xe8 + ttt_rom_enable (); + ttt_write_mem (addr, 0xe8); + + // read status + set_ai (3); + set_data_read (); + while (inportb (port_c) < 0x80) + ; + + set_ai (3); + outportb (port_c, 0x1f); + ttt_set_ai_data (6, 0x94); + set_addr_write (addr); + for (i = 0; i <= 0x1f; i++) + outportb (port_c, buf[(addr + i) & 0x3fff]); + ttt_set_ai_data (6, 0x84); + set_ai (3); + outportb (port_c, 0xd0); + + // read status + set_ai (3); + set_data_read (); + while (inportb (port_c) < 0x80) + ; + ttt_rom_disable (); +} + + +void +ttt_write_byte_ram (int addr, unsigned char b) // original name: writeRAMDataB +{ + ttt_ram_enable (); + ttt_write_mem (addr, b); + ttt_ram_disable (); +} + + +void +ttt_write_page_ram (int addr, unsigned char *buf) // original name: writeRAMDataPAGE +{ + int i; + + ttt_ram_enable (); + ttt_set_ai_data (6, 0x98); + set_addr_write (addr); + for (i = 0; i < 0x100; i++) + outportb (port_c, buf[(addr + i) & 0x3fff]); + ttt_ram_disable (); +} + + +void +ttt_write_page_ram2 (int addr, unsigned char *buf) // original name: writeRAMDBDataPAGE +{ // MD-PRO function + int i; + + ttt_ram_enable (); + ttt_set_ai_data (6, 0x98); + set_addr_write (addr * 2); + for (i = 0; i < 0x80; i++) + { + outportb (port_c, buf[(addr + i) & 0x3fff]); + outportb (port_c, buf[(addr + i) & 0x3fff]); + } + ttt_ram_disable (); +} + + +#if 0 +void +readstatus (void) +{ // MD-PRO function + // send command 0xe8 + ttt_rom_enable (); + set_ai (3); + set_data_read (); + while (inportb (port_c) < 0x80) + ; + ttt_rom_disable (); +} +#endif + +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/backup/tototek.h b/ucon64/2.0/src/backup/tototek.h new file mode 100644 index 0000000..ed47e2a --- /dev/null +++ b/ucon64/2.0/src/backup/tototek.h @@ -0,0 +1,47 @@ +/* +tototek.h - General ToToTEK flash card programmer routines for uCON64 + +Copyright (c) 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef TOTOTEK_H +#define TOTOTEK_H + +#ifdef USE_PARALLEL +extern void ttt_init_io (unsigned int port); +extern void ttt_deinit_io (void); +extern void ttt_set_ai_data (unsigned char ai, unsigned char data); +extern void ttt_rom_enable (void); +extern void ttt_rom_disable (void); +extern void ttt_ram_enable (void); +extern void ttt_ram_disable (void); +extern void ttt_write_mem (int addr, unsigned char b); +extern unsigned short int ttt_get_id (void); +extern void ttt_read_rom_b (int addr, unsigned char *buf); +extern void ttt_read_rom_w (int addr, unsigned char *buf); +extern void ttt_read_ram_b (int addr, unsigned char *buf); +extern void ttt_read_ram_w (int addr, unsigned char *buf); +extern void ttt_erase_block (int addr); +extern void ttt_write_byte_sharp (int addr, unsigned char b); +extern void ttt_write_byte_intel (int addr, unsigned char b); +extern void ttt_write_page_rom (int addr, unsigned char *buf); +extern void ttt_write_byte_ram (int addr, unsigned char b); +extern void ttt_write_page_ram (int addr, unsigned char *buf); +extern void ttt_write_page_ram2 (int addr, unsigned char *buf); +#endif // USE_PARALLEL + +#endif // TOTOTEK_H diff --git a/ucon64/2.0/src/backup/ufo.c b/ucon64/2.0/src/backup/ufo.c new file mode 100644 index 0000000..bbf83c0 --- /dev/null +++ b/ucon64/2.0/src/backup/ufo.c @@ -0,0 +1,45 @@ +/* +ufo.c - Super UFO support for uCON64 + +Copyright (c) 2003 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ufo.h" + + +const st_getopt2_t ufo_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Super UFO Pro 8"/*"UFO Enterprises"*/, + NULL + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; diff --git a/ucon64/2.0/src/backup/ufo.h b/ucon64/2.0/src/backup/ufo.h new file mode 100644 index 0000000..81c918f --- /dev/null +++ b/ucon64/2.0/src/backup/ufo.h @@ -0,0 +1,144 @@ +/* +ufo.h - Super UFO for uCON64 + +Copyright (c) 2003 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef UFO_H +#define UFO_H + +extern const st_getopt2_t ufo_usage[]; + +/* +Super UFO Pro 8 Header Format (researched by John Weidman) + +Byte-Offset Function +----------- --------------------------------------------------- +00-01 Chunk size: 04 00 == 4 Mbit (same format as the DX2) +02 LoROM games: 0x40 == continue loading another chunk after this one. + 0x00 == This is the last chunk. + HiROM games: 0x40 == more work to figure this out -- maybe interleave info + 0x10 == more work to figure this out -- maybe interleave info + 0x00 == This is the last chunk. +03-07 0x00 +08-0F 53 55 50 45 52 55 46 4F (SUPERUFO) +10 0x01 == This file is a ROM image file +11 ROM size: 04 == 4 Mb, 0x18 == 24 Mb, 0x20 == 32 Mb, etc. +12 ROM format: 00 == HiROM, 01 == LoROM + +==== Start SRAM address mapping config =============== + +13 SRAM Size: + 00: 0Kb + 01: 16Kb + 02: 64Kb + 03: 256Kb + 04-07: Not used + 08: XXXKb (Used for sram sizes above 256Kb, like 1024Kb) +14 SRAM A15 control: + 00: A15 not used for SRAM control? + Use this for HiROM games + LoROM: Use this if SRAM size = 0Kb (no SRAM) + 01: A15=X selects SRAM + 02: A15=0 selects SRAM + 03: A15=1 selects SRAM +15 SRAM A20 and A21 control: + Bits 3:2 + 00: A21=x selects SRAM + 01: Not used? + 10: A21=0 selects SRAM + 11: A21=1 selects SRAM + Bits 1:0 + 00: A20=x selects SRAM + 01: Not used? + 10: A20=0 selects SRAM + 11: A20=1 selects SRAM +16 SRAM A22 and A23 control: + Bits 3:2 + 00: A23=x selects SRAM + 01: Not used? + 10: A23=0 selects SRAM + 11: A23=1 selects SRAM + Bits 1:0 + 00: A22=x selects SRAM + 01: Not used? + 10: A22=0 selects SRAM + 11: A22=1 selects SRAM +17 SRAM type + 0x00: Linear (HiROM) + 0x03: Skip (LoROM) + +==== End SRAM address mapping config ================ + +18-1FF 00 + +LoROM SRAM header +========================================================= + +The SRAM mapping I would try first for LoROM games is: + +0Kb SRAM +0012-0017 01 00 00 00 02 00 +0Kb LoROM DSP +0012-0017 01 00 01 0C 00 03 + +Note: LoROM DSPs with SRAM don't seem to work on the Super UFO + (For reference, no LoROM DSP carts work on the SWC DX2) + +Non 0 SRAM - default map (map low halves of banks 7x to SRAM) +0012-0017 01 ss 02 0F 03 03 +Non 0 SRAM - alternate map (map all of banks 7x to SRAM -- will not work for > 28 Mbit + games ) +0012-0017 01 ss 01 0F 03 03 + +HiROM SRAM header +========================================================== + +0Kb SRAM +0012-0017 00 00 00 00 02 00 +Non 0 SRAM +0012-0017 00 ss 00 0C 02 00 (Hopefully this will work for everything?) + +If you find an SRAM protected game that doesn't work with the above mapping try: +0012-0017 00 ss 00 03 02 00 (seen in a couple of games but should work with above + mapping too) +-- +For Tales of Phantasia or Dai Kaijyu Monogatari II + +0012-0017 00 ss 00 00 0E 00 (Unverified) +*/ +typedef struct st_ufo_header +{ + unsigned char size_low; + unsigned char size_high; + unsigned char multi; + unsigned char pad[5]; + unsigned char id[8]; // "SUPERUFO" + unsigned char isrom; + unsigned char size; + unsigned char banktype; + unsigned char sram_size; + unsigned char sram_a15; + unsigned char sram_a20_a21; + unsigned char sram_a22_a23; + unsigned char sram_type; + unsigned char pad2[488]; +} st_ufo_header_t; + +#define UFO_HEADER_LEN (sizeof (st_ufo_header_t)) + +#endif // UFO_H diff --git a/ucon64/2.0/src/backup/yoko.c b/ucon64/2.0/src/backup/yoko.c new file mode 100644 index 0000000..fcfbf94 --- /dev/null +++ b/ucon64/2.0/src/backup/yoko.c @@ -0,0 +1,46 @@ +/* +yoko.c - support for Yoko backup unit (Atari 2600, etc.) + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include "misc/misc.h" +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "yoko.h" + + +const st_getopt2_t yoko_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "YOKO backup unit", + NULL + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; diff --git a/ucon64/2.0/src/backup/yoko.h b/ucon64/2.0/src/backup/yoko.h new file mode 100644 index 0000000..7be8729 --- /dev/null +++ b/ucon64/2.0/src/backup/yoko.h @@ -0,0 +1,26 @@ +/* +yoko.h - minimal support for Yoko backup unit (Atari 2600, etc.) + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef YOKO_H +#define YOKO_H + +extern const st_getopt2_t yoko_usage[]; + +#endif diff --git a/ucon64/2.0/src/backup/z64.c b/ucon64/2.0/src/backup/z64.c new file mode 100644 index 0000000..c95c721 --- /dev/null +++ b/ucon64/2.0/src/backup/z64.c @@ -0,0 +1,46 @@ +/* +z64.c - Mr. Backup Z64 support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include "misc/misc.h" +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "z64.h" + + +const st_getopt2_t z64_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Mr. Backup Z64", + NULL + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; diff --git a/ucon64/2.0/src/backup/z64.h b/ucon64/2.0/src/backup/z64.h new file mode 100644 index 0000000..e82d2c6 --- /dev/null +++ b/ucon64/2.0/src/backup/z64.h @@ -0,0 +1,26 @@ +/* +z64.h - Z64 support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef Z64_H +#define Z64_H + +extern const st_getopt2_t z64_usage[]; + +#endif diff --git a/ucon64/2.0/src/config.h.in b/ucon64/2.0/src/config.h.in new file mode 100644 index 0000000..bf20827 --- /dev/null +++ b/ucon64/2.0/src/config.h.in @@ -0,0 +1,128 @@ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* enable debug output (default: no) */ +#undef DEBUG + +/* enable dynamic loading of add-on libraries (default: yes) */ +#undef DLOPEN + +/* Define to 1 if you have the header file. */ +#undef HAVE_BYTESWAP_H + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_DIRENT_H + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#undef HAVE_DOPRNT + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +#undef HAVE_NDIR_H + +/* Define to 1 if you have the `realpath' function. */ +#undef HAVE_REALPATH + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strftime' function. */ +#undef HAVE_STRFTIME + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_SYS_DIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IO_H + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_SYS_NDIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vprintf' function. */ +#undef HAVE_VPRINTF + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* enable usage of color (default: yes) */ +#undef USE_ANSI_COLOR + +/* enable libdiscmage (default: yes) */ +#undef USE_DISCMAGE + +/* enable libcd64 (default: no) */ +#undef USE_LIBCD64 + +/* enable support for parallel port backup units (default: yes) */ +#undef USE_PARALLEL + +/* use ppdev for parallel port I/O (default: no) */ +#undef USE_PPDEV + +/* build with (lib)usb support (default: no) */ +#undef USE_USB + +/* build with gzip and zip support (default: yes) */ +#undef USE_ZLIB + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `int' if doesn't define. */ +#undef gid_t + +/* Define as `__inline' if that's what the C compiler calls it, or to nothing + if it is not supported. */ +#undef inline + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to `unsigned' if does not define. */ +#undef size_t + +/* Define to `int' if doesn't define. */ +#undef uid_t diff --git a/ucon64/2.0/src/config.h.orig b/ucon64/2.0/src/config.h.orig new file mode 100644 index 0000000..ca2d95b --- /dev/null +++ b/ucon64/2.0/src/config.h.orig @@ -0,0 +1,129 @@ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* enable debug output (default: no) */ +/* #undef DEBUG */ + +/* enable dynamic loading of add-on libraries (default: yes) */ +#define DLOPEN 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_BYTESWAP_H */ + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_INTTYPES_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +/* #undef HAVE_NDIR_H */ + +/* Define to 1 if you have the `realpath' function. */ +/* #undef HAVE_REALPATH */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_STDINT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strftime' function. */ +#define HAVE_STRFTIME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_IO_H */ + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "noisyb@gmx.net" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "uCON64" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "uCON64 2.0.0" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "ucon64" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "2.0.0" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* enable usage of color (default: yes) */ +#define USE_ANSI_COLOR 1 + +/* enable libdiscmage (default: yes) */ +#define USE_DISCMAGE 1 + +/* enable libcd64 (default: no) */ +#define USE_LIBCD64 1 + +/* enable support for parallel port backup units (default: yes) */ +#define USE_PARALLEL 1 + +/* use ppdev for parallel port I/O (default: no) */ +/* #undef USE_PPDEV */ + +/* build with (lib)usb support (default: no) */ +/* #undef USE_USB */ + +/* build with gzip and zip support (default: yes) */ +//#define USE_ZLIB 1 + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `int' if doesn't define. */ +/* #undef gid_t */ + +/* Define as `__inline' if that's what the C compiler calls it, or to nothing + if it is not supported. */ +/* #undef inline */ + +/* Define to `int' if does not define. */ +/* #undef pid_t */ + +/* Define to `unsigned' if does not define. */ +/* #undef size_t */ + +/* Define to `int' if doesn't define. */ +/* #undef uid_t */ diff --git a/ucon64/2.0/src/config.h.vc6 b/ucon64/2.0/src/config.h.vc6 new file mode 100644 index 0000000..b429936 --- /dev/null +++ b/ucon64/2.0/src/config.h.vc6 @@ -0,0 +1,129 @@ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* enable debug output (default: no) */ +/* #undef DEBUG */ + +/* enable dynamic loading of add-on libraries (default: yes) */ +#define DLOPEN 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_BYTESWAP_H */ + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_DIRENT_H */ + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_INTTYPES_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +/* #undef HAVE_NDIR_H */ + +/* Define to 1 if you have the `realpath' function. */ +/* #undef HAVE_REALPATH */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_STDINT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strftime' function. */ +#define HAVE_STRFTIME 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_STRINGS_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_IO_H */ + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_UNISTD_H */ + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "noisyb@gmx.net" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "uCON64" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "uCON64 2.0.0" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "ucon64" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "2.0.0" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* enable usage of color (default: yes) */ +#define USE_ANSI_COLOR 1 + +/* enable libdiscmage (default: yes) */ +#define USE_DISCMAGE 1 + +/* enable libcd64 (default: no) */ +#define USE_LIBCD64 1 + +/* enable support for parallel port backup units (default: yes) */ +#define USE_PARALLEL 1 + +/* use ppdev for parallel port I/O (default: no) */ +/* #undef USE_PPDEV */ + +/* build with (lib)usb support (default: no) */ +/* #undef USE_USB */ + +/* build with gzip and zip support (default: yes) */ +//#define USE_ZLIB 1 + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `int' if doesn't define. */ +/* #undef gid_t */ + +/* Define as `__inline' if that's what the C compiler calls it, or to nothing + if it is not supported. */ +/* #undef inline */ + +/* Define to `int' if does not define. */ +/* #undef pid_t */ + +/* Define to `unsigned' if does not define. */ +/* #undef size_t */ + +/* Define to `int' if doesn't define. */ +/* #undef uid_t */ diff --git a/ucon64/2.0/src/configure b/ucon64/2.0/src/configure new file mode 100755 index 0000000..c9ab122 --- /dev/null +++ b/ucon64/2.0/src/configure @@ -0,0 +1,5619 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.58 for uCON64 2.0.0. +# +# Report bugs to . +# +# Copyright (C) 2003 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME='uCON64' +PACKAGE_TARNAME='ucon64' +PACKAGE_VERSION='2.0.0' +PACKAGE_STRING='uCON64 2.0.0' +PACKAGE_BUGREPORT='noisyb@gmx.net' + +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP EGREP DEFINE_DLOPEN_MAKE DEFINE_DISCMAGE_MAKE DEFINE_LIBCD64_MAKE LIBI386_MAKE DEFINE_ZLIB_MAKE DEFINE_USB_MAKE LIBOBJS RANLIB ac_ct_RANLIB INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LTLIBOBJS' +ac_subst_files='' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CPP_set=${CPP+set} +ac_env_CPP_value=$CPP +ac_cv_env_CPP_set=${CPP+set} +ac_cv_env_CPP_value=$CPP + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures uCON64 2.0.0 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +_ACEOF + + cat <<_ACEOF +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of uCON64 2.0.0:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-debug enable debug output (default: no) + --enable-parallel enable support for parallel port backup units + (default: yes) + --enable-ppdev use ppdev for parallel port I/O (default: no) + --enable-ansi-color enable usage of color (default: yes) + --enable-dload enable dynamic loading of add-on libraries (default: + yes) + --enable-discmage enable libdiscmage (default: yes) + --enable-libcd64 enable libcd64 (default: no) + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-zlib build with gzip and zip support (default: yes) + --with-libusb build with (lib)usb support (default: no) + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac +case "$ac_dir" in +.) ac_abs_builddir=$ac_builddir;; +*) + case $ac_builddir in + .) ac_abs_builddir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_builddir=$ac_builddir;; + *) ac_abs_builddir="$ac_dir"/$ac_builddir;; + esac;; +esac +case "$ac_dir" in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir="$ac_dir"/${ac_top_builddir}.;; + esac;; +esac +case "$ac_dir" in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir="$ac_dir"/$ac_srcdir;; + esac;; +esac +case "$ac_dir" in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir="$ac_dir"/$ac_top_srcdir;; + esac;; +esac + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + echo + $SHELL $ac_srcdir/configure.gnu --help=recursive + elif test -f $ac_srcdir/configure; then + echo + $SHELL $ac_srcdir/configure --help=recursive + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF +uCON64 configure 2.0.0 +generated by GNU Autoconf 2.58 + +Copyright (C) 2003 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by uCON64 $as_me 2.0.0, which was +generated by GNU Autoconf 2.58. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_sep= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + + + + + + + + + + + + + ac_config_headers="$ac_config_headers config.h libdiscmage/config.h" + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + for ac_prog in gcc egcs cc + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in gcc egcs cc +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std1 is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std1. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6 +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6 +if test "${ac_cv_prog_egrep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 +echo "${ECHO_T}$ac_cv_prog_egrep" >&6 + EGREP=$ac_cv_prog_egrep + + +if test $ac_cv_c_compiler_gnu = yes; then + echo "$as_me:$LINENO: checking whether $CC needs -traditional" >&5 +echo $ECHO_N "checking whether $CC needs -traditional... $ECHO_C" >&6 +if test "${ac_cv_prog_gcc_traditional+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_pattern="Autoconf.*'x'" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +Autoconf TIOCGETP +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +else + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +Autoconf TCGETA +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_gcc_traditional" >&5 +echo "${ECHO_T}$ac_cv_prog_gcc_traditional" >&6 + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + + + +echo "$as_me:$LINENO: checking whether debug output is enabled" >&5 +echo $ECHO_N "checking whether debug output is enabled... $ECHO_C" >&6 +# Check whether --enable-debug or --disable-debug was given. +if test "${enable_debug+set}" = set; then + enableval="$enable_debug" + enable_debug=$enableval +else + enable_debug=no +fi; +if test $enable_debug = yes; then + +cat >>confdefs.h <<\_ACEOF +#define DEBUG 1 +_ACEOF + +fi +echo "$as_me:$LINENO: result: $enable_debug" >&5 +echo "${ECHO_T}$enable_debug" >&6 + + +echo "$as_me:$LINENO: checking whether support for parallel port backup units is enabled" >&5 +echo $ECHO_N "checking whether support for parallel port backup units is enabled... $ECHO_C" >&6 +# Check whether --enable-parallel or --disable-parallel was given. +if test "${enable_parallel+set}" = set; then + enableval="$enable_parallel" + enable_parallel=$enableval +else + enable_parallel=yes +fi; +if test $enable_parallel = yes; then + +cat >>confdefs.h <<\_ACEOF +#define USE_PARALLEL 1 +_ACEOF + +fi +echo "$as_me:$LINENO: result: $enable_parallel" >&5 +echo "${ECHO_T}$enable_parallel" >&6 + + + + +echo "$as_me:$LINENO: checking whether to use ppdev" >&5 +echo $ECHO_N "checking whether to use ppdev... $ECHO_C" >&6 +# Check whether --enable-ppdev or --disable-ppdev was given. +if test "${enable_ppdev+set}" = set; then + enableval="$enable_ppdev" + enable_ppdev=$enableval +else + enable_ppdev=no +fi; +if test $enable_ppdev = yes; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + enable_ppdev=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +enable_ppdev=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +if test $enable_ppdev = yes; then + +cat >>confdefs.h <<\_ACEOF +#define USE_PPDEV 1 +_ACEOF + +fi +echo "$as_me:$LINENO: result: $enable_ppdev" >&5 +echo "${ECHO_T}$enable_ppdev" >&6 + + +echo "$as_me:$LINENO: checking whether the use of color is enabled" >&5 +echo $ECHO_N "checking whether the use of color is enabled... $ECHO_C" >&6 +# Check whether --enable-ansi_color or --disable-ansi_color was given. +if test "${enable_ansi_color+set}" = set; then + enableval="$enable_ansi_color" + enable_ansi_color=$enableval +else + enable_ansi_color=yes +fi; +if test $enable_ansi_color = yes; then + +cat >>confdefs.h <<\_ACEOF +#define USE_ANSI_COLOR 1 +_ACEOF + +fi +echo "$as_me:$LINENO: result: $enable_ansi_color" >&5 +echo "${ECHO_T}$enable_ansi_color" >&6 + + +echo "$as_me:$LINENO: checking whether add-on libraries are dynamically loaded" >&5 +echo $ECHO_N "checking whether add-on libraries are dynamically loaded... $ECHO_C" >&6 +# Check whether --enable-dload or --disable-dload was given. +if test "${enable_dload+set}" = set; then + enableval="$enable_dload" + enable_dload=$enableval +else + enable_dload=yes +fi; +if test $enable_dload = yes; then + +cat >>confdefs.h <<\_ACEOF +#define DLOPEN 1 +_ACEOF + + DEFINE_DLOPEN_MAKE="DLOPEN=1" +fi +echo "$as_me:$LINENO: result: $enable_dload" >&5 +echo "${ECHO_T}$enable_dload" >&6 + + + +echo "$as_me:$LINENO: checking whether libdiscmage is enabled" >&5 +echo $ECHO_N "checking whether libdiscmage is enabled... $ECHO_C" >&6 +# Check whether --enable-discmage or --disable-discmage was given. +if test "${enable_discmage+set}" = set; then + enableval="$enable_discmage" + enable_discmage=$enableval +else + enable_discmage=yes +fi; +if test $enable_discmage = yes; then + +cat >>confdefs.h <<\_ACEOF +#define USE_DISCMAGE 1 +_ACEOF + + DEFINE_DISCMAGE_MAKE="USE_DISCMAGE=1" +fi +echo "$as_me:$LINENO: result: $enable_discmage" >&5 +echo "${ECHO_T}$enable_discmage" >&6 + + + +echo "$as_me:$LINENO: checking whether libcd64 is enabled" >&5 +echo $ECHO_N "checking whether libcd64 is enabled... $ECHO_C" >&6 +# Check whether --enable-libcd64 or --disable-libcd64 was given. +if test "${enable_libcd64+set}" = set; then + enableval="$enable_libcd64" + enable_libcd64=$enableval +else + enable_libcd64=no +fi; +if test $enable_libcd64 = yes; then + +cat >>confdefs.h <<\_ACEOF +#define USE_LIBCD64 1 +_ACEOF + + DEFINE_LIBCD64_MAKE="USE_LIBCD64=1" +else + LIBI386_MAKE="-li386" +fi +echo "$as_me:$LINENO: result: $enable_libcd64" >&5 +echo "${ECHO_T}$enable_libcd64" >&6 + + + + +echo "$as_me:$LINENO: checking for zlib" >&5 +echo $ECHO_N "checking for zlib... $ECHO_C" >&6 + +# Check whether --with-zlib or --without-zlib was given. +if test "${with_zlib+set}" = set; then + withval="$with_zlib" + with_zlib=$withval +else + with_zlib=yes +fi; +if test $with_zlib = yes; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + with_zlib=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +with_zlib=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +if test $with_zlib = yes; then + +cat >>confdefs.h <<\_ACEOF +#define USE_ZLIB 1 +_ACEOF + + DEFINE_ZLIB_MAKE="USE_ZLIB=1" +fi +echo "$as_me:$LINENO: result: $with_zlib" >&5 +echo "${ECHO_T}$with_zlib" >&6 + + + +echo "$as_me:$LINENO: checking for libusb" >&5 +echo $ECHO_N "checking for libusb... $ECHO_C" >&6 + +# Check whether --with-libusb or --without-libusb was given. +if test "${with_libusb+set}" = set; then + withval="$with_libusb" + with_libusb=$withval +else + with_libusb=no +fi; +if test $with_libusb = yes; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + with_libusb=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +with_libusb=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +if test $with_libusb = yes; then + +cat >>confdefs.h <<\_ACEOF +#define USE_USB 1 +_ACEOF + + DEFINE_USB_MAKE="USE_USB=1" +fi +echo "$as_me:$LINENO: result: $with_libusb" >&5 +echo "${ECHO_T}$with_libusb" >&6 + + + + + + + + +ac_header_dirent=no +for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do + as_ac_Header=`echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_hdr that defines DIR" >&5 +echo $ECHO_N "checking for $ac_hdr that defines DIR... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include <$ac_hdr> + +int +main () +{ +if ((DIR *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_hdr" | $as_tr_cpp` 1 +_ACEOF + +ac_header_dirent=$ac_hdr; break +fi + +done +# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. +if test $ac_header_dirent = dirent.h; then + echo "$as_me:$LINENO: checking for library containing opendir" >&5 +echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6 +if test "${ac_cv_search_opendir+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_opendir=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir (); +int +main () +{ +opendir (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_opendir="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_opendir" = no; then + for ac_lib in dir; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir (); +int +main () +{ +opendir (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_opendir="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5 +echo "${ECHO_T}$ac_cv_search_opendir" >&6 +if test "$ac_cv_search_opendir" != no; then + test "$ac_cv_search_opendir" = "none required" || LIBS="$ac_cv_search_opendir $LIBS" + +fi + +else + echo "$as_me:$LINENO: checking for library containing opendir" >&5 +echo $ECHO_N "checking for library containing opendir... $ECHO_C" >&6 +if test "${ac_cv_search_opendir+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_func_search_save_LIBS=$LIBS +ac_cv_search_opendir=no +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir (); +int +main () +{ +opendir (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_opendir="none required" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test "$ac_cv_search_opendir" = no; then + for ac_lib in x; do + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir (); +int +main () +{ +opendir (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_search_opendir="-l$ac_lib" +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done +fi +LIBS=$ac_func_search_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5 +echo "${ECHO_T}$ac_cv_search_opendir" >&6 +if test "$ac_cv_search_opendir" != no; then + test "$ac_cv_search_opendir" = "none required" || LIBS="$ac_cv_search_opendir $LIBS" + +fi + +fi + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + + + + +for ac_header in fcntl.h unistd.h byteswap.h inttypes.h sys/io.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ----------------------------- ## +## Report this to noisyb@gmx.net ## +## ----------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 +echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6 +if test "${ac_cv_c_const+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset x; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *ccp; + char **p; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + ccp = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++ccp; + p = (char**) ccp; + ccp = (char const *const *) p; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + } +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_const=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_c_const=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 +echo "${ECHO_T}$ac_cv_c_const" >&6 +if test $ac_cv_c_const = no; then + +cat >>confdefs.h <<\_ACEOF +#define const +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for uid_t in sys/types.h" >&5 +echo $ECHO_N "checking for uid_t in sys/types.h... $ECHO_C" >&6 +if test "${ac_cv_type_uid_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "uid_t" >/dev/null 2>&1; then + ac_cv_type_uid_t=yes +else + ac_cv_type_uid_t=no +fi +rm -f conftest* + +fi +echo "$as_me:$LINENO: result: $ac_cv_type_uid_t" >&5 +echo "${ECHO_T}$ac_cv_type_uid_t" >&6 +if test $ac_cv_type_uid_t = no; then + +cat >>confdefs.h <<\_ACEOF +#define uid_t int +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define gid_t int +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for inline" >&5 +echo $ECHO_N "checking for inline... $ECHO_C" >&6 +if test "${ac_cv_c_inline+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_inline=$ac_kw; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_inline" >&5 +echo "${ECHO_T}$ac_cv_c_inline" >&6 + + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; +esac + +echo "$as_me:$LINENO: checking for pid_t" >&5 +echo $ECHO_N "checking for pid_t... $ECHO_C" >&6 +if test "${ac_cv_type_pid_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((pid_t *) 0) + return 0; +if (sizeof (pid_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_pid_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_pid_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_pid_t" >&5 +echo "${ECHO_T}$ac_cv_type_pid_t" >&6 +if test $ac_cv_type_pid_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for size_t" >&5 +echo $ECHO_N "checking for size_t... $ECHO_C" >&6 +if test "${ac_cv_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((size_t *) 0) + return 0; +if (sizeof (size_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_size_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_size_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +echo "${ECHO_T}$ac_cv_type_size_t" >&6 +if test $ac_cv_type_size_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned +_ACEOF + +fi + + +echo "$as_me:$LINENO: checking for working memcmp" >&5 +echo $ECHO_N "checking for working memcmp... $ECHO_C" >&6 +if test "${ac_cv_func_memcmp_working+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_memcmp_working=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Some versions of memcmp are not 8-bit clean. */ + char c0 = 0x40, c1 = 0x80, c2 = 0x81; + if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0) + exit (1); + + /* The Next x86 OpenStep bug shows up only when comparing 16 bytes + or more and with at least one buffer not starting on a 4-byte boundary. + William Lewis provided this test program. */ + { + char foo[21]; + char bar[21]; + int i; + for (i = 0; i < 4; i++) + { + char *a = foo + i; + char *b = bar + i; + strcpy (a, "--------01111111"); + strcpy (b, "--------10000000"); + if (memcmp (a, b, 16) >= 0) + exit (1); + } + exit (0); + } + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_memcmp_working=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_memcmp_working=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_memcmp_working" >&5 +echo "${ECHO_T}$ac_cv_func_memcmp_working" >&6 +test $ac_cv_func_memcmp_working = no && case $LIBOBJS in + "memcmp.$ac_objext" | \ + *" memcmp.$ac_objext" | \ + "memcmp.$ac_objext "* | \ + *" memcmp.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS memcmp.$ac_objext" ;; +esac + + + +for ac_func in strftime +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + # strftime is in -lintl on SCO UNIX. +echo "$as_me:$LINENO: checking for strftime in -lintl" >&5 +echo $ECHO_N "checking for strftime in -lintl... $ECHO_C" >&6 +if test "${ac_cv_lib_intl_strftime+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lintl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char strftime (); +int +main () +{ +strftime (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_intl_strftime=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_intl_strftime=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_intl_strftime" >&5 +echo "${ECHO_T}$ac_cv_lib_intl_strftime" >&6 +if test $ac_cv_lib_intl_strftime = yes; then + cat >>confdefs.h <<\_ACEOF +#define HAVE_STRFTIME 1 +_ACEOF + +LIBS="-lintl $LIBS" +fi + +fi +done + + +for ac_func in vprintf +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +echo "$as_me:$LINENO: checking for _doprnt" >&5 +echo $ECHO_N "checking for _doprnt... $ECHO_C" >&6 +if test "${ac_cv_func__doprnt+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define _doprnt to an innocuous variant, in case declares _doprnt. + For example, HP-UX 11i declares gettimeofday. */ +#define _doprnt innocuous__doprnt + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char _doprnt (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef _doprnt + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char _doprnt (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub__doprnt) || defined (__stub____doprnt) +choke me +#else +char (*f) () = _doprnt; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != _doprnt; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func__doprnt=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func__doprnt=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func__doprnt" >&5 +echo "${ECHO_T}$ac_cv_func__doprnt" >&6 +if test $ac_cv_func__doprnt = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_DOPRNT 1 +_ACEOF + +fi + +fi +done + + + +for ac_func in realpath +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + echo "$as_me:$LINENO: result: $RANLIB" >&5 +echo "${ECHO_T}$RANLIB" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":" +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 +echo "${ECHO_T}$ac_ct_RANLIB" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + RANLIB=$ac_ct_RANLIB +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 +echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + + + ac_config_files="$ac_config_files Makefile libdiscmage/Makefile" +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if diff $cache_file confcache >/dev/null 2>&1; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by uCON64 $as_me 2.0.0, which was +generated by GNU Autoconf 2.58. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to ." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +uCON64 config.status 2.0.0 +configured by $0, generated by GNU Autoconf 2.58, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2003 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +INSTALL="$INSTALL" +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + ac_shift=: + ;; + -*) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + + + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "libdiscmage/Makefile" ) CONFIG_FILES="$CONFIG_FILES libdiscmage/Makefile" ;; + "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "libdiscmage/config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS libdiscmage/config.h" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@CPP@,$CPP,;t t +s,@EGREP@,$EGREP,;t t +s,@DEFINE_DLOPEN_MAKE@,$DEFINE_DLOPEN_MAKE,;t t +s,@DEFINE_DISCMAGE_MAKE@,$DEFINE_DISCMAGE_MAKE,;t t +s,@DEFINE_LIBCD64_MAKE@,$DEFINE_LIBCD64_MAKE,;t t +s,@LIBI386_MAKE@,$LIBI386_MAKE,;t t +s,@DEFINE_ZLIB_MAKE@,$DEFINE_ZLIB_MAKE,;t t +s,@DEFINE_USB_MAKE@,$DEFINE_USB_MAKE,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@RANLIB@,$RANLIB,;t t +s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac +case "$ac_dir" in +.) ac_abs_builddir=$ac_builddir;; +*) + case $ac_builddir in + .) ac_abs_builddir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_builddir=$ac_builddir;; + *) ac_abs_builddir="$ac_dir"/$ac_builddir;; + esac;; +esac +case "$ac_dir" in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir="$ac_dir"/${ac_top_builddir}.;; + esac;; +esac +case "$ac_dir" in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir="$ac_dir"/$ac_srcdir;; + esac;; +esac +case "$ac_dir" in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir="$ac_dir";; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir="$ac_dir"/$ac_top_srcdir;; + esac;; +esac + + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_builddir$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@abs_srcdir@,$ac_abs_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t +s,@builddir@,$ac_builddir,;t t +s,@abs_builddir@,$ac_abs_builddir,;t t +s,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_HEADER section. +# + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='[ ].*$,\1#\2' +ac_dC=' ' +ac_dD=',;t' +# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='$,\1#\2define\3' +ac_uC=' ' +ac_uD=',;t' + +for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + # Do quote $f, to prevent DOS paths from being IFS'd. + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } + # Remove the trailing spaces. + sed 's/[ ]*$//' $ac_file_inputs >$tmp/in + +_ACEOF + +# Transform confdefs.h into two sed scripts, `conftest.defines' and +# `conftest.undefs', that substitutes the proper values into +# config.h.in to produce config.h. The first handles `#define' +# templates, and the second `#undef' templates. +# And first: Protect against being on the right side of a sed subst in +# config.status. Protect against being in an unquoted here document +# in config.status. +rm -f conftest.defines conftest.undefs +# Using a here document instead of a string reduces the quoting nightmare. +# Putting comments in sed scripts is not portable. +# +# `end' is used to avoid that the second main sed command (meant for +# 0-ary CPP macros) applies to n-ary macro definitions. +# See the Autoconf documentation for `clear'. +cat >confdef2sed.sed <<\_ACEOF +s/[\\&,]/\\&/g +s,[\\$`],\\&,g +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp +t end +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp +: end +_ACEOF +# If some macros were called several times there might be several times +# the same #defines, which is useless. Nevertheless, we may not want to +# sort them, since we want the *last* AC-DEFINE to be honored. +uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines +sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs +rm -f confdef2sed.sed + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >>conftest.undefs <<\_ACEOF +s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, +_ACEOF + +# Break up conftest.defines because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS +echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS +echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS +echo ' :' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.defines >/dev/null +do + # Write a limited-size here document to $tmp/defines.sed. + echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#define' lines. + echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/defines.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines +echo ' fi # grep' >>$CONFIG_STATUS +echo >>$CONFIG_STATUS + +# Break up conftest.undefs because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #undef templates' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.undefs >/dev/null +do + # Write a limited-size here document to $tmp/undefs.sed. + echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#undef' + echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/undefs.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail + rm -f conftest.undefs + mv conftest.tail conftest.undefs +done +rm -f conftest.undefs + +cat >>$CONFIG_STATUS <<\_ACEOF + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + echo "/* Generated by configure. */" >$tmp/config.h + else + echo "/* $ac_file. Generated by configure. */" >$tmp/config.h + fi + cat $tmp/in >>$tmp/config.h + rm -f $tmp/in + if test x"$ac_file" != x-; then + if diff $ac_file $tmp/config.h >/dev/null 2>&1; then + { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + rm -f $ac_file + mv $tmp/config.h $ac_file + fi + else + cat $tmp/config.h + rm -f $tmp/config.h + fi +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + + +echo +echo NOTE: On non-Linux systems you might need to use gmake instead of make diff --git a/ucon64/2.0/src/configure.in b/ucon64/2.0/src/configure.in new file mode 100644 index 0000000..7c2f7d6 --- /dev/null +++ b/ucon64/2.0/src/configure.in @@ -0,0 +1,183 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(uCON64, 2.0.0, noisyb@gmx.net) +AC_CONFIG_HEADER(config.h libdiscmage/config.h) + + +dnl Checks for programs. +AC_PROG_CC(gcc egcs cc) +AC_PROG_GCC_TRADITIONAL + + +AC_MSG_CHECKING(whether debug output is enabled) +AC_ARG_ENABLE(debug, + AC_HELP_STRING([--enable-debug], + [enable debug output (default: no)]), + enable_debug=$enableval, + enable_debug=no) +if test $enable_debug = yes; then + AC_DEFINE(DEBUG, 1, [enable debug output (default: no)]) +fi +AC_MSG_RESULT($enable_debug) + + +AC_MSG_CHECKING(whether support for parallel port backup units is enabled) +AC_ARG_ENABLE(parallel, + AC_HELP_STRING([--enable-parallel], + [enable support for parallel port backup units (default: yes)]), + enable_parallel=$enableval, + enable_parallel=yes) +if test $enable_parallel = yes; then + AC_DEFINE(USE_PARALLEL, 1, [enable support for parallel port backup units (default: yes)]) +fi +AC_MSG_RESULT($enable_parallel) + + +dnl AC_MSG_CHECKING(whether support for serial port backup units is enabled) +dnl AC_ARG_ENABLE(serial, +dnl AC_HELP_STRING([--enable-serial], +dnl [enable support for serial port backup units (default: yes)]), +dnl enable_serial=$enableval, +dnl enable_serial=yes) +dnl if test $enable_serial = yes; then +dnl AC_DEFINE(USE_SERIAL, 1, [enable support for serial port backup units (default: yes)]) +dnl fi +dnl AC_MSG_RESULT($enable_serial) + + +AC_MSG_CHECKING(whether to use ppdev) +AC_ARG_ENABLE(ppdev, + AC_HELP_STRING([--enable-ppdev], + [use ppdev for parallel port I/O (default: no)]), + enable_ppdev=$enableval, + enable_ppdev=no) +if test $enable_ppdev = yes; then + AC_TRY_COMPILE([#include ], , enable_ppdev=yes, enable_ppdev=no) +fi +if test $enable_ppdev = yes; then + AC_DEFINE(USE_PPDEV, 1, [use ppdev for parallel port I/O (default: no)]) +fi +AC_MSG_RESULT($enable_ppdev) + + +AC_MSG_CHECKING(whether the use of color is enabled) +AC_ARG_ENABLE(ansi_color, + AC_HELP_STRING([--enable-ansi-color], + [enable usage of color (default: yes)]), + enable_ansi_color=$enableval, + enable_ansi_color=yes) +if test $enable_ansi_color = yes; then + AC_DEFINE(USE_ANSI_COLOR, 1, [enable usage of color (default: yes)]) +fi +AC_MSG_RESULT($enable_ansi_color) + + +AC_MSG_CHECKING(whether add-on libraries are dynamically loaded) +AC_ARG_ENABLE(dload, + AC_HELP_STRING([--enable-dload], + [enable dynamic loading of add-on libraries (default: yes)]), + enable_dload=$enableval, + enable_dload=yes) +if test $enable_dload = yes; then + AC_DEFINE(DLOPEN, 1, [enable dynamic loading of add-on libraries (default: yes)]) + DEFINE_DLOPEN_MAKE="DLOPEN=1" +fi +AC_MSG_RESULT($enable_dload) +AC_SUBST(DEFINE_DLOPEN_MAKE) + + +AC_MSG_CHECKING(whether libdiscmage is enabled) +AC_ARG_ENABLE(discmage, + AC_HELP_STRING([--enable-discmage], + [enable libdiscmage (default: yes)]), + enable_discmage=$enableval, + enable_discmage=yes) +if test $enable_discmage = yes; then + AC_DEFINE(USE_DISCMAGE, 1, [enable libdiscmage (default: yes)]) + DEFINE_DISCMAGE_MAKE="USE_DISCMAGE=1" +fi +AC_MSG_RESULT($enable_discmage) +AC_SUBST(DEFINE_DISCMAGE_MAKE) + + +dnl This belongs here, not at the checks for libraries. We don't have to check +dnl if libcd64 is present as it is part of our source tree. +AC_MSG_CHECKING(whether libcd64 is enabled) +AC_ARG_ENABLE(libcd64, + AC_HELP_STRING([--enable-libcd64], + [enable libcd64 (default: no)]), + enable_libcd64=$enableval, + enable_libcd64=no) +if test $enable_libcd64 = yes; then + AC_DEFINE(USE_LIBCD64, 1, [enable libcd64 (default: no)]) + DEFINE_LIBCD64_MAKE="USE_LIBCD64=1" +else + dnl libi386 is necessary under OpenBSD, but only if libcd64 isn't enabled. The + dnl reason is that libcd64 already includes libi386. + LIBI386_MAKE="-li386" +fi +AC_MSG_RESULT($enable_libcd64) +AC_SUBST(DEFINE_LIBCD64_MAKE) +AC_SUBST(LIBI386_MAKE) + + +dnl Checks for libraries. +AC_MSG_CHECKING(for zlib) +AC_ARG_WITH(zlib, + AC_HELP_STRING([--with-zlib], + [build with gzip and zip support (default: yes)]), + with_zlib=$withval, + with_zlib=yes) +if test $with_zlib = yes; then + AC_TRY_COMPILE([#include ], , with_zlib=yes, with_zlib=no) +fi +if test $with_zlib = yes; then + AC_DEFINE(USE_ZLIB, 1, [build with gzip and zip support (default: yes)]) + DEFINE_ZLIB_MAKE="USE_ZLIB=1" +fi +AC_MSG_RESULT($with_zlib) +AC_SUBST(DEFINE_ZLIB_MAKE) + + +AC_MSG_CHECKING(for libusb) +AC_ARG_WITH(libusb, + AC_HELP_STRING([--with-libusb], + [build with (lib)usb support (default: no)]), + with_libusb=$withval, + with_libusb=no) +if test $with_libusb = yes; then + AC_TRY_COMPILE([#include ], , with_libusb=yes, with_libusb=no) +fi +if test $with_libusb = yes; then + AC_DEFINE(USE_USB, 1, [build with (lib)usb support (default: no)]) + DEFINE_USB_MAKE="USE_USB=1" +fi +AC_MSG_RESULT($with_libusb) +AC_SUBST(DEFINE_USB_MAKE) + + +dnl Checks for header files. +AC_HEADER_DIRENT +AC_HEADER_STDC +AC_CHECK_HEADERS(fcntl.h unistd.h byteswap.h inttypes.h sys/io.h) +dnl NOT zlib.h! Or else --with[out]-zlib gets overrriden in config.h. + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_UID_T +AC_C_INLINE +AC_TYPE_PID_T +AC_TYPE_SIZE_T + +dnl Checks for library functions. +AC_FUNC_MEMCMP +AC_FUNC_STRFTIME +AC_FUNC_VPRINTF +AC_CHECK_FUNCS(realpath) + +AC_PROG_RANLIB +AC_PROG_INSTALL + +AC_OUTPUT(Makefile libdiscmage/Makefile) + +echo +echo NOTE: On non-Linux systems you might need to use gmake instead of make diff --git a/ucon64/2.0/src/console/boardnames b/ucon64/2.0/src/console/boardnames new file mode 100644 index 0000000..e19805b --- /dev/null +++ b/ucon64/2.0/src/console/boardnames @@ -0,0 +1,65 @@ +351258: UNROM +351298: UNROM +351908 +352026: TLROM (w/ LS32 for VROM enable control) +51555: Acclaim, MMC3B mapper, PRG ROM, CHR ROM +53361 +54425 +55741 +56504 +AMROM: LS161, VRAM, PRG-ROM +ANROM: LS161+LS02 mapper, PRG-ROM, CHR-RAM +AOROM: LS161 mapper, PRG-ROM, CHR-ROM +BNROM: LS161, VRAM, PRG-ROM (Different LS161 bits? Only used on Deadly Towers) +CNROM: LS161 mapper, PRG-ROM, CHR-ROM?/CHR-RAM +COB: "Glop Top" style board +CPROM: LS04, LS08, LS161, 32K ROM, 16K VRAM (bankswitched, Videomation only) +DEIROM +DEROM +DRROM: MMC3, 4K of nametable RAM (for 4-screen), PRG-ROM, CHR-ROM (only in Gauntlet) +EKROM +ELROM: MMC5, PRG-ROM, CHR-ROM +ETROM: MMC5, PRG-ROM, CHR-ROM, 2x 8k optionnal RAM (battery) +EWROM: MMC5, PRG-ROM, CHR-ROM, 32k optionnal RAM (battery) +GNROM: LS161 mapper, PRG ROM, CHR ROM +HKROM: MMC6B, PRG-ROM, CHR-ROM, Battery +MHROM: LS161 mapper, black blob chips. Mario Bros / Duck Hunt multi +NES-B4: Same as TLROM +NES-BTR: Sunsoft FME7 mapper, PRG ROM, CHR ROM, 8k optionnal RAM +NES-QJ: +NES-RROM: Same as NROM (Only used in Clu Clu land) +NROM: No mapper, PRG-ROM, CHR-ROM +PNROM: MMC2, PRG-ROM, CHR-ROM +SAROM: MMC1B, PRG ROM, CHR ROM, optional 8k of RAM (battery) +SBROM: MMC1A, PRG ROM, CHR ROM (only 32K of CHR ROM) +SCEOROM +SC1ROM: MMC1B, PRG ROM, CHR ROM +SCROM: LS161, LS02, VRAM, PRG-ROM (Similar to UNROM) +SEROM: MMC1B, PRG ROM, CHR ROM +SFROM +SGROM: MMC1B, PRG ROM, 8k CHR RAM +SHROM +SJROM +SKROM: MMC1B, PRG ROM, CHR ROM, 8k optional RAM (battery) +SL1ROM: MMC3, PRG ROM, CHR ROM, LS32 (for 128K 28 pin CHR ROMs) +SL2ROM +SL3ROM +SLROM: MMC1A, PRG ROM, CHR ROM +SLRROM +SN1-ROM AW (Overlord only) +SNROM: MMC1A, PRG ROM, CHR ROM/RAM ?, 8k optional RAM (battery) +SOROM: MMC1B2, PRG ROM, VRAM, 16K of WRAM (Battery) Only 8K battery-backed +SVROM: MMC1B2, PRG ROM, VRAM, WRAM (Battery) +SUROM: MMC1B2, PRG ROM, CHR RAM/(ROM?), 8k battery-backed RAM (DW4???) +TEROM: MMC3A, PRG ROM, CHR ROM, (32k ROMs) +TFROM: MMC3B, PRG ROM, CHR ROM (64K of CHR only) +TGROM: MMC3C, PRG ROM, VRAM (512K of PRG) +TKROM: MMC3A, PRG ROM, CHR ROM, 8k optional RAM (battery) +TL1ROM: Same as TLROM +TLROM: MMC3B, PRG ROM, CHR ROM +TLSROM: Same as TLROM +TQROM: MMC3B+74HC32, PRG ROM, CHR ROM + 8k of CHR-RAM +TSROM: MMC3A, PRG ROM, CHR ROM, 8k optionnal RAM +TVROM: MMC3B, PRG ROM, CHR ROM, 4K of Nametable RAM (4-screen) +UNROM: 74LS32+74LS161 mapper, 128k PRG, 8k CHR-RAM +UOROM diff --git a/ucon64/2.0/src/console/boardtable.txt b/ucon64/2.0/src/console/boardtable.txt new file mode 100644 index 0000000..4c15147 --- /dev/null +++ b/ucon64/2.0/src/console/boardtable.txt @@ -0,0 +1,782 @@ +Name |Maker |RR|Board +--------------------------------------+--------------------+--+---------------- +10 Yard Fight |Nintendo |E |NROM +110 in 1 (Canada?) |Supervision | | +1942 |Capcom |C+|NROM +1943 |Capcom |C+|UNROM +6 in 1 |Caltron |A | +720 |Mindscape |C |SGROM +8 Eyes |Taxan |C-|TLROM +A Boy and His Blob |Absolute |C |SLROM +Abadox |Milton Bradley |C |SLROM +Action 52 |Active Enterprises |A | +Addams Family |Ocean |B |SLROM +Adv of Bayou Billy |Konami |C-|SLROM +Adv of Dino Riki |Hudson |C |CNROM +Adv of Lolo |HAL |C+|SEROM +Adv of Lolo 2 |HAL |B-|TEROM +Adv of Lolo 3 |HAL |B+|SLROM +Adv of Tom Saywer |Seta |B |SLROM +Adventure Island |Hudson |D |CNROM +Adventure Island 2 |Hudson |B-|TLROM +Adventure Island 3 |Hudson |B |TLROM +After Burner |Tengen |C+| +Air Fortress |HAL |C |SJROM +Airwolf |Acclaim |C |SHROM +Al Unser Racing |Data East |C |SKROM +Aladdin Deck Enhancer |Camerica |A+| +Alfred Chicken |Mindscape |B+|UNROM +Alien 3 |LJN |B+|55741 +Alien Syndrome |Tengen |C+| +All Pro Basketball |Vic Tokai |C |SLROM +Alpha Mission |SNK |C |CNROM +Amagon |American Sammy |C |UNROM +American Gladiators |Gametek |B-|SLROM +Anticipation |Nintendo |C |SEROM +Arch Rivals |Acclaim |C |AMROM +Archon |Activision |B |UNROM +Arkanoid |Taito |B+|CNROM +Arkanoid Controller |Taito |A | +Arkistas Ring |American Sammy |B |CNROM +Astyanax |Jaleco |C |TLROM +Athena |SNK |C |UNROM +Athletic World |Bandai |B-|CNROM +Attack/Killer Tomato |T*HQ |B-|SLROM +Baby Boomer |Color Dreams |B-| +Back to the Future |LJN |D |CNROM +Back to the Future 2 & 3 |LJN |C |SLROM +Bad Dudes |Data East |C |TLROM +Bad News Baseball |Tecmo |B |SLROM +Bad Street Brawler |Mattel |C |SGROM +Balloon Fight |Nintendo |C |NROM +Bandit Kings/China |Koei |B-|ETROM +Barbie |Hi Tech |B |SLROM +Bard's Tale |FCI |B-|SNROM +Base Wars |Ultra |C |TKROM +Baseball |Nintendo |E |NROM +Baseball Simulator |Culture Brain |C+|SKROM +Baseball Stars |SNK |C+|SKROM +Baseball Stars 2 |Romstar |B+|TKROM +Bases Loaded |Jaleco |E |SFROM +Bases Loaded 2 |Jaleco |D |SL3ROM +Bases Loaded 3 |Jaleco |C |TLROM +Bases Loaded 4 |Jaleco |C+|TLROM +Batman |Sunsoft |D |NES_B4 +Batman Return/Joker |Sunsoft |B-|NES_BTR +Batman Returns |Konami |B+|TLROM +Battle Chess |Data East |B+|SKROM +Battle of Olympus |Broderbund |C |SGROM +Battle Tank |Absolute |B |CNROM +Battle Toads |Tradewest |D |AOROM +Battle Toads & Double Dragon |Tradewest |B+|AOROM +Battleship |Mindscape |B |CNROM +Bee 52 |Camerica |B+| +Beetlejuice |LJN |C+|AMROM +Best of the Best |Electro Brain |B+|UOROM +Bible Adventures |Wisdom Tree |B+| +Bible Adventures (blue label) |Wisdom Tree |B+| +Bible Buffet |Wisdom Tree |B+| +Big Bird Hide & Speak |Hi Tech |B |SLROM +Big Foot |Acclaim |B-|SLROM +Big Nose Freaks Out |Camerica |A | +Big Nose Freaks Out (Aladdin Cart) |Camerica |A+| +Big Nose the Caveman |Camerica |B | +Bill & Ted Adventure |LJN |C+|SLROM +Bill Elliot NASCAR |Konami |C+|TSROM +Bionic Commando |Capcom |C |SGROM +Black Bass |Hot B |B |UNROM +Blackjack |American Video |A-| +Blades of Steel |Konami |D |UNROM +Blaster Master |Sunsoft |D |SL2ROM +Blue Marlin |Hot B |B+|TLROM +Blues Brothers |Titus |B |UNROM +Bo Jackson Baseball |Data East |C+|TSROM +Bomberman |Hudson |C |NROM +Bomberman 2 |Hudson |B |SNROM +Bonks Adventure |Hudson |B+|TLROM +Boulder Dash |JVC |B+|SEROM +Break Time |FCI |B+|SFROM +Breakthru |Data East |C |(SLROM) +Bubble Bath Babes |Panesian |A+| +Bubble Bobble |Taito |D |SFROM +Bubble Bobble 2 |Taito |B |TLROM +Bucky O'Hare |Konami |B |TLROM +Bugs Bunny Birthday Blowout |Kemco |B |TSROM +Bugs Bunny Castle |Kemco |B+|SBROM +Bump & Jump |Vic Tokai |B-|CNROM +Burai Fighter |Taxan |B-|TEROM +Burger Time |Data East |B-|NROM +Cabal |Milton Bradley |C |AMROM +Caesars Palace |Virgin |B |UNROM +California Games |Milton Bradley |C |UNROM +Captain America |Data East |B |TLROM +Captain Comic |Color Dreams |B | +Captain Planet |Mindscape |B |TLROM +Captain Skyhawk |Milton Bradley |C |AMROM +Casino Kid |Sofel |C+|UNROM +Casino Kid 2 |Sofel |B+|UNROM +Castelian |Triffix |B |UNROM +Castle of Deceit |Bunch Games |B | +Castle of Dragon |Seta |B |UNROM +Castlequest |Nexoft |B |CNROM +Castlevania |Konami |E |UNROM +Castlevania 2 |Konami |D |SLROM +Castlevania 3 |Konami |C-|ELROM +Caveman Games |Data East |B-|SLROM +Challenge of the Dragon |Color Dreams |B | +Championship Bowling |Romstar |B |CNROM +Championship Pool |Mindscape |B+|UNROM +Cheetahman II |Active Enterprises |A | +Chessmaster |Hi Tech |B |SJROM +Chiller |American Game Carts Inc|B+| +Chubby Cherub |Bandai |B |NROM +Circus Caper |Toho |B |SLROM +City Connection |Jaleco |B |CNROM +Clash at Demonhead |Vic Tokai |B-|SLROM +Classic Concentration |Gametek |B-|UNROM +Cliffhanger |Imagesoft |B |TLROM +Clu Clu Land |Nintendo |B-|NES_RROM +Cobra Command |Data East |C |SLROM +Cobra Triangle |Nintendo |B-|ANROM +Codename Viper |Capcom |C |TLROM +Color A Dinosaur |Virgin |A-|UNROM +Commando |Capcom |C |UNROM +Conan |Mindscape |B |UNROM +Conflict |Vic Tokai |C+|SKROM +Conquest Crystal Palace |Asmik |C+|TLROM +Contra |Konami |E |UNROM +Contra Force |Konami |B |TLROM +Cool World |Ocean |B |SLROM +Cowboy Kid |Romstar |A-|TLROM +Crash & the Boys Street Challange |American Technos |B |TLROM +Crash Dummies |LJN |B |55741 +Crystal Mines |Color Dreams |B+| +Crystalis |SNK |C |TKROM +Cyberball |Jaleco |B |TLROM +Cybernoid |Acclaim |C |CNROM +Dance Aerobics |Nintendo |C+|SBROM +Darkman |Ocean |B |SLROM +Darkwing Duck |Capcom |B+|SLROM +Dash Galaxy |Data East |C |CNROM +Day Dreamin' Davey |HAL |B+|SLROM +Days of Thunder |Mindscape |C+|TLROM +Deadly Towers |Broderbund |C+|BNROM +Death Race |American Game Carts Inc|B+| +Deathbots |American Video |B+| +Defender 2 |HAL |B |NROM +Defender of the Crown |Ultra |C+|SGROM +Defenders of Dynacron City |JVC |B |TLROM +Deja Vu |Kemco |C+|TKROM +Demon Sword |Taito |C+|SL1ROM +Desert Commander |Kemco |C-|SKROM +Destination Earthstar |Acclaim |C |CNROM +Destiny/Emporer |Capcom |B-|SNROM +Dick Tracy |Bandai |C+|UNROM +Die Hard |Activision |B+|SLROM +Dig Dug 2 |Bandai |B |NROM +Digger |Milton Bradley |B+|AMROM +Dirty Harry |Mindscape |C+|TLROM +Disney Adventure |Capcom |B |SLROM +Dizzy the Adventurer (Aladdin Cart) |Camerica |A+| +Donkey Kong |Nintendo |B-|NROM +Donkey Kong 3 |Nintendo |B-|NROM +Donkey Kong Classics |Nintendo |B-|CNROM +Donkey Kong Jr |Nintendo |B-|NROM +Donkey Kong Jr Math |Nintendo |A-|NROM +Double Dare |Gametek |B |AOROM +Double Dragon |Tradewest |E |SLROM +Double Dragon 2 |Acclaim |D |TL1ROM +Double Dragon 3 |Acclaim |C+|TLROM +Double Dribble |Konami |D |UNROM +Double Strike |American Video |B+| +Dr. Chaos |FCI |C |UNROM +Dr. Jeckyl/Mr. Hyde |Bandai |C+|SFROM (SFDOROM) +Dr. Mario |Nintendo |D |SEROM +Dracula |Imagesoft |B |TSROM +Dragon Fighter |Sofel |B |SLROM +Dragon Power |Bandai |C |GNROM +Dragon Spirit |Bandai |B |TLROM +Dragon Strike |FCI |C |TLROM +Dragon Warrior |Nintendo |E |SAROM +Dragon Warrior 2 |Enix |C+|SNROM +Dragon Warrior 3 |Enix |B+|SVROM +Dragon Warrior 4 |Enix |B |SVROM +Dragon's Lair |Imagesoft |B |UNROM +Duck Hunt |Nintendo |F |NROM +Duck Tales |Capcom |C+|UNROM +Duck Tales 2 |Capcom |B+|UNROM +Dudes with Attitude |American Video |B+| +Dungeon Magic |Taito |C |SKROM +Dusty Diamond All Star Softball |Broderbund |B-|SLROM +Dyno Warz |Bandai |C+|SLROM +Elevator Action |Taito |B-|NROM +Eliminator Boat Duel |Electro Brain |C+|SLROM +Empire Strikes Back |JVC |B |TLROM +Excitebike |Nintendo |E |NROM +Exodus |Wisdom Tree |B+| +F 117 Stealth |Microprose |B+|TLROM +F 15 City War |American Video |B | +F 15 Strike Eagle |Microprose |B+|TLROM +F1 Built to Win |Seta |B+|SKROM +Family Feud |Gametek |B+|SHROM +Fantastic Adv Dizzy (Aladdin Cart) |Camerica |A+| +Fantastic Adventures of Dizzy |Camerica |B-| +Fantasy Zone |Tengen |C | +Faria |Nexoft |B+|SKROM +Fast Break |Tradewest |C |SCROM +Faxanadu |Nintendo |C-|SGROM +Felix the Cat |Hudson |B+|TSROM +Ferrari Grand Prix |Acclaim |B |SLROM +Fester's Quest |Sunsoft |C |SLROM +Fighting Golf |SNK |C |SLROM +Final Fantasy |Nintendo |C+|SNROM +Fire & Ice |Tecmo |A-|TLROM +Fire Hawk |Camerica |B | +Firehouse Rescue |Gametek |B |CNROM +Fist of the North Star |Taxan |C+|UNROM +Flight of the Intruder |Mindscape |B |UNROM +Flintstones |Taito |C+|TLROM +Flintstones 2 |Taito |A+| +Flying Dragon |Culture Brain |C |UNROM +Flying Warriors |Culture Brain |C |SLROM +Frankenstein |Bandai |B+|SLRROM +Freedom Force |Sunsoft |D |SLROM +Friday The 13th |LJN |C-|CNROM +Fun House |Hi Tech |B+|UNROM +G I Joe |Taxan |B+|TLROM +G I Joe Atlantis Factor |Capcom |C+|TLROM +Galactic Crusader |Bunch Games |B | +Galaga |Bandai |B |NROM +Galaxy 5000 |Activision |A-|TLROM +Game Action Replay |STD |A-| +Game Genie |Galoob |D | +Gargoyle's Quest 2 |Capcom |B+|TLROM +Gauntlet (licensed) |Tengen |C |DRROM +Gauntlet (unlicensed) |Tengen |C | +Gauntlet 2 |Mindscape |C |TSROM +Gemfire |Koei |B-|EKROM +Genghis Kahn |Koei |C+|SOROM +George Forman |Acclaim |B |55741 +Ghost & Goblins |Capcom |C |UNROM +Ghost Lion |Kemco |B |SKROM +Ghostbusters |Activision |B-|CNROM +Ghostbusters 2 |Activision |B |SLROM +Ghoul School |Electro Brain |B-|SLROM +Gilligans Island |Bandai |B+|UNROM +Goal |Jaleco |D |SL3ROM +Goal 2 |Jaleco |B-|TLSROM +Godzilla |Toho |C+|SLROM +Godzilla 2 |Toho |B |SLROM +Gold Metal Challenge |Capcom |B+|TKROM +Golf |Nintendo |E |NROM +Golf Grand Slam |Atlus |B+|SLROM +Golf Power |Virgin |B+|SNROM +Golgo 13 Top Secret Episode |Vic Tokai |D |SLROM +Goonies 2 |Konami |C+|351258 +Gotcha |LJN |C |CNROM +Gradius |Konami |C-|CNROM +Great Waldo Search |T*HQ |B-|SLROM +Gremlins 2 |Sunsoft |B-|TLROM +Guardian Legend |Broderbund |C+|UNROM +Guerrilla War |SNK |C |SLROM +Gum Shoe |Nintendo |C+|GNROM +Gun Nac |Ascii |B |TLROM +Gunsmoke |Capcom |B-|UNROM +Gyromite |Nintendo |E |NROM +Gyruss |Ultra |C+|CNROM +Harlem Globetrotters |Gametek |B |SLROM +Hatris |Bullet Proof |A |SNROM +Heavy Barrel |Data East |C |TLROM +Heavy Shreddin' |Parker Brothers |B-|SLROM +Heroes of the Lance |FCI |C |SKROM +High Speed |Tradewest |B-|TQROM +Hillsfar |FCI |B |SNROM +Hogan's Alley |Nintendo |D |NROM +Hollywood Squares |Gametek |B+|UNROM +Home Alone |T*HQ |B-|TSROM +Home Alone 2 |T*HQ |B-|TLROM +Hook |Imagesoft |B |SLROM +Hoops |Jaleco |D |SLROM +Hot Slots |Panesian |A+| +Hudson Hawk |Imagesoft |B |SLROM +Hunt for Red October |Hi Tech |C |TLROM +Hydlide |FCI |D |NROM +I Can Remember |Gametek |B |CNROM +Ice Climber |Nintendo |C+|NROM +Ice Hockey |Nintendo |D |NROM +Ikari Warriors |SNK |C-|UNROM +Ikari Warriors 2 |SNK |C-|SGROM +Ikari Warriors 3 |SNK |B-|SLROM +Image Fight |Irem |B |TSROM +Immortal |Electronic Arts |B |TLROM +Impossible Mission 2 |American Video |A-| +Impossible Mission 2 |SEI |B+| +Indiana Jones Last Crusade |UBI Soft |B+|UNROM +Indiana Jones Last Crusade |Taito |B+|SGROM +Indiana Jones Temple of Doom |Tengen |B-| +Indiana Jones Temple of Doom |Mindscape |C |TFROM +Indy Heat |Tradewest |B |AMROM +Infiltrator |Mindscape |B |TLROM +Iron Tank |SNK |C |SLROM +Isolated Warrior |NTVIC |B |TLROM +Jack Nicklaus Golf |Konami |B-|351258 +Jackal |Konami |C |UNROM +Jackie Chan Kung Fu |Hudson |B |TLROM +James Bond Jr |T*HQ |B-|TLROM +Jaws |LJN |D |CNROM +Jeopardy |Gametek |C |AOROM +Jeopardy 25th Anniversary |Gametek |C+|ANROM +Jeopardy Jr |Gametek |C |ANROM +Jeopardy Super |Gametek |B |SLROM +Jetsons |Taito |B+|TLROM +Jimmy Connors Tennis |UBI Soft |B+|UNROM +Joe & Mac |Data East |B+|TLROM +Jordan vs. Bird |Milton Bradley |C |UNROM +Joshua |Wisdom Tree |B+| +Journey to Silius |Sunsoft |C+|SLROM +Joust |HAL |B |CNROM +Jungle Book |Virgin |A-|TLROM +Jurassic Park |Ocean |B |TSROM +Karate Champ |Data East |C |CNROM +Karate Kid |LJN |C |CNROM +Karnov |Data East |C |DEIROM +Kick Master |Taito |C+|TLROM +Kickle Cubicle |Irem |B+|TLROM +Kid Icarus |Nintendo |C-|SNROM +Kid Klown |Kemco |B |TSROM +Kid Kool |Vic Tokai |C+|UNROM +Kid Niki |Data East |C |SGROM +King Neptune's Adventure |Color Dreams |B+| +King of Kings |Wisdom Tree |B+| +King of Kings (cartoon mule) |Wisdom Tree |B+| +King of the Ring |Acclaim |B+|55741 +Kings Knight |Square |C+|CNROM +Kings of the Beach |Ultra |C |CNROM +Kings Quest 5 |Konami |B+|TSROM +Kirbys Adventure |Nintendo |B-|TKROM +Kiwi Kraze |Taito |B+|TLROM +Klash Ball |Sofel |B-|UNROM +Klax |Tengen |B+| +Knight Rider |Acclaim |B-|SCIROM +Krazy Kreatures |American Video |B+| +Krion Conquest |Vic Tokai |B |TLROM +Krusty's Fun House |Acclaim |C+|TLROM +Kung Fu |Nintendo |E |NROM +Kung Fu Heroes |Culture Brain |C |CNROM +L'Empereur |Koei |B |ETROM +Laser Invasion |Konami |B-|ELROM +Last Action Hero |Imagesoft |B+|TLROM +Last Ninja |Jaleco |B-|TLROM +Last Starfighter |Mindscape |C+|CNROM +Legacy/Wizard |Broderbund |C |TFROM +Legend of Kage |Taito |C-|CNROM +Legendary Wings |Capcom |B |UNROM +Legends/Diamond |Bandai |B+|TLROM +Lemmings |Sunsoft |B-|SLROM +Lethal Weapon |Ocean |B |SLROM +Life Force |Konami |C |351258 +Linus Spacehead |Camerica |B | +Linus Spacehead (Aladdin Cart) |Camerica |A+| +Little League Baseball |SNK |B-|SLROM +Little Mermaid |Capcom |B |UNROM +Little Nemo |Capcom |B+|TLROM +Little Ninja Brothers |Culture Brain |C |TLROM +Little Sampson |Taito |B |TLROM +Lode Runner |Broderbund |B |NROM +Lone Ranger |Konami |B-|TLROM +Loopz |Mindscape |B+|UNROM +Low G Man |Taxan |C |TLROM +Lunar Pool |FCI |C+|NROM +M C Kids |Virgin |C+|TSROM +M.U.L.E. |Mindscape |C+|SNROM +Mach Rider |Nintendo |C-|NROM +Mad Max |Mindscape |C+|TLROM +Mafat Conspiracy |Vic Tokai |C |TLROM +Magic Darts |Romstar |B |SLRROM +Magic of Scheherazade |Culture Brain |C-|SLROM +Magician |Taxan |A-|TKROM +Magmax |FCI |C-|NROM +Major League Baseball |LJN |E |CNROM +Maniac Mansion |Jaleco |C+|SNROM +Mappyland |Taxan |C+|TFROM +Marble Madness |Milton Bradley |C+|ANROM +Mario Brothers |Nintendo |B-|NROM +Mario Is Missing |Mindscape |B |TLROM +Mario Time Machine |Mindscape |A-|TLROM +Marvel's X-Men |LJN |B-|UNROM +Master Chu & the Drunkard Hu |Color Dreams |B | +Maxi 15 |American Video |A | +Mechanized Attack |SNK |C |SCROM +Mega Man |Capcom |B-|UNROM +Mega Man 2 |Capcom |C |SGROM +Mega Man 3 |Capcom |C |TLROM +Mega Man 4 |Capcom |B-|TGROM +Mega Man 5 |Capcom |B |TLROM +Mega Man 6 |Nintendo |B+|TGROM +Menace Beach |Color Dreams |A-| +Mendel Palace |Hudson |B |TLROM +Mermaids of Atlantis |American Video |B+| +Metal Fighter |Color Dreams |B | +Metal Gear |Ultra |D |UNROM +Metal Mech |Jaleco |B-|SLROM +Metal Storm |Irem |B+|TLROM +Metroid |Nintendo |D |SNROM +Michael Andretti World GP |American Sammy |B-|TLROM +Mickey Mousecapade |Capcom |C+|CNROM +Mickey Numbers |Hi Tech |B+|TLROM +Mickey's Safari in Letterland |Hi Tech |B+|55741 +Micro Machines |Camerica |A-| +Micro Machines (Aladdin Cart) |Camerica |A+| +Mig 29 |Camerica |C+| +Might & Magic |American Sammy |B+|TKROM +Mighty Bombjack |Tecmo |B |CNROM +Mighty Final Fight |Capcom |B+|TLROM +Mike Tyson's Punch Out |Nintendo |D |PNROM +Millipede |HAL |B |NROM +Milon's Secret Castle |Hudson |C |CNROM +Miracle Piano |Mindscape |A-|SJROM +Mission Cobra |Bunch Games |A-| +Mission Impossible |Ultra |C+|352026 +Monopoly |Parker Brothers |B-|SLROM +Monster in my Pocket |Konami |B+|TLROM +Monster Party |Bandai |B |SLROM +Monster Truck Rally |INTV |B |CNROM +Moon Ranger |Bunch Games |C | +Motor City Patrol |Matchbox |C+|SLROM +Ms. Pacman |Namco |A |NROM +Ms. Pacman |Tengen |B+| +Muppet Adventure |Hi Tech |B |SGROM +Muscle |Bandai |B-|NROM +Mutant Virus |American Software |B+|SLROM +Mystery Quest |Taxan |C+|CNROM +NARC |Acclaim |C |AMROM +NES Open Golf |Nintendo |B-|SNROM +NFL Football |LJN |C |UNROM +Nigel Mansell |Gametek |B+|SLROM +Nightmare on Elm Street |LJN |B |AMROM +Nightshade |Ultra |C+|TLROM +Ninja Crusaders |American Sammy |B |TGROM +Ninja Gaiden |Tecmo |E |SLROM +Ninja Gaiden 2 |Tecmo |C-|TLROM +Ninja Gaiden 3 |Tecmo |C+|TLROM +Ninja Kid |Bandai |C+|CNROM +Nombunagas Ambition |Koei |C |SOROM +Nombunagas Ambition 2 |Koei |B |ETROM +North & South |Kemco |B+|TSROM +Operation Secret Storm |Color Dreams |A | +Operation Wolf |Taito |D |SLROM +ORB-3D |Hi Tech |C |SCROM +Othello |Acclaim |C |NROM +Overlord |Virgin |B-|SN1 +P'radikus Conflict |Color Dreams |B | +Pac Man |Namco |A |56504 +Pac Man (licensed) |Tengen |B-|NROM +Pac Man (unlicensed) |Tengen |B-| +Pac Mania |Tengen |A | +Palamedes |Hot B |B+|SEROM +Panic Resturant |Taito |B |TLROM +Paperboy |Mindscape |C |CNROM +Paperboy 2 |Mindscape |B |UOROM +Parodius (England) |Palcom | | +Pebble Beach Golf |Bandai |C+|CNROM +Peek A Boo Poker |Panesian |A+| +Perfect Fit |Gametek |B |CNROM +Pesterminator |Color Dreams |B | +Peter Pan & the Pirates |T*HQ |B-|SFROM +Phantom Fighter |FCI |C+|SGROM +Pictionary |LJN |B-|SLROM +Pinball |Nintendo |D |NROM +Pinball Quest |Jaleco |B |SLROM +Pinbot |Nintendo |B |TQROM +Pipe Dream |Bullet Proof |B |CNROM +Pirates |Ultra |B |SKROM +Platoon |Sunsoft |D |SLROM +Play Action Football |Nintendo |D |TLSROM +Pool of Radiance |FCI |B+|TKROM +Popeye |Nintendo |B-|NROM +POW |SNK |C |SLROM +Power Blade |Taito |B-|TLROM +Power Blade 2 |Taito |B-|TLROM +Power Punch 2 |American Softworks |B |TLROM +Predator |Activision |C+|SLROM +Prince of Persia |Virgin |C+|UNROM +Princess Tomato |Hudson |B+|SGROM +Pro Am |Nintendo |D |SEROM +Pro Am 2 |Tradewest |B |AOROM +Pro Sport Hockey |Jaleco |B+|TLSROM +Pro Wrestling |Nintendo |D |UNROM +Puggsly's Scavenger Hunt |Ocean |B |SLROM +Punch Out |Nintendo |C+|PNROM +Punisher |LJN |B |TLROM +Puss N Boots |Electro Brain |B-|UNROM +Puzzle |American Video |A-| +Puzznic |Taito |B+|CNROM +Pyramid |American Video |A-| +Q*Bert |Ultra |B-|CNROM +Qix |Taito |A |SNROM +Quantum Fighter |HAL |B |TLROM +Quarterback |Tradewest |D |CNROM +Quattro Adventure |Camerica |B+| +Quattro Adventure (Aladdin Cart) |Camerica |A+| +Quattro Arcade |Camerica |A-| +Quattro Sports |Camerica |B | +Quattro Sports (Aladdin Cart) |Camerica |A+| +Race America |Absolute |B+|SLROM +Racket Attack |Jaleco |C-|SLROM +Rad Gravity |Activision |B-|SLROM +Rad Racer |Nintendo |D |SGROM +Rad Racer 2 |Square |B-|TVROM +Rad Racket |American Video |A-| +Raid 2020 |Color Dreams |B | +Raid on Bungling Bay |Broderbund |B |NROM +Rainbow Island |Taito |B+|UNROM +Rally Bike |Romstar |B |UNROM +Rambo |Acclaim |E |UNROM +Rampage |Data East |C+|TFROM +Rampart |Jaleco |B |TLROM +RBI Baseball (licensed) |Tengen |C+|DEROM +RBI Baseball (unlicensed) |Tengen |C | +RBI Baseball 2 |Tengen |C+| +RBI Baseball 3 |Tengen |B-| +Remote Control |Hi Tech |C |SLROM +Ren + Stimpy Buckaroos |T*HQ |B-|TLROM +Renegade |Taito |C-|UNROM +Rescue |Kemco |C-|SLROM +Rescue Rangers |Capcom |C+|SLROM +Rescue Rangers 2 |Capcom |B+|SLROM +Ring King |Data East |C |DEROM +River City Ransom |American Technos |C |TLROM +Road Blasters |Mindscape |C+|SLROM +Road Runner |Tengen |B+| +Robin Hood |Virgin |C+|SGROM +Robo Cop |Data East |C |TL1ROM +Robo Cop 2 |Data East |B-|SLROM +Robo Cop 3 |Ocean |B |SLROM +Robo Demons |Color Dreams |B | +Robo Warrior |Jaleco |B |UNROM +Rock N Ball |NTVIC |B |TFROM +Rocket Ranger |Kemco |B-|SGROM +Rocketeer |Bandai |B-|SGROM +Rockin Kats |Atlus |B+|TLROM +Rocky & Bullwinkle |T*HQ |B-|TLROM +Roger Clemens |LJN |C |53361 +Roller Games |Ultra |B-|TLROM +Rollerball |HAL |B |SFROM +Rollerblade Racer |Hi Tech |B |53361 +Rolling Thunder |Tengen |C+| +Romance/3 Kingdoms |Koei |C |SOROM +Romance/3 Kingdoms 2 |Koei |B |EWROM +Roundball |Mindscape |B-|TSROM +Rush N Attack |Konami |D |UNROM +Rygar |Tecmo |C |UNROM +SCAT |Natsume |B |SLROM +Secret Scout |Color Dreams |A | +Section Z |Capcom |C |UNROM +Seicross |FCI |D |NROM +Sesame Street 1-2-3 |Hi Tech |C+|SEROM (SCROROM) +Sesame Street 123/ABC |Hi Tech |B |SLROM +Sesame Street A-B-C |Hi Tech |C+|SEROM +Sesame Street Countdown |Hi Tech |A-|SLROM +Shadow of the Ninja |Natsume |B |TLROM +Shadowgate |Kemco |C+|TKROM +Shatterhand |Jaleco |B-|TLROM +Shingen the Ruler |Hot B |C |SNROM +Shinobi |Tengen |C+| +Shockwave |American Game Carts Inc|B | +Shooting Range |Bandai |B |CNROM +Short Order/Eggsplode |Nintendo |B+|SBROM +Side Pocket |Data East |B |UNROM +Silent Assault |Color Dreams |B | +Silent Service |Ultra |C-|351258 +Silk Worm |American Sammy |C |SLROM +Silver Surfer |Arcadia |B |TSROM +Simpsons Bart Meets Radioactive Man |Acclaim |B+|55741 +Simpsons Bart Vs Space Mutants |Acclaim |D |SLROM +Simpsons Bart Vs World |Acclaim |B-|53361 +Skate or Die |Ultra |D |351258 +Skate or Die 2 |Electronic Arts |B |SLROM +Ski or Die |Ultra |B-|351908 +Skull & Crossbones |Tengen |B | +Sky Kid |Sunsoft |B-|SCEOROM +Sky Shark |Taito |D |SL1ROM +Slalom |Nintendo |B-|NROM +Smash TV |Acclaim |C+|51555 +Snake Rattle & Roll |Nintendo |C+|SEROM +Snakes Revenge |Ultra |C |SLROM +Snoopy Silly Sports |Kemco |B+|SLROM +Snow Brothers |Capcom |B |SLROM +Soccer |Nintendo |D |NROM +Solar Jetman |Tradewest |C |AOROM +Solitaire |American Video |A-| +Soloman's Key |Tecmo |C+|CNROM +Solstice |Imagesoft |C |ANROM +Space Shuttle |Absolute |B+|SGROM +Spelunker |Broderbund |C+|NROM +Spiderman |LJN |B-|53361 +Spiritual Warfare |Wisdom Tree |B+| +Spot |Arcadia |B-|SNROM +Spy Hunter |Sunsoft |C |CNROM +Spy vs. Spy |Kemco |B-|NROM +Sqoon |Irem |B+|NROM +Stack Up |Nintendo |A-|HVC +Stadium Events |Bandai |B | +Stanley |Electro Brain |B |TLROM +Star Force |Tecmo |C+|CNROM +Star Soldier |Taxan |C-|CNROM +Star Trek 25th Anniversary |Ultra |B |TLROM +Star Trek: The Next Generation |Absolute |A-|UNROM +Star Tropics |Nintendo |C-|HKROM +Star Voyager |Acclaim |C+|CNROM +Star Wars |JVC |B |TSROM +Starship Hector |Hudson |B-|UNROM +Stealth ATF |Activision |C+|SLROM +Stinger |Konami |B-|UNROM +Street Cop |Bandai |A-|SLROM +Street Fighter 2010 |Capcom |B |TLROM +Strider |Capcom |C |SGROM +Stunt Kids |Camerica |A-| +Sunday Funday |Wisdom Tree |B+| +Super C |Konami |C |352026 +Super Cars |Electro Brain |B+|UNROM +Super Dodge Ball |Imagesoft |C |SLROM +Super Glove Ball |Mattel |C |UNROM +Super Mario Brothers |Nintendo |F |NROM +Super Mario Brothers 2 |Nintendo |D |TSROM +Super Mario Brothers 3 |Nintendo |D |TSROM +Super Mario/Duck Hunt |Nintendo |F |MH +Super Mario/Duck Hunt/Track Meet |Nintendo |C |COB +Super Off Road |Tradewest |C-|AMROM +Super Pitfall |Activision |C+|UNROM +Super Spike V'Ball |Nintendo |C-|TLROM +Super Spike/World Cup |Nintendo |B-|COB +Super Sprint |Tengen |C |COB +Super Spy Hunter |Sunsoft |B+|TLROM +Super Team Games |Nintendo |C-|CNROM +Superman |Kemco |B+|SLROM +Swamp Thing |T*HQ |C+|SLROM +Sword Master |Activision |A-|TLROM +Swords & Serpents |Acclaim |B+|UNROM +T&C Surf Design |LJN |E |CNROM +Taboo |Tradewest |C+|SEROM +Tag Team Wrestling |Data East |C |NROM +Taggin Dragon |Bunch Games |B | +Talespin |Capcom |B |SLROM +Target Renegade |Taito |C+|SLROM +Tecmo Baseball |Tecmo |C |SGROM +Tecmo Basketball |Tecmo |C+|TKROM +Tecmo Bowl |Tecmo |E |SLROM +Tecmo Cup Soccer |Tecmo |B+|SLROM +Tecmo Super Bowl |Tecmo |C-|TKROM +Tecmo Wrestling |Tecmo |C |SLROM +Teenage Turtles |Ultra |E |351908 +Teenage Turtles 2 |Ultra |D |TLROM +Teenage Turtles 3 |Konami |B-|TLROM +Teenage Turtles Tournament Fighters |Konami |B+|TLROM +Tennis |Nintendo |D |NROM +Terminator |Mindscape |B+|TLROM +Terminator 2 Judgement Day |LJN |B-|53361 +Terra Cresta |Vic Tokai |B+|UNROM +Tetris |Tengen |A-| +Tetris |Nintendo |D |SEROM +Tetris 2 |Nintendo |B-|TSROM +Three Stooges |Activision |C+|SLROM +Thrilla Safari |LJN |B |53361 +Thunder & Lightning |Romstar |B+|GNROM +Thunderbirds |Activision |C+|SLROM +Thundercade |American Sammy |C+|UNROM +Tiger Heli |Acclaim |C+|CNROM +Tiles of Fate |American Video |B+| +Time Lord |Milton Bradley |C |AMROM +Times of Lore |Toho |B-|UNROM +Tiny Toon |Konami |C+|TLROM +Tiny Toon Cartoon Workshop |Konami |B+|TSROM +Tiny Toons 2 |Konami |B+|TLROM +To The Earth |Nintendo |C+|TEROM +Toki |Taito |B+|TLROM +Tom & Jerry |Hi Tech |B+|TLROM +Tombs & Treasures |Infocom |A-|SGROM +Toobin |Tengen |B | +Top Gun |Konami |D |351298 +Top Gun 2 |Konami |D |352026 +Top Players Tennis |Asmik |C+|SLROM +Total Recall |Acclaim |D |UNROM +Totally Rad |Jaleco |B-|TLROM +Touchdown Fever |SNK |B+|SFROM +Toxic Crusader |Bandai |B |TLROM +Track & Field |Konami |D |CNROM +Track & Field 2 |Konami |D |SLROM +Treasure Master |American Softworks |A-|SLROM +Trick Shooting |Nintendo |C+|SCROM +Trog |Acclaim |B+|UNROM +Trojan |Capcom |C |UNROM +Trolls on Treasure Island |American Video |A-| +Twin Cobra |American Sammy |C+|TLROM +Twin Eagle |Romstar |C+|UNROM +Ultima/Exodus |FCI |C-|SNROM +Ultima/Quest Avatar |FCI |B |SNROM +Ultima/War Destiny |FCI |B+|SNROM +Ultimate Air Combat |Activision |B+|TLROM +Ultimate Basketball |American Sammy |C |TLROM +Ultimate League Soccer |American Video |A-| +Ultimate Stuntman |Camerica |C+| +Uncharted Waters |Koei |B-|ETROM +Uninvited |Kemco |B+|TKROM +Untouchables |Ocean |B-|SLROM +Urban Champion |Nintendo |D |NROM +Vegas Dream |HAL |B |SKROM +Venice Beach Volleyball |American Video |A-| +Vice Project Doom |American Sammy |C+|TLROM +Videomation |T*HQ |B+|CPROM +Vindicators |Tengen |C | +Volleyball |Nintendo |C |NROM +Wacky Races |Atlus |B+|TLROM +Wall Street Kid |Sofel |B |UNROM +Wally Bear and the No Gang |American Video |A-| +Wario Woods |Nintendo |B+|TKROM +Wayne Gretzky |T*HQ |C+|UNROM +Wayne's World |T*HQ |B |TLROM +Werewolf |Data East |C |TLROM +Wheel/Fortune |Gametek |C |AOROM +Wheel/Fortune/Family |Gametek |C |ANROM +Wheel/Fortune/Junior |Gametek |C |ANROM +Wheel/Fortune/Vanna |Gametek |B |AOROM +Where in Time is Carmen |Konami |B |TSROM +Where's Waldo |T*HQ |C+|TSROM +Who Framed Roger Rabbit |LJN |B-|ANROM +Whomp Em |Jaleco |B |TLROM +Widget |Atlus |B |TLROM +Wild Gunman |Nintendo |C |NROM +Willow |Capcom |B-|SLROM +Win Lose or Draw |Hi Tech |C |SGROM +Winter Games |Acclaim |B-|UNROM +Wizardry |Nexoft |C+|SKROM +Wizardry 2 Knight of Diamonds |Ascii |B+|TKROM +Wizards & Warriors |Acclaim |D |ANROM +Wizards & Warriors 2 - Ironsword |Acclaim |D |AOROM +Wizards & Warriors 3 |Acclaim |C-|54425 +Wolverine |LJN |B-|TLROM +World Champ |Romstar |B+|TLROM +World Championship Wrestling |FCI |D |TLROM +World Class Track Meet |Nintendo |D |CNROM +World Cup Soccer |Nintendo |C-|TLROM +World Games |Milton Bradley |B-|ANROM +World Runner 3D |Acclaim |C-|UNROM +Wrath of the Black Manta |Taito |C+|SLROM +Wrecking Crew |Nintendo |C+|NROM +Wrestlemania |Acclaim |D |ANROM +Wrestlemania Challenge |LJN |C-|UNROM +Wrestlemania Steel Cage |LJN |C+|53361 +Wurm |Asmik |B-|TLROM +Xenophobe |Sunsoft |B-|SFROM +Xevious |Bandai |C+|NROM +Xexyz |Hudson |C+|SLROM +Yo Noid |Capcom |C+|SLROM +Yoshi |Nintendo |C-|SFROM +Yoshi's Cookie |Nintendo |B-|TFROM +Young Indy |Jaleco |B+|TLROM +Zanac |FCI |C+|UNROM +Zelda |Nintendo |E |SNROM +Zelda 2 |Nintendo |D |SKROM +Zen |Konami |B |TLROM +Zoda's Revenge, Startropics 2 |Nintendo |B-|HKROM +Zombie Nation |Meldac |B |TLROM diff --git a/ucon64/2.0/src/console/console.h b/ucon64/2.0/src/console/console.h new file mode 100644 index 0000000..bb4c577 --- /dev/null +++ b/ucon64/2.0/src/console/console.h @@ -0,0 +1,38 @@ +/* +console.h - single header for all console functions + +Copyright (c) 2003 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef CONSOLE_H +#define CONSOLE_H +#include "dc.h" +#include "gb.h" +#include "gba.h" +#include "genesis.h" +#include "jaguar.h" +#include "lynx.h" +#include "n64.h" +#include "neogeo.h" +#include "nes.h" +#include "ngp.h" +#include "pce.h" +#include "psx.h" +#include "sms.h" +#include "snes.h" +#include "swan.h" +#endif // CONSOLE_H diff --git a/ucon64/2.0/src/console/dc.c b/ucon64/2.0/src/console/dc.c new file mode 100644 index 0000000..e9f78bb --- /dev/null +++ b/ucon64/2.0/src/console/dc.c @@ -0,0 +1,646 @@ +/* +dc.c - Dreamcast support for uCON64 + +Copyright (c) 2004 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "misc/file.h" +#include "misc/misc.h" +#include "misc/property.h" +#include "misc/string.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "dc.h" + + +const st_getopt2_t dc_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Dreamcast" /* "1998 SEGA http://www.sega.com" */, + NULL + }, + { + "dc", 0, 0, UCON64_DC, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_DC_SWITCH] + }, +#if 0 + { + "vms", 1, 0, UCON64_VMS, + "SAV", "convert NES SAV file to a VMS file for use with NesterDC", + NULL + }, +#endif + { + "scr", 0, 0, UCON64_SCR, + NULL, "scramble 1ST_READ.BIN for selfboot CDs", + &ucon64_wf[WF_OBJ_DC_DEFAULT] + }, + { + "unscr", 0, 0, UCON64_UNSCR, + NULL, "unscramble 1ST_READ.BIN for non-selfboot CDs", + &ucon64_wf[WF_OBJ_DC_DEFAULT] + }, +#if 0 + { + "ip", 1, 0, UCON64_IP, + "FILE", "extract ip.bin FILE from IMAGE; " OPTION_LONG_S "rom=IMAGE", + NULL + }, +#endif + { + "mkip", 0, 0, UCON64_MKIP, + NULL, "generate IP.BIN file with default values", + &ucon64_wf[WF_OBJ_DC_NO_ROM] + }, + { + "parse", 1, 0, UCON64_PARSE, + "TEMPLATE", "parse TEMPLATE file into a IP.BIN;\n" + "creates an empty template when TEMPLATE does not exist", + &ucon64_wf[WF_OBJ_DC_NO_ROM] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +static int +calc_crc (const unsigned char *buf, int size) +{ + int i, c, n = 0xffff; + + for (i = 0; i < size; i++) + { + n ^= (buf[i] << 8); + for (c = 0; c < 8; c++) + if (n & 0x8000) + n = (n << 1) ^ 4129; + else + n = (n << 1); + } + return n & 0xffff; +} + + +static void +update_crc (char *ip) +{ + int n = calc_crc ((unsigned char *) (ip + 0x40), 16); + char buf[5]; + + sprintf (buf, "%04X", n); + if (memcmp (buf, ip + 0x20, 4)) + { + printf ("Setting CRC to %s (was %.4s)\n", buf, ip + 0x20); + memcpy (ip + 0x20, buf, 4); + } +} + + +static unsigned int seed; + +static void +dc_srand (unsigned int n) +{ + seed = n & 0xffff; +} + + +static unsigned int +dc_rand () +{ + seed = (seed * 2109 + 9273) & 0x7fff; + return (seed + 0xc000) & 0xffff; +} + + +#if 0 +// header for SAV -> VMS conversion +static const uint8_t nstrsave_bin[1024] = { + 0x4e, 0x45, 0x53, 0x52, 0x4f, 0x4d, 0x2e, 0x4e, // 0x8 (8) + 0x45, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10 (16) + 0x4e, 0x65, 0x73, 0x74, 0x65, 0x72, 0x44, 0x43, // 0x18 (24) + 0x20, 0x53, 0x61, 0x76, 0x65, 0x52, 0x61, 0x6d, // 0x20 (32) + 0x20, 0x46, 0x69, 0x6c, 0x65, 0x00, 0x00, 0x00, // 0x28 (40) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x30 (48) + 0x2a, 0x2a, 0x2a, 0x4e, 0x65, 0x73, 0x74, 0x65, // 0x38 (56) + 0x72, 0x44, 0x43, 0x2a, 0x2a, 0x2a, 0x00, 0x00, // 0x40 (64) + 0x01, 0x00, 0x00, 0x00, 0x01, 0x3f, 0x00, 0x00, // 0x48 (72) + 0x80, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50 (80) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x58 (88) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x60 (96) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x68 (104) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x70 (112) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x78 (120) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x80 (128) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x88 (136) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x90 (144) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x98 (152) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xa0 (160) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xa8 (168) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xb0 (176) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xb8 (184) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xc0 (192) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xc8 (200) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xd0 (208) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xd8 (216) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xe0 (224) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xe8 (232) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xf0 (240) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xf8 (248) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x100 (256) + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x108 (264) + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x110 (272) + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x118 (280) + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x120 (288) + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x128 (296) + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x130 (304) + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x138 (312) + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x140 (320) + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x148 (328) + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x150 (336) + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x158 (344) + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x160 (352) + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x168 (360) + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x170 (368) + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x178 (376) + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, // 0x180 (384) + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x188 (392) + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x190 (400) + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x198 (408) + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1a0 (416) + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1a8 (424) + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1b0 (432) + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1b8 (440) + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1c0 (448) + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1c8 (456) + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1d0 (464) + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1d8 (472) + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1e0 (480) + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1e8 (488) + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1f0 (496) + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x1f8 (504) + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // 0x200 (512) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x208 (520) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x210 (528) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x218 (536) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x220 (544) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x228 (552) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x230 (560) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x238 (568) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x240 (576) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x248 (584) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x250 (592) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x258 (600) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x260 (608) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x268 (616) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x270 (624) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x278 (632) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // 0x280 (640) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x288 (648) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x290 (656) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x298 (664) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2a0 (672) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2a8 (680) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2b0 (688) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2b8 (696) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2c0 (704) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2c8 (712) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2d0 (720) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2d8 (728) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2e0 (736) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2e8 (744) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2f0 (752) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2f8 (760) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x300 (768) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x308 (776) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x310 (784) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x318 (792) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x320 (800) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x328 (808) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x330 (816) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x338 (824) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x340 (832) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x348 (840) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x350 (848) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x358 (856) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x360 (864) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x368 (872) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x370 (880) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x378 (888) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x380 (896) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x388 (904) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x390 (912) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x398 (920) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3a0 (928) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3a8 (936) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3b0 (944) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3b8 (952) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3c0 (960) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3c8 (968) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3d0 (976) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3d8 (984) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3e0 (992) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3e8 (1000) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3f0 (1008) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x3f8 (1016) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 0x400 (1024) +}; +#endif + + +int +dc_init (st_rominfo_t *rominfo) +{ + int result = -1; + + rominfo->console_usage = dc_usage[0].help; + + return result; +} + + +static int +check_areasym (char *ptr, int len) +{ + int i, a = 0; + + for (i = 0; i < len; i++) + switch (ptr[i]) + { + case 'J': + a |= (1<<0); + break; + case 'U': + a |= (1<<1); + break; + case 'E': + a |= (1<<2); + break; + case ' ': + break; + default: + fprintf (stderr, "ERROR: Unknown area symbol '%c'\n", ptr[i]); + return -1; + } + for (i = 0; i < len; i++) + if ((a & (1< templ[i].len) + { + fprintf (stderr, "ERROR: Data for field \"%s\" is too long... stripping to %d chars\n", + templ[i].name, templ[i].len); + p[templ[i].len] = 0; + } + + memcpy (ip + templ[i].pos, p, strlen (p)); + + if (templ[i].extra_check) + if (templ[i].extra_check (ip + templ[i].pos, templ[i].len) == -1) + return -1; + + filled_in[i] = 1; + } + + for (i = 0; templ[i].name; i++) + if (!filled_in[i]) + { + fprintf (stderr, "ERROR: Missing value for \"%s\"\n", templ[i].name); + return -1; + } + + return 0; +} + + +int +dc_parse (const char *templ_file) +{ + char ip[0x8000], dest_name[FILENAME_MAX]; + + if (access (templ_file, F_OK) == -1) + { + int i = 0; + + printf ("Creating empty template file: \"%s\"\n", templ_file); + + for (i = 0; templ[i].name; i++) + set_property (templ_file, templ[i].name, templ[i].def, templ[i].comment); + + printf (ucon64_msg[WROTE], templ_file); + } + + if (parse_templ (templ_file, ip) == -1) + return -1; + + update_crc (ip); + + strcpy (dest_name, "ip.bin"); + ucon64_file_handler (dest_name, NULL, 0); + + ucon64_fwrite (ip, 0, 0x8000, dest_name, "wb"); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +dc_mkip (void) +{ + dc_parse ("default"); + + return 0; +} + + +static int +load_chunk (FILE * fh, unsigned char *ptr, int32_t sz) +{ +#define MAXCHUNK (2048*1024) + static int idx[MAXCHUNK / 32]; + int32_t i; + + /* Convert chunk size to number of slices */ + sz /= 32; + + /* Initialize index table with unity, + so that each slice gets loaded exactly once */ + for (i = 0; i < sz; i++) + idx[i] = i; + + for (i = sz - 1; i >= 0; --i) + { + /* Select a replacement index */ + int x = (dc_rand () * i) >> 16; + + /* Swap */ + int tmp = idx[i]; + idx[i] = idx[x]; + idx[x] = tmp; + + /* Load resulting slice */ + if (fread (ptr + 32 * idx[i], 1, 32, fh) != 32) + return -1; + } + return 0; +} + + +static int +load_file (FILE * fh, unsigned char *ptr, uint32_t filesz) +{ + uint32_t chunksz; + + dc_srand (filesz); + + /* Descramble 2 meg blocks for as long as possible, then + gradually reduce the window down to 32 bytes (1 slice) */ + for (chunksz = MAXCHUNK; chunksz >= 32; chunksz >>= 1) + while (filesz >= chunksz) + { + load_chunk (fh, ptr, chunksz); + filesz -= chunksz; + ptr += chunksz; + } + + /* Load final incomplete slice */ + if (filesz) + if (fread (ptr, 1, filesz, fh) != filesz) + return -1; + + return 0; +} + + +static int +save_chunk (FILE * fh, unsigned char *ptr, int32_t sz) +{ + static int idx[MAXCHUNK / 32]; + int32_t i; + + /* Convert chunk size to number of slices */ + sz /= 32; + + /* Initialize index table with unity, + so that each slice gets saved exactly once */ + for (i = 0; i < sz; i++) + idx[i] = i; + + for (i = sz - 1; i >= 0; --i) + { + /* Select a replacement index */ + int x = (dc_rand () * i) >> 16; + + /* Swap */ + int tmp = idx[i]; + idx[i] = idx[x]; + idx[x] = tmp; + + /* Save resulting slice */ + if (fwrite (ptr + 32 * idx[i], 1, 32, fh) != 32) + return -1; + } + + return 0; +} + + +static int +save_file (FILE * fh, unsigned char *ptr, uint32_t filesz) +{ + uint32_t chunksz; + + dc_srand (filesz); + + /* Descramble 2 meg blocks for as long as possible, then + gradually reduce the window down to 32 bytes (1 slice) */ + for (chunksz = MAXCHUNK; chunksz >= 32; chunksz >>= 1) + while (filesz >= chunksz) + { + save_chunk (fh, ptr, chunksz); + filesz -= chunksz; + ptr += chunksz; + } + + /* Save final incomplete slice */ + if (filesz) + if (fwrite (ptr, 1, filesz, fh) != filesz) + return -1; + + return 0; +} + + +static int +descramble (const char *src, char *dst) +{ + unsigned char *ptr = NULL; + uint32_t sz = 0; + FILE *fh; + + if (!(fh = fopen (src, "rb"))) + return -1; + + sz = fsizeof (src); + if (!(ptr = (unsigned char *) malloc (sz))) + return -1; + + load_file (fh, ptr, sz); + fclose (fh); + + if (!(fh = fopen (dst, "wb"))) + return -1; + + if (fwrite (ptr, 1, sz, fh) != sz) + return -1; + + fclose (fh); + free (ptr); + return 0; +} + + +static int +scramble (const char *src, char *dst) +{ + unsigned char *ptr = NULL; + uint32_t sz = 0; + FILE *fh; + + if (!(fh = fopen (src, "rb"))) + return -1; + + sz = fsizeof (src); + + if (!(ptr = (unsigned char *) malloc (sz))) + return -1; + + if (fread (ptr, 1, sz, fh) != sz) + return -1; + + fclose (fh); + + if (!(fh == fopen (dst, "wb"))) + return -1; + save_file (fh, ptr, sz); + + fclose (fh); + + free (ptr); + return 0; +} + + +int +dc_scramble (void) +{ + char dest_name[FILENAME_MAX]; + + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + + if (!scramble (ucon64.rom, dest_name)) + printf (ucon64_msg[WROTE], dest_name); + else + fprintf (stderr, ucon64_msg[WRITE_ERROR], dest_name); + return 0; +} + + +int +dc_unscramble (void) +{ + char dest_name[FILENAME_MAX]; + + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + + if (!descramble (ucon64.rom, dest_name)) + printf (ucon64_msg[WROTE], dest_name); + else + fprintf (stderr, ucon64_msg[WRITE_ERROR], dest_name); + return 0; +} diff --git a/ucon64/2.0/src/console/dc.h b/ucon64/2.0/src/console/dc.h new file mode 100644 index 0000000..1e6b7a9 --- /dev/null +++ b/ucon64/2.0/src/console/dc.h @@ -0,0 +1,29 @@ +/* +dc.h - Dreamcast support for uCON64 + +Copyright (c) 2004 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef DC_H +#define DC_H +extern int dc_init (st_rominfo_t *rominfo); +extern const st_getopt2_t dc_usage[]; +extern int dc_parse (const char *template_file); +extern int dc_mkip (void); +extern int dc_scramble (void); +extern int dc_unscramble (void); +#endif diff --git a/ucon64/2.0/src/console/gb.c b/ucon64/2.0/src/console/gb.c new file mode 100644 index 0000000..7ce0120 --- /dev/null +++ b/ucon64/2.0/src/console/gb.c @@ -0,0 +1,703 @@ +/* +gb.c - Game Boy support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2001 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include "misc/file.h" +#include "misc/misc.h" +#include "misc/string.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "console/nes.h" +#include "backup/gbx.h" +#include "backup/mgd.h" +#include "backup/ssc.h" +#include "patch/ips.h" +#include "patch/bsl.h" +#include "gb.h" + + +#define GAMEBOY_HEADER_START 0x100 +#define GAMEBOY_HEADER_LEN (sizeof (st_gameboy_header_t)) +#define GB_NAME_LEN 15 + +const st_getopt2_t gameboy_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Game Boy/(Super GB)/GB Pocket/Color GB/(GB Advance)" + /*"1989/1994/1996/1998/2001 Nintendo http://www.nintendo.com"*/, + NULL + }, + { + "gb", 0, 0, UCON64_GB, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_GB_SWITCH] + }, + { + "n", 1, 0, UCON64_N, + "NEW_NAME", "change internal ROM name to NEW_NAME", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "logo", 0, 0, UCON64_LOGO, + NULL, "restore ROM logo character data (offset: 0x104-0x134)", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "mgd", 0, 0, UCON64_MGD, + NULL, "convert to Multi Game*/MGD2/RAW", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "ssc", 0, 0, UCON64_SSC, + NULL, "convert to Super Smart Card/SSC", + &ucon64_wf[WF_OBJ_GB_DEFAULT] + }, + { + "sgb", 0, 0, UCON64_SGB, + NULL, "convert from GB Xchanger/GB/GBC to Super Backup Card/GX/GBX", + &ucon64_wf[WF_OBJ_GB_DEFAULT] + }, + { + "gbx", 0, 0, UCON64_GBX, + NULL, "convert from Super Backup Card/GX/GBX to GB Xchanger/GB/GBC", + &ucon64_wf[WF_OBJ_GB_DEFAULT] + }, + { + "n2gb", 1, 0, UCON64_N2GB, + "NESROM", "KAMI's FC EMUlator (NES emulator);\n" + "ROM should be KAMI's FC Emulator ROM image\n" + "NESROM should contain 16 kB of PRG data and 8 kB of CHR data", + &ucon64_wf[WF_OBJ_GB_DEFAULT] + }, + { + "chk", 0, 0, UCON64_CHK, + NULL, "fix ROM checksum", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +/* +0148 ROM size: + 0 - 256kBit = 32kB = 2 banks + 1 - 512kBit = 64kB = 4 banks + 2 - 1Mb = 128kB = 8 banks + 3 - 2Mb = 256kB = 16 banks + 4 - 4Mb = 512kB = 32 banks +0149 RAM size: + 0 - None + 1 - 16kBit = 2kB = 1 bank + 2 - 64kBit = 8kB = 1 bank + 3 - 256kBit = 32kB = 4 banks +*/ +typedef struct st_gameboy_header +{ + unsigned char opcode1; // 0x00 usually 0x00, NOP + unsigned char opcode2; // 0x01 usually 0xc3, JP + unsigned char start_low; // 0x02 first byte of parameter of JP + unsigned char start_high; // 0x03 second byte + unsigned char logo[GB_LOGODATA_LEN]; // 0x04 + unsigned char name[GB_NAME_LEN]; // 0x34 + unsigned char gb_type; // 0x43 + unsigned char maker_high; // 0x44 + unsigned char maker_low; // 0x45 + unsigned char pad; + unsigned char rom_type; // 0x47 + unsigned char rom_size; // 0x48 + unsigned char sram_size; // 0x49 + unsigned char country; // 0x4a + unsigned char maker; // 0x4b + unsigned char version; // 0x4c + unsigned char header_checksum; // 0x4d + unsigned char checksum_high; // 0x4e + unsigned char checksum_low; // 0x4f +} st_gameboy_header_t; + +static st_gameboy_header_t gameboy_header; +const unsigned char gb_logodata[] = // Note: not a static variable + { + 0xce, 0xed, 0x66, 0x66, 0xcc, 0x0d, 0x00, 0x0b, + 0x03, 0x73, 0x00, 0x83, 0x00, 0x0c, 0x00, 0x0d, + 0x00, 0x08, 0x11, 0x1f, 0x88, 0x89, 0x00, 0x0e, + 0xdc, 0xcc, 0x6e, 0xe6, 0xdd, 0xdd, 0xd9, 0x99, + 0xbb, 0xbb, 0x67, 0x63, 0x6e, 0x0e, 0xec, 0xcc, + 0xdd, 0xdc, 0x99, 0x9f, 0xbb, 0xb9, 0x33, 0x3e + }, + rocket_logodata[] = // idem + { + 0x11, 0x23, 0xf1, 0x1e, 0x01, 0x22, 0xf0, 0x00, + 0x08, 0x99, 0x78, 0x00, 0x08, 0x11, 0x9a, 0x48, + 0x11, 0x23, 0xf0, 0x0e, 0x70, 0x01, 0xf8, 0x80, + 0x22, 0x44, 0x44, 0x22, 0x22, 0x21, 0x00, 0x1e, + 0x99, 0x10, 0x00, 0x1e, 0x19, 0x22, 0x44, 0x22, + 0x22, 0x47, 0x00, 0x0e, 0x11, 0x22, 0x00, 0x00 + }; + +typedef struct st_gameboy_chksum +{ + unsigned short value; + unsigned char header; +} st_gameboy_chksum_t; + +static st_gameboy_chksum_t checksum; +static st_gameboy_chksum_t gameboy_chksum (st_rominfo_t *rominfo); + + +int +gameboy_logo (st_rominfo_t *rominfo) +{ + char dest_name[FILENAME_MAX]; + + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + ucon64_fwrite ((unsigned char *) + ((gameboy_header.rom_type >= 0x97 && gameboy_header.rom_type <= 0x99) ? + rocket_logodata : gb_logodata), + rominfo->buheader_len + GAMEBOY_HEADER_START + 4, GB_LOGODATA_LEN, + dest_name, "r+b"); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +gameboy_n2gb (st_rominfo_t *rominfo, const char *nesrom) +{ +#define EMULATOR_LEN 0x10000 + st_ines_header_t ines_header; + int n = 0, crc = 0; + unsigned char *buf; + char dest_name[FILENAME_MAX]; + + if (ucon64.file_size - rominfo->buheader_len != EMULATOR_LEN) + { + fprintf (stderr, "ERROR: %s does not appear to be KAMI's FC emulator\n", ucon64.rom); + return -1; + } + + ucon64_fread (&ines_header, 0, INES_HEADER_LEN, nesrom); + if (memcmp (ines_header.signature, INES_SIG_S, 4)) + { + fprintf (stderr, "ERROR: Only NES ROMs with iNES header are supported\n"); + return -1; + } + if (ines_header.prg_size != 1 || ines_header.chr_size != 1) + { + fprintf (stderr, + "ERROR: Only NES ROMs with 0.1250 Mb of ROM (PRG) and 0.0625 Mb of VROM (CHR)\n" + " are supported by KAMI's FC emulator\n"); + return -1; + } + + if (!(buf = (unsigned char *) malloc (ucon64.file_size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], ucon64.file_size); + return -1; + } + ucon64_fread (buf, 0, ucon64.file_size, ucon64.rom); + ucon64_fread (rominfo->buheader_len + buf + 0x4000, INES_HEADER_LEN, + 0x4000 + 0x2000, nesrom); // read PRG & CHR data + + for (n = 0; n < ucon64.file_size - rominfo->buheader_len; n++) + { + if ((n == GAMEBOY_HEADER_START + 0x4e) || (n == GAMEBOY_HEADER_START + 0x4f)) + continue; + else + crc += buf[rominfo->buheader_len + n]; + } + + buf[rominfo->buheader_len + GAMEBOY_HEADER_START + 0x4e] = crc >> 8; + buf[rominfo->buheader_len + GAMEBOY_HEADER_START + 0x4f] = crc; + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + ucon64_fwrite (buf, 0, ucon64.file_size, dest_name, "wb"); + + free (buf); + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +static int +gameboy_convert_data (st_rominfo_t *rominfo, unsigned char *conversion_table, + const char *suffix) +{ + char dest_name[FILENAME_MAX], src_name[FILENAME_MAX]; + unsigned char buf[MAXBUFSIZE]; + int x, n, n_bytes; + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, suffix); + ucon64_file_handler (dest_name, src_name, 0); + + x = rominfo->buheader_len; + while ((n_bytes = ucon64_fread (buf, x, MAXBUFSIZE, src_name))) + { + for (n = 0; n < n_bytes; n++) + buf[n] = conversion_table[(int) buf[n]]; + ucon64_fwrite (buf, x, n_bytes, dest_name, x == rominfo->buheader_len ? "wb" : "ab"); + x += n_bytes; + } + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +gameboy_gbx (st_rominfo_t *rominfo) +{ + unsigned char gbx2gbc[] = + { + 0xB4, 0xBC, 0xA4, 0xAC, 0x94, 0x9C, 0x84, 0x8C, + 0xF4, 0xFC, 0xE4, 0xEC, 0xD4, 0xDC, 0xC4, 0xCC, + 0x34, 0x3C, 0x24, 0x2C, 0x14, 0x1C, 0x04, 0x0C, + 0x74, 0x7C, 0x64, 0x6C, 0x54, 0x5C, 0x44, 0x4C, + 0xB5, 0xBD, 0xA5, 0xAD, 0x95, 0x9D, 0x85, 0x8D, + 0xF5, 0xFD, 0xE5, 0xED, 0xD5, 0xDD, 0xC5, 0xCD, + 0x35, 0x3D, 0x25, 0x2D, 0x15, 0x1D, 0x05, 0x0D, + 0x75, 0x7D, 0x65, 0x6D, 0x55, 0x5D, 0x45, 0x4D, + 0xB6, 0xBE, 0xA6, 0xAE, 0x96, 0x9E, 0x86, 0x8E, + 0xF6, 0xFE, 0xE6, 0xEE, 0xD6, 0xDE, 0xC6, 0xCE, + 0x36, 0x3E, 0x26, 0x2E, 0x16, 0x1E, 0x06, 0x0E, + 0x76, 0x7E, 0x66, 0x6E, 0x56, 0x5E, 0x46, 0x4E, + 0xB7, 0xBF, 0xA7, 0xAF, 0x97, 0x9F, 0x87, 0x8F, + 0xF7, 0xFF, 0xE7, 0xEF, 0xD7, 0xDF, 0xC7, 0xCF, + 0x37, 0x3F, 0x27, 0x2F, 0x17, 0x1F, 0x07, 0x0F, + 0x77, 0x7F, 0x67, 0x6F, 0x57, 0x5F, 0x47, 0x4F, + 0xB0, 0xB8, 0xA0, 0xA8, 0x90, 0x98, 0x80, 0x88, + 0xF0, 0xF8, 0xE0, 0xE8, 0xD0, 0xD8, 0xC0, 0xC8, + 0x30, 0x38, 0x20, 0x28, 0x10, 0x18, 0x00, 0x08, + 0x70, 0x78, 0x60, 0x68, 0x50, 0x58, 0x40, 0x48, + 0xB1, 0xB9, 0xA1, 0xA9, 0x91, 0x99, 0x81, 0x89, + 0xF1, 0xF9, 0xE1, 0xE9, 0xD1, 0xD9, 0xC1, 0xC9, + 0x31, 0x39, 0x21, 0x29, 0x11, 0x19, 0x01, 0x09, + 0x71, 0x79, 0x61, 0x69, 0x51, 0x59, 0x41, 0x49, + 0xB2, 0xBA, 0xA2, 0xAA, 0x92, 0x9A, 0x82, 0x8A, + 0xF2, 0xFA, 0xE2, 0xEA, 0xD2, 0xDA, 0xC2, 0xCA, + 0x32, 0x3A, 0x22, 0x2A, 0x12, 0x1A, 0x02, 0x0A, + 0x72, 0x7A, 0x62, 0x6A, 0x52, 0x5A, 0x42, 0x4A, + 0xB3, 0xBB, 0xA3, 0xAB, 0x93, 0x9B, 0x83, 0x8B, + 0xF3, 0xFB, 0xE3, 0xEB, 0xD3, 0xDB, 0xC3, 0xCB, + 0x33, 0x3B, 0x23, 0x2B, 0x13, 0x1B, 0x03, 0x0B, + 0x73, 0x7B, 0x63, 0x6B, 0x53, 0x5B, 0x43, 0x4B + }; + const char *old_suffix = get_suffix (ucon64.rom), *new_suffix; + new_suffix = stricmp (old_suffix, ".GBX") ? ".GB" : ".GBC"; + return gameboy_convert_data (rominfo, gbx2gbc, new_suffix); +} + + +int +gameboy_sgb (st_rominfo_t *rominfo) +{ + unsigned char gbc2gbx[] = + { + 0x96, 0xB6, 0xD6, 0xF6, 0x16, 0x36, 0x56, 0x76, + 0x97, 0xB7, 0xD7, 0xF7, 0x17, 0x37, 0x57, 0x77, + 0x94, 0xB4, 0xD4, 0xF4, 0x14, 0x34, 0x54, 0x74, + 0x95, 0xB5, 0xD5, 0xF5, 0x15, 0x35, 0x55, 0x75, + 0x92, 0xB2, 0xD2, 0xF2, 0x12, 0x32, 0x52, 0x72, + 0x93, 0xB3, 0xD3, 0xF3, 0x13, 0x33, 0x53, 0x73, + 0x90, 0xB0, 0xD0, 0xF0, 0x10, 0x30, 0x50, 0x70, + 0x91, 0xB1, 0xD1, 0xF1, 0x11, 0x31, 0x51, 0x71, + 0x9E, 0xBE, 0xDE, 0xFE, 0x1E, 0x3E, 0x5E, 0x7E, + 0x9F, 0xBF, 0xDF, 0xFF, 0x1F, 0x3F, 0x5F, 0x7F, + 0x9C, 0xBC, 0xDC, 0xFC, 0x1C, 0x3C, 0x5C, 0x7C, + 0x9D, 0xBD, 0xDD, 0xFD, 0x1D, 0x3D, 0x5D, 0x7D, + 0x9A, 0xBA, 0xDA, 0xFA, 0x1A, 0x3A, 0x5A, 0x7A, + 0x9B, 0xBB, 0xDB, 0xFB, 0x1B, 0x3B, 0x5B, 0x7B, + 0x98, 0xB8, 0xD8, 0xF8, 0x18, 0x38, 0x58, 0x78, + 0x99, 0xB9, 0xD9, 0xF9, 0x19, 0x39, 0x59, 0x79, + 0x86, 0xA6, 0xC6, 0xE6, 0x06, 0x26, 0x46, 0x66, + 0x87, 0xA7, 0xC7, 0xE7, 0x07, 0x27, 0x47, 0x67, + 0x84, 0xA4, 0xC4, 0xE4, 0x04, 0x24, 0x44, 0x64, + 0x85, 0xA5, 0xC5, 0xE5, 0x05, 0x25, 0x45, 0x65, + 0x82, 0xA2, 0xC2, 0xE2, 0x02, 0x22, 0x42, 0x62, + 0x83, 0xA3, 0xC3, 0xE3, 0x03, 0x23, 0x43, 0x63, + 0x80, 0xA0, 0xC0, 0xE0, 0x00, 0x20, 0x40, 0x60, + 0x81, 0xA1, 0xC1, 0xE1, 0x01, 0x21, 0x41, 0x61, + 0x8E, 0xAE, 0xCE, 0xEE, 0x0E, 0x2E, 0x4E, 0x6E, + 0x8F, 0xAF, 0xCF, 0xEF, 0x0F, 0x2F, 0x4F, 0x6F, + 0x8C, 0xAC, 0xCC, 0xEC, 0x0C, 0x2C, 0x4C, 0x6C, + 0x8D, 0xAD, 0xCD, 0xED, 0x0D, 0x2D, 0x4D, 0x6D, + 0x8A, 0xAA, 0xCA, 0xEA, 0x0A, 0x2A, 0x4A, 0x6A, + 0x8B, 0xAB, 0xCB, 0xEB, 0x0B, 0x2B, 0x4B, 0x6B, + 0x88, 0xA8, 0xC8, 0xE8, 0x08, 0x28, 0x48, 0x68, + 0x89, 0xA9, 0xC9, 0xE9, 0x09, 0x29, 0x49, 0x69 + }; + const char *old_suffix = get_suffix (ucon64.rom), *new_suffix; + new_suffix = stricmp (old_suffix, ".GBC") ? ".GX" : ".GBX"; + return gameboy_convert_data (rominfo, gbc2gbx, new_suffix); +} + + +int +gameboy_n (st_rominfo_t *rominfo, const char *name) +{ + char buf[GB_NAME_LEN], dest_name[FILENAME_MAX]; + + memset (buf, 0, GB_NAME_LEN); + strncpy (buf, name, GB_NAME_LEN); + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + ucon64_fwrite (buf, rominfo->buheader_len + GAMEBOY_HEADER_START + 0x34, + GB_NAME_LEN, dest_name, "r+b"); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +gameboy_chk (st_rominfo_t *rominfo) +{ + char buf[4], dest_name[FILENAME_MAX]; + + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + + buf[0] = checksum.header; + buf[1] = rominfo->current_internal_crc >> 8; + buf[2] = rominfo->current_internal_crc; + ucon64_fwrite (buf, rominfo->buheader_len + GAMEBOY_HEADER_START + 0x4d, 3, + dest_name, "r+b"); + + dumper (stdout, buf, 3, GAMEBOY_HEADER_START + rominfo->buheader_len + 0x4d, DUMPER_HEX); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +gameboy_mgd (st_rominfo_t *rominfo) +// TODO: convert the ROM data +{ + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + int size = ucon64.file_size - rominfo->buheader_len; + + strcpy (src_name, ucon64.rom); + mgd_make_name (ucon64.rom, UCON64_GB, size, dest_name); + ucon64_file_handler (dest_name, src_name, OF_FORCE_BASENAME); + + fcopy (src_name, rominfo->buheader_len, size, dest_name, "wb"); + + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + + mgd_write_index_file ((char *) basename2 (dest_name), 1); + return 0; +} + + +int +gameboy_ssc (st_rominfo_t *rominfo) +// TODO: convert the ROM data +{ + st_unknown_header_t header; + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + const char *p = NULL; + int size = ucon64.file_size - rominfo->buheader_len; + + memset (&header, 0, UNKNOWN_HEADER_LEN); + + header.size_low = size / 8192; + header.size_high = size / 8192 >> 8; + header.id1 = 0xaa; + header.id2 = 0xbb; +#if 0 // TODO: find out correct value. 2 is used for Magic Super Griffin + header.type = 2; +#endif + + strcpy (src_name, ucon64.rom); + p = basename2 (ucon64.rom); + // TODO: find out if this is correct (giving the file name a prefix) + if ((p[0] == 'G' || p[0] == 'g') && (p[1] == 'B' || p[1] == 'b')) + strcpy (dest_name, p); + else + sprintf (dest_name, "gb%s", p); + set_suffix (dest_name, ".gb"); + + ucon64_file_handler (dest_name, src_name, 0); + ucon64_fwrite (&header, 0, UNKNOWN_HEADER_LEN, dest_name, "wb"); + fcopy (src_name, rominfo->buheader_len, size, dest_name, "ab"); + + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return 0; +} + + +int +gameboy_init (st_rominfo_t *rominfo) +{ + int result = -1, value, x; + char buf[MAXBUFSIZE]; + static const char *gameboy_romtype1[0x20] = + { + "ROM only", + "ROM + MBC1", + "ROM + MBC1 + RAM", + "ROM + MBC1 + RAM + Battery", + NULL, + "ROM + MBC2", + "ROM + MBC2 + Battery", + NULL, + "ROM + RAM", // correct? - dbjh + "ROM + RAM + Battery", // correct? - dbjh + NULL, + "ROM + MMM01", + "ROM + MMM01 + SRAM", + "ROM + MMM01 + SRAM + Battery", + NULL, + "ROM + MBC3 + Battery + Timer", + "ROM + MBC3 + RAM + Battery + Timer", + "ROM + MBC3", + "ROM + MBC3 + RAM", + "ROM + MBC3 + RAM + Battery", + NULL, + NULL, + NULL, + NULL, + NULL, + "ROM + MBC5", + "ROM + MBC5 + RAM", + "ROM + MBC5 + RAM + Battery", + "ROM + MBC5 + Rumble", + "ROM + MBC5 + SRAM + Rumble", + "ROM + MBC5 + SRAM + Battery + Rumble", + "Nintendo Pocket Camera" + }, + *gameboy_romtype2[3] = + { + "Rocket Games", + NULL, + "Rocket Games 2-in-1" + }, + *gameboy_romtype3[3] = + { + "Bandai TAMA5", + "Hudson HuC-3", + "Hudson HuC-1" + }, + *str; + + rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? ucon64.buheader_len : 0; + + ucon64_fread (&gameboy_header, rominfo->buheader_len + GAMEBOY_HEADER_START, + GAMEBOY_HEADER_LEN, ucon64.rom); + if (gameboy_header.opcode1 == 0x00 && gameboy_header.opcode2 == 0xc3) + result = 0; + else + { + rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? + ucon64.buheader_len : (int) SSC_HEADER_LEN; + + ucon64_fread (&gameboy_header, rominfo->buheader_len + GAMEBOY_HEADER_START, + GAMEBOY_HEADER_LEN, ucon64.rom); + if (gameboy_header.opcode1 == 0x00 && gameboy_header.opcode2 == 0xc3) + result = 0; + else + result = -1; + } + if (ucon64.console == UCON64_GB) + result = 0; + + rominfo->header_start = GAMEBOY_HEADER_START; + rominfo->header_len = GAMEBOY_HEADER_LEN; + rominfo->header = &gameboy_header; + + // internal ROM name + strncpy (rominfo->name, (const char *) gameboy_header.name, GB_NAME_LEN); + rominfo->name[GB_NAME_LEN] = 0; // terminate string + + // ROM maker + if (gameboy_header.maker == 0x33) + { + int ih = gameboy_header.maker_high <= '9' ? + gameboy_header.maker_high - '0' : gameboy_header.maker_high - 'A' + 10, + il = gameboy_header.maker_low <= '9' ? + gameboy_header.maker_low - '0' : gameboy_header.maker_low - 'A' + 10; + x = ih * 36 + il; + } + else + x = (gameboy_header.maker >> 4) * 36 + (gameboy_header.maker & 0x0f); + + /* + I added the first if statement, because I didn't want to expand + nintendo_maker by a large amount for only one publisher and because index 0 + is used when the publisher code is unknown (so it shouldn't be set to + "Rocket Games"). - dbjh + */ + if (x == 33 * 36 + 33 || x == 0) // publisher code XX/00 + x = 2; // Rocket Games + else if (x < 0 || x >= NINTENDO_MAKER_LEN) + x = 0; + rominfo->maker = NULL_TO_UNKNOWN_S (nintendo_maker[x]); + + // ROM country + rominfo->country = gameboy_header.country == 0 ? "Japan" : "U.S.A. & Europe"; + + // misc stuff + // Don't move division by 4 to shift parameter (gameboy_header.rom_size can be < 2) + sprintf (buf, "Internal size: %.4f Mb\n", (1 << gameboy_header.rom_size) / 4.0f); + strcat (rominfo->misc, buf); + + if (gameboy_header.rom_type <= 0x1f) + str = NULL_TO_UNKNOWN_S (gameboy_romtype1[gameboy_header.rom_type]); + else if (gameboy_header.rom_type >= 0x97 && gameboy_header.rom_type <= 0x99) + str = gameboy_romtype2[gameboy_header.rom_type - 0x97]; + else if (gameboy_header.rom_type >= 0xfd) + str = gameboy_romtype3[gameboy_header.rom_type - 0xfd]; + else + str = "Unknown"; + sprintf (buf, "ROM type: %s\n", str); + strcat (rominfo->misc, buf); + + if (!gameboy_header.sram_size) + sprintf (buf, "Save RAM: No\n"); + else + { + value = (gameboy_header.sram_size & 7) << 1; // 0/1/2/4/5 + if (value) + value = 1 << (value - 1); + + sprintf (buf, "Save RAM: Yes, %d kBytes\n", value); + } + strcat (rominfo->misc, buf); + + sprintf (buf, "Version: 1.%d\n", gameboy_header.version); + strcat (rominfo->misc, buf); + + sprintf (buf, "Game Boy type: %s\n", + (gameboy_header.gb_type == 0x80) ? "Color" : +// (OFFSET (gameboy_header, 0x46) == 0x03) ? "Super" : + "Standard (4 colors)"); + strcat (rominfo->misc, buf); + + value = gameboy_header.start_high << 8; + value += gameboy_header.start_low; + sprintf (buf, "Start address: 0x%04x\n", value); + strcat (rominfo->misc, buf); + + strcat (rominfo->misc, "Logo data: "); + if (memcmp (gameboy_header.logo, + (gameboy_header.rom_type >= 0x97 && gameboy_header.rom_type <= 0x99) ? + rocket_logodata : gb_logodata, + GB_LOGODATA_LEN) == 0) + { +#ifdef USE_ANSI_COLOR + if (ucon64.ansi_color) + strcat (rominfo->misc, "\x1b[01;32mOk\x1b[0m"); + else +#endif + strcat (rominfo->misc, "Ok"); + } + else + { +#ifdef USE_ANSI_COLOR + if (ucon64.ansi_color) + strcat (rominfo->misc, "\x1b[01;31mBad\x1b[0m"); + else +#endif + strcat (rominfo->misc, "Bad"); + } + + if (!UCON64_ISSET (ucon64.do_not_calc_crc) && result == 0) + { + rominfo->has_internal_crc = 1; + rominfo->internal_crc_len = 2; + checksum = gameboy_chksum (rominfo); + rominfo->current_internal_crc = checksum.value; + + rominfo->internal_crc = (gameboy_header.checksum_high << 8) + + gameboy_header.checksum_low; + + x = gameboy_header.header_checksum; + sprintf (rominfo->internal_crc2, + "Header checksum: %s, 0x%02x (calculated) %c= 0x%02x (internal)", +#ifdef USE_ANSI_COLOR + ucon64.ansi_color ? + ((checksum.header == x) ? + "\x1b[01;32mOk\x1b[0m" : "\x1b[01;31mBad\x1b[0m") + : + ((checksum.header == x) ? "Ok" : "Bad"), +#else + (checksum.header == x) ? "Ok" : "Bad", +#endif + checksum.header, (checksum.header == x) ? '=' : '!', x); + } + + rominfo->console_usage = gameboy_usage[0].help; + rominfo->copier_usage = (!rominfo->buheader_len ? mgd_usage[0].help : ssc_usage[0].help); + + return result; +} + + +st_gameboy_chksum_t +gameboy_chksum (st_rominfo_t *rominfo) +{ + st_gameboy_chksum_t sum = { 0, 0 }; + unsigned char *rom_buffer; + int size = ucon64.file_size - rominfo->buheader_len, i; + + if (!(rom_buffer = (unsigned char *) malloc (size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size); + return sum; + } + ucon64_fread (rom_buffer, rominfo->buheader_len, size, ucon64.rom); + + for (i = GAMEBOY_HEADER_START + 0x34; i < GAMEBOY_HEADER_START + 0x4d; i++) + sum.header += ~rom_buffer[i]; + for (i = 0; i < size; i++) + sum.value += rom_buffer[i]; + sum.value -= (rom_buffer[GAMEBOY_HEADER_START + 0x4d] - sum.header) + + rom_buffer[GAMEBOY_HEADER_START + 0x4e] + + rom_buffer[GAMEBOY_HEADER_START + 0x4f]; + + free (rom_buffer); + + return sum; +} diff --git a/ucon64/2.0/src/console/gb.h b/ucon64/2.0/src/console/gb.h new file mode 100644 index 0000000..b8420fc --- /dev/null +++ b/ucon64/2.0/src/console/gb.h @@ -0,0 +1,39 @@ +/* +gb.h - Game Boy support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef GB_H +#define GB_H + +#define GB_LOGODATA_LEN 48 + +extern const st_getopt2_t gameboy_usage[]; +extern const unsigned char gb_logodata[], rocket_logodata[]; + +extern int gameboy_chk (st_rominfo_t *rominfo); +extern int gameboy_gbx (st_rominfo_t *rominfo); +extern int gameboy_mgd (st_rominfo_t *rominfo); +extern int gameboy_n (st_rominfo_t *rominfo, const char *name); +extern int gameboy_n2gb (st_rominfo_t *rominfo, const char *emu_rom); +extern int gameboy_sgb (st_rominfo_t *rominfo); +extern int gameboy_ssc (st_rominfo_t *rominfo); +extern int gameboy_init (st_rominfo_t *rominfo); +extern int gameboy_logo (st_rominfo_t *rominfo); + +#endif diff --git a/ucon64/2.0/src/console/gba.c b/ucon64/2.0/src/console/gba.c new file mode 100644 index 0000000..6148cb5 --- /dev/null +++ b/ucon64/2.0/src/console/gba.c @@ -0,0 +1,850 @@ +/* +gba.c - Game Boy Advance support for uCON64 + +Copyright (c) 2001 NoisyB +Copyright (c) 2001 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include "misc/file.h" +#include "misc/misc.h" +#include "misc/property.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "misc/string.h" +#include "ucon64.h" +#include "ucon64_misc.h" +#include "gba.h" +#include "backup/fal.h" + + +#define GBA_NAME_LEN 12 +#define GBA_HEADER_START 0 +#define GBA_HEADER_LEN (sizeof (st_gba_header_t)) + + +static int gba_chksum (void); + +const st_getopt2_t gba_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Game Boy Advance"/*"2001 Nintendo http://www.nintendo.com"*/, + NULL + }, + { + "gba", 0, 0, UCON64_GBA, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_GBA_SWITCH] + }, + { + "n", 1, 0, UCON64_N, + "NEW_NAME", "change internal ROM name to NEW_NAME", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "logo", 0, 0, UCON64_LOGO, + NULL, "restore ROM logo character data (offset: 0x04-0x9F)", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "chk", 0, 0, UCON64_CHK, + NULL, "fix ROM header checksum", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "sram", 0, 0, UCON64_SRAM, + NULL, "patch ROM for SRAM saving", + &ucon64_wf[WF_OBJ_GBA_DEFAULT] + }, + { + "crp", 1, 0, UCON64_CRP, + "WAIT_TIME", "slow down ROM access (\"crash patch\");\n" + "WAIT_TIME=0 default in most crash patches\n" + "WAIT_TIME=4 faster than 0, slower than 8\n" + "WAIT_TIME=8 faster than 4, slower than 28\n" + "WAIT_TIME=12 slowest cartridge access speed\n" + "WAIT_TIME=16 faster than 28, but slower than 20\n" + "WAIT_TIME=20 default in most original cartridges\n" + "WAIT_TIME=24 fastest cartridge access speed\n" + "WAIT_TIME=28 faster than 8 but slower than 16", + &ucon64_wf[WF_OBJ_GBA_DEFAULT] + }, +// "n 0 and 28, with a stepping of 4. I.e. 0, 4, 8, 12 ...\n" + { + "multi", 1, 0, UCON64_MULTI, + "SIZE", "make multi-game file for use with FAL/F2A flash card, truncated\n" + "to SIZE Mbit; file with loader must be specified first, then\n" + "all the ROMs, multi-game file to create last", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_STOP] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +/* +Offset 00h-03h - Start Address - A 32 bit ARM B command with jump destination + to the start address of the program, cannot be manipulated + with this tool, there's no reason. + +Offset 04h-9fh - Nintendo logo character data - The fix Nintendo logo graphics + needed to start a ROM on the real machine as it is verified + by it. + +Offset a0h-abh - Game title - The game title is an ASCII string, officially + can use only ASCII characters between the ASCII code 20h and + 60h. Although it is not a strict rule for hobby programmers, + it is fun to follow such a rules in my opinion. As I know + developers can choose their own game title here describing + the product in short. + +Offset ach-afh - Game code - The 4 bytes long code of the game is an ASCII + string too, officially can use only ASCII characters between + the ASCII code 20h and 60h. The first letter is always A as I + know, probably stands for GBA, so it won't change unless a + higher hardware with backwards compatibility won't be + introduced and this letter could hold some more infos about + it. The second and third letters are the shortened version of + the name of the game. And the fourth letter is the territory + code. Don't afraid, there's no territory lockout, this is for + information purposes only. So far as I know J stands for + Japan and Asia, E stands for USA and the whole American + continent and P stands for Europe, Australia and Africa + (probably came from that these are the PAL video standard + territories, but I could be wrong). Although it is not a + strict rule for hobby programmers, it is fun to follow such a + rules in my opinion. Developers get this 4 letter code right + from Nintendo and they have to use that. + +Offset b0h-b1h - Maker code - The 2 bytes long code of the developer company + is an ASCII string too, officially can use only ASCII + characters between the ASCII code 20h and 60h. Although it is + not a strict rule for hobby programmers, it is fun to follow + such a rules in my opinion. Developers get this 2 letter code + right from Nintendo and they have to use that. + +Offset b2h-b2h - 96h - Fixed 96h byte without any useful information. + +Offset b3h-b3h - Main unit code - This hexadecimal byte is the destination + hardware code. It is always 00h at the moment as it stands + for Game Boy Advance, so it won't change in the future either + unless a higher hardware with backwards compatibility won't + be introduced and this byte could hold some more infos about + it. There's no reason to change this or write something + different than 00h into it. + +Offset b4h-b4h - Device type - This hexadecimal byte is the device type code. + It is always 00h as the only other possible value stands for + a debugger cart what I assume won't be available on the + streets and I assume even if a developer works with such a + hardware, he or she doesn't have to change this byte, however + he or she easily can of course. So there's no reason to + change this or write something different than 00h into it. + +Offset b5h-bbh - Reserved area - Fixed, 00h filled area without any useful + information. + +Offset bch-bch - Mask ROM version number - This hexadecimal byte holds the + version number of the ROM. As I know it works somehow that + way, the first published (and released on the streets) is + always the first version and for that 00h is stored here. In + the case it is getting updated, so in the same territory the + very same game with the very same title is getting replaced + with a new version, what is happening rarely, the number here + is getting increased by one. So usually this byte holds 00h + and there isn't too much reason to write something here and + something else than 00h. + +Offset bdh-bdh - Complement check - This hexadecimal byte have to be + calculated automatically, when the whole header is in its + final state, so nothing will change inside of it. (Manually + it would be hard to calculate.) Add the bytes between offset + a0h and bch together, take the number's two's complement and + add 19h to the result. Store the lowest 8 bits here. Or + calculate automatically with GBARM. The hardware is + verifying this byte just like the Nintendo logo character + data and in the case it isn't correct, the game won't start + on the real machine. + +Offset beh-bfh - Reserved area - Fixed, 00h filled area without any useful + information. +*/ +typedef struct st_gba_header +{ + unsigned char start[4]; // 0x00 + unsigned char logo[GBA_LOGODATA_LEN]; // 0x04 + unsigned char name[GBA_NAME_LEN]; // 0xa0 + unsigned char game_id_prefix; // 0xac + unsigned char game_id_low; // 0xad + unsigned char game_id_high; // 0xae + unsigned char game_id_country; // 0xaf + unsigned char maker_high; // 0xb0 + unsigned char maker_low; // 0xb1 + unsigned char pad1; + unsigned char gba_type; // 0xb3 + unsigned char device_type; // 0xb4 + unsigned char pad2[7]; + unsigned char version; // 0xbc + unsigned char checksum; // 0xbd + unsigned char pad3[2]; +} st_gba_header_t; + +static st_gba_header_t gba_header; +const unsigned char gba_logodata[] = // Note: not a static variable + { + 0x24, 0xff, 0xae, 0x51, + 0x69, 0x9a, 0xa2, 0x21, 0x3d, 0x84, 0x82, 0x0a, + 0x84, 0xe4, 0x09, 0xad, 0x11, 0x24, 0x8b, 0x98, + 0xc0, 0x81, 0x7f, 0x21, 0xa3, 0x52, 0xbe, 0x19, + 0x93, 0x09, 0xce, 0x20, 0x10, 0x46, 0x4a, 0x4a, + 0xf8, 0x27, 0x31, 0xec, 0x58, 0xc7, 0xe8, 0x33, + 0x82, 0xe3, 0xce, 0xbf, 0x85, 0xf4, 0xdf, 0x94, + 0xce, 0x4b, 0x09, 0xc1, 0x94, 0x56, 0x8a, 0xc0, + 0x13, 0x72, 0xa7, 0xfc, 0x9f, 0x84, 0x4d, 0x73, + 0xa3, 0xca, 0x9a, 0x61, 0x58, 0x97, 0xa3, 0x27, + 0xfc, 0x03, 0x98, 0x76, 0x23, 0x1d, 0xc7, 0x61, + 0x03, 0x04, 0xae, 0x56, 0xbf, 0x38, 0x84, 0x00, + 0x40, 0xa7, 0x0e, 0xfd, 0xff, 0x52, 0xfe, 0x03, + 0x6f, 0x95, 0x30, 0xf1, 0x97, 0xfb, 0xc0, 0x85, + 0x60, 0xd6, 0x80, 0x25, 0xa9, 0x63, 0xbe, 0x03, + 0x01, 0x4e, 0x38, 0xe2, 0xf9, 0xa2, 0x34, 0xff, + 0xbb, 0x3e, 0x03, 0x44, 0x78, 0x00, 0x90, 0xcb, + 0x88, 0x11, 0x3a, 0x94, 0x65, 0xc0, 0x7c, 0x63, + 0x87, 0xf0, 0x3c, 0xaf, 0xd6, 0x25, 0xe4, 0x8b, + 0x38, 0x0a, 0xac, 0x72, 0x21, 0xd4, 0xf8, 0x07 + }; + + +int +gba_n (st_rominfo_t *rominfo, const char *name) +{ + char buf[GBA_NAME_LEN], dest_name[FILENAME_MAX]; + + memset (buf, 0, GBA_NAME_LEN); + strncpy (buf, name, GBA_NAME_LEN); + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + ucon64_fwrite (buf, GBA_HEADER_START + rominfo->buheader_len + 0xa0, GBA_NAME_LEN, + dest_name, "r+b"); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +gba_logo (st_rominfo_t *rominfo) +{ + char dest_name[FILENAME_MAX]; + + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + ucon64_fwrite (gba_logodata, GBA_HEADER_START + rominfo->buheader_len + 0x04, + GBA_LOGODATA_LEN, dest_name, "r+b"); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +gba_chk (st_rominfo_t *rominfo) +{ + char buf, dest_name[FILENAME_MAX]; + + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + + buf = rominfo->current_internal_crc; + ucon64_fputc (dest_name, GBA_HEADER_START + rominfo->buheader_len + 0xbd, + buf, "r+b"); + + dumper (stdout, &buf, 1, GBA_HEADER_START + rominfo->buheader_len + 0xbd, DUMPER_HEX); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +gba_sram (void) +// This function is based on Omar Kilani's gbautil 1.1 +{ + unsigned char st_orig[2][10] = + { + { 0x0E, 0x48, 0x39, 0x68, 0x01, 0x60, 0x0E, 0x48, 0x79, 0x68 }, + { 0x13, 0x4B, 0x18, 0x60, 0x13, 0x48, 0x01, 0x60, 0x13, 0x49 } + }, + st_repl[2][10] = + { + { 0x00, 0x48, 0x00, 0x47, 0x01, 0xFF, 0xFF, 0x08, 0x79, 0x68 }, + { 0x01, 0x4C, 0x20, 0x47, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x08 } + }, + fl_orig[2][24] = + { + { + 0xD0, 0x20, 0x00, 0x05, 0x01, 0x88, 0x01, 0x22, 0x08, 0x1C, + 0x10, 0x40, 0x02, 0x1C, 0x11, 0x04, 0x08, 0x0C, 0x00, 0x28, + 0x01, 0xD0, 0x1B, 0xE0 + }, + { + 0xD0, 0x21, 0x09, 0x05, 0x01, 0x23, 0x0C, 0x4A, 0x08, 0x88, + 0x18, 0x40, 0x00, 0x28, 0x08, 0xD1, 0x10, 0x78, 0x00, 0x28, + 0xF8, 0xD0, 0x08, 0x88 + } + }, + fl_repl[2][24] = + { + { + 0xE0, 0x20, 0x00, 0x05, 0x01, 0x88, 0x01, 0x22, 0x08, 0x1C, + 0x10, 0x40, 0x02, 0x1C, 0x11, 0x04, 0x08, 0x0C, 0x00, 0x28, + 0x01, 0xD0, 0x1B, 0xE0 + }, + { + 0xE0, 0x21, 0x09, 0x05, 0x01, 0x23, 0x0C, 0x4A, 0x08, 0x88, + 0x18, 0x40, 0x00, 0x28, 0x08, 0xD1, 0x10, 0x78, 0x00, 0x28, + 0xF8, 0xD0, 0x08, 0x88 + } + }, + p_repl[2][188] = + { + { + 0x39, 0x68, 0x27, 0x48, 0x81, 0x42, 0x23, 0xD0, 0x89, 0x1C, + 0x08, 0x88, 0x01, 0x28, 0x02, 0xD1, 0x24, 0x48, 0x78, 0x60, + 0x33, 0xE0, 0x00, 0x23, 0x00, 0x22, 0x89, 0x1C, 0x10, 0xB4, + 0x01, 0x24, 0x08, 0x68, 0x20, 0x40, 0x5B, 0x00, 0x03, 0x43, + 0x89, 0x1C, 0x52, 0x1C, 0x06, 0x2A, 0xF7, 0xD1, 0x10, 0xBC, + 0x39, 0x60, 0xDB, 0x01, 0x02, 0x20, 0x00, 0x02, 0x1B, 0x18, + 0x0E, 0x20, 0x00, 0x06, 0x1B, 0x18, 0x7B, 0x60, 0x39, 0x1C, + 0x08, 0x31, 0x08, 0x88, 0x09, 0x38, 0x08, 0x80, 0x16, 0xE0, + 0x15, 0x49, 0x00, 0x23, 0x00, 0x22, 0x10, 0xB4, 0x01, 0x24, + 0x08, 0x68, 0x20, 0x40, 0x5B, 0x00, 0x03, 0x43, 0x89, 0x1C, + 0x52, 0x1C, 0x06, 0x2A, 0xF7, 0xD1, 0x10, 0xBC, 0xDB, 0x01, + 0x02, 0x20, 0x00, 0x02, 0x1B, 0x18, 0x0E, 0x20, 0x00, 0x06, + 0x1B, 0x18, 0x08, 0x3B, 0x3B, 0x60, 0x0B, 0x48, 0x39, 0x68, + 0x01, 0x60, 0x0A, 0x48, 0x79, 0x68, 0x01, 0x60, 0x0A, 0x48, + 0x39, 0x1C, 0x08, 0x31, 0x0A, 0x88, 0x80, 0x21, 0x09, 0x06, + 0x0A, 0x43, 0x02, 0x60, 0x07, 0x48, 0x00, 0x47, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0E, 0x04, 0x00, + 0x00, 0x0E, 0xD4, 0x00, 0x00, 0x04, 0xD8, 0x00, 0x00, 0x04, + 0xDC, 0x00, 0x00, 0x04, 0xFF, 0xFF, 0xFF, 0x08 + }, + { + 0x22, 0x4C, 0x84, 0x42, 0x20, 0xD0, 0x80, 0x1C, 0x04, 0x88, + 0x01, 0x25, 0x2C, 0x40, 0x01, 0x2C, 0x02, 0xD1, 0x80, 0x1E, + 0x1E, 0x49, 0x2E, 0xE0, 0x00, 0x23, 0x00, 0x24, 0x80, 0x1C, + 0x40, 0xB4, 0x01, 0x26, 0x05, 0x68, 0x35, 0x40, 0x5B, 0x00, + 0x2B, 0x43, 0x80, 0x1C, 0x64, 0x1C, 0x06, 0x2C, 0xF7, 0xD1, + 0x40, 0xBC, 0xDB, 0x01, 0x02, 0x24, 0x24, 0x02, 0x1B, 0x19, + 0x0E, 0x24, 0x24, 0x06, 0x1B, 0x19, 0x19, 0x1C, 0x09, 0x3A, + 0x16, 0xE0, 0x12, 0x48, 0x00, 0x23, 0x00, 0x24, 0x40, 0xB4, + 0x01, 0x26, 0x05, 0x68, 0x35, 0x40, 0x5B, 0x00, 0x2B, 0x43, + 0x80, 0x1C, 0x64, 0x1C, 0x06, 0x2C, 0xF7, 0xD1, 0x40, 0xBC, + 0xDB, 0x01, 0x02, 0x24, 0x24, 0x02, 0x1B, 0x19, 0x0E, 0x24, + 0x24, 0x06, 0x1B, 0x19, 0x08, 0x3B, 0x18, 0x1C, 0x08, 0x4C, + 0x20, 0x60, 0x08, 0x4C, 0x21, 0x60, 0x08, 0x49, 0x80, 0x20, + 0x00, 0x06, 0x02, 0x43, 0x0A, 0x60, 0x06, 0x4C, 0x20, 0x47, + 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0E, 0x04, 0x00, + 0x00, 0x0E, 0xD4, 0x00, 0x00, 0x04, 0xD8, 0x00, 0x00, 0x04, + 0xDC, 0x00, 0x00, 0x04, 0xFF, 0xFF, 0xFF, 0x08 + } + }, + major, minor, micro, *buffer, *bufferptr, *ptr, value; + char dest_name[FILENAME_MAX]; + int p_size[2] = { 188, 168 }, p_off, st_off; + unsigned int fsize = ucon64.file_size; + FILE *destfile; + + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + + if ((destfile = fopen (dest_name, "rb+")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], dest_name); + return -1; + } + if (!(buffer = (unsigned char *) malloc (fsize))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], fsize); + fclose (destfile); + exit (1); + } + if (fread (buffer, 1, fsize, destfile) != fsize) + { + fprintf (stderr, ucon64_msg[READ_ERROR], dest_name); + free (buffer); + fclose (destfile); + return -1; + } + + bufferptr = buffer + 160 + 12 + 4; + + ptr = (unsigned char *) memmem2 (bufferptr, fsize, "EEPROM_", 7, 0); + if (ptr == 0) + { + printf ("This ROM does not appear to use EEPROM saving\n"); + free (buffer); + fclose (destfile); + return -1; + } + major = ptr[8] - '0'; + minor = ptr[9] - '0'; + micro = ptr[10] - '0'; + if (ucon64.quiet < 0) + printf ("version: %d.%d.%d; offset: 0x%08x\n", + major, minor, micro, (int) (ptr - buffer)); + if (minor > 2) + { + fputs ("ERROR: ROMs with an EEPROM minor version higher than 2 are not supported\n", stderr); + free (buffer); + fclose (destfile); + return -1; + } + + ptr = (unsigned char *) memmem2 (bufferptr, fsize, + fl_orig[minor - 1], sizeof (fl_orig[minor - 1]), 0); + if (ptr == 0) + { + fputs ("ERROR: Could not find fl pattern. Perhaps this file is already patched?\n", stderr); + free (buffer); + fclose (destfile); + return -1; + } + if (ucon64.quiet < 0) + printf ("fl offset: 0x%08x\n", (int) (ptr - buffer)); + fseek (destfile, ptr - buffer, SEEK_SET); + fwrite (fl_repl[minor - 1], 1, sizeof (fl_repl[minor - 1]), destfile); + + ptr = buffer + fsize - 1; + value = *ptr; + do + ptr--; + while (*ptr == value && ptr - buffer > 0); + + p_off = (ptr - buffer + 0xff) & ~0xff; // align at 256 byte boundary + if (ucon64.quiet < 0) + printf ("p_off: 0x%08x\n", p_off); + // if the SRAM function won't fit at the end of the ROM, abort + if ((minor == 1 && (int) (fsize - 188) < p_off) || + (minor == 2 && (int) (fsize - 168) < p_off)) + { + fputs ("ERROR: Not enough room for SRAM function at end of ROM\n", stderr); + free (buffer); + fclose (destfile); + return -1; + } + + ptr = (unsigned char *) memmem2 (bufferptr, fsize, + st_orig[minor - 1], sizeof (st_orig[minor - 1]), 0); + if (ptr == 0) + { + fputs ("ERROR: Could not find st pattern\n", stderr); + free (buffer); + fclose (destfile); + return -1; + } + st_off = ptr - buffer; + if (ucon64.quiet < 0) + printf ("st offset: 0x%08x\n", st_off); + + bufferptr = buffer + p_off; + switch (minor) + { + case 1: + // these are the offsets to the caller function, it handles all saving and + // is at st_off + p_repl[minor - 1][184] = (unsigned char) (st_off + 0x21); + p_repl[minor - 1][186] = (unsigned char) (st_off >> 16); + + if (*(bufferptr - 1) == 0xff) + p_repl[minor - 1][185] = (unsigned char) (st_off >> 8); + else + { + st_off += 0x1f; + p_repl[minor - 1][185] = (unsigned char) (st_off >> 8); + } + + // tell the calling function where the SRAM function is (p_off) + st_repl[minor - 1][5] = (unsigned char) (p_off >> 8); + st_repl[minor - 1][6] = (unsigned char) (p_off >> 16); + break; + case 2: + // offsets to the caller function + p_repl[minor - 1][164] = (unsigned char) (st_off + 0x13); + p_repl[minor - 1][165] = (unsigned char) (st_off >> 8); + p_repl[minor - 1][166] = (unsigned char) (st_off >> 16); + + // tell the calling function where the SRAM function is + st_repl[minor - 1][7] = (unsigned char) (p_off >> 8); + st_repl[minor - 1][8] = (unsigned char) (p_off >> 16); + break; + } + fseek (destfile, st_off, SEEK_SET); + fwrite (st_repl[minor - 1], 1, sizeof (st_repl[minor - 1]), destfile); + fseek (destfile, p_off, SEEK_SET); + fwrite (p_repl[minor - 1], 1, p_size[minor - 1], destfile); + + free (buffer); + fclose (destfile); + + puts ("SRAM patch applied"); + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +gba_crp (st_rominfo_t *rominfo, const char *value) +{ + FILE *srcfile, *destfile; + int bytesread, n = 0; + char buffer[32 * 1024], src_name[FILENAME_MAX], dest_name[FILENAME_MAX], + replace[2], wait_time = atoi (value); + + if (wait_time % 4 != 0 || wait_time > 28 || wait_time < 0) + { + fprintf (stderr, "ERROR: You specified an incorrect WAIT_TIME value\n"); + return -1; + } + + puts ("Applying crash patch..."); + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, src_name, 0); + if ((srcfile = fopen (src_name, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], src_name); + return -1; + } + if ((destfile = fopen (dest_name, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], dest_name); + return -1; + } + if (rominfo->buheader_len) // copy header (if present) + { + fread (buffer, 1, rominfo->buheader_len, srcfile); + fwrite (buffer, 1, rominfo->buheader_len, destfile); + } + + replace[0] = wait_time; + replace[1] = 0x40; + while ((bytesread = fread (buffer, 1, 32 * 1024, srcfile))) + { // '!' == ASCII 33 (\x21), '*' == 42 (\x2a) + n += change_mem (buffer, bytesread, "\x04\x02\x00\x04\x14\x40", 6, '*', '!', replace, 1, -1); + n += change_mem (buffer, bytesread, "\x02\x00\x04\x14\x40\x00", 6, '*', '!', replace, 1, -2); + n += change_mem (buffer, bytesread, "\x04\x02\x00\x04\xB4\x45", 6, '*', '!', replace, 2, -1); + n += change_mem (buffer, bytesread, "\x3E\xE0\x00\x00\xB4\x45", 6, '*', '!', replace, 2, -1); + n += change_mem (buffer, bytesread, "\x04\x02\x00\x04\x94\x44", 6, '*', '!', replace, 2, -1); + + fwrite (buffer, 1, bytesread, destfile); + } + fclose (srcfile); + fclose (destfile); + + printf ("Found %d pattern%s\n", n, n != 1 ? "s" : ""); + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return 0; +} + + +int +gba_init (st_rominfo_t *rominfo) +{ + int result = -1, value; + char buf[MAXBUFSIZE]; + + rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? + ucon64.buheader_len : 0; + + ucon64_fread (&gba_header, GBA_HEADER_START + + rominfo->buheader_len, GBA_HEADER_LEN, ucon64.rom); + if (/*gba_header.game_id_prefix == 'A' && */ // 'B' in Mario vs. Donkey Kong + gba_header.start[3] == 0xea && gba_header.pad1 == 0x96 && gba_header.gba_type == 0) + result = 0; + else + { +#if 0 // AFAIK (dbjh) GBA ROMs never have a header + rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? + ucon64.buheader_len : UNKNOWN_HEADER_LEN; + + ucon64_fread (&gba_header, GBA_HEADER_START + + rominfo->buheader_len, GBA_HEADER_LEN, ucon64.rom); + if (gba_header.game_id_prefix == 'A' && gba_header.gba_type == 0) + result = 0; + else +#endif + result = -1; + } + if (ucon64.console == UCON64_GBA) + result = 0; + + rominfo->header_start = GBA_HEADER_START; + rominfo->header_len = GBA_HEADER_LEN; + rominfo->header = &gba_header; + + // internal ROM name + strncpy (rominfo->name, (const char *) gba_header.name, GBA_NAME_LEN); + rominfo->name[GBA_NAME_LEN] = 0; + + // ROM maker + { + int ih = gba_header.maker_high <= '9' ? + gba_header.maker_high - '0' : gba_header.maker_high - 'A' + 10, + il = gba_header.maker_low <= '9' ? + gba_header.maker_low - '0' : gba_header.maker_low - 'A' + 10; + value = ih * 36 + il; + } + if (value < 0 || value >= NINTENDO_MAKER_LEN) + value = 0; + rominfo->maker = NULL_TO_UNKNOWN_S (nintendo_maker[value]); + + // ROM country + rominfo->country = + (gba_header.game_id_country == 'J') ? "Japan/Asia" : + (gba_header.game_id_country == 'E') ? "U.S.A." : + (gba_header.game_id_country == 'P') ? "Europe, Australia and Africa" : + "Unknown country"; + + // misc stuff + sprintf (buf, "Version: %d\n", gba_header.version); + strcat (rominfo->misc, buf); + + sprintf (buf, "Device type: 0x%02x\n", gba_header.device_type); + strcat (rominfo->misc, buf); + + /* + start address = current address + (parameter of B instruction * 4) + 8 + gba_header.start[3] is opcode of B instruction (0xea) + */ + value = 0x8000008 + + (gba_header.start[2] << 18 | gba_header.start[1] << 10 | gba_header.start[0] << 2); + sprintf (buf, "Start address: 0x%08x\n", value); + strcat (rominfo->misc, buf); + + strcat (rominfo->misc, "Logo data: "); + if (memcmp (gba_header.logo, gba_logodata, GBA_LOGODATA_LEN) == 0) + { +#ifdef USE_ANSI_COLOR + if (ucon64.ansi_color) + strcat (rominfo->misc, "\x1b[01;32mOk\x1b[0m"); + else +#endif + strcat (rominfo->misc, "Ok"); + } + else + { +#ifdef USE_ANSI_COLOR + if (ucon64.ansi_color) + strcat (rominfo->misc, "\x1b[01;31mBad\x1b[0m"); + else +#endif + strcat (rominfo->misc, "Bad"); + } + + // internal ROM crc + if (!UCON64_ISSET (ucon64.do_not_calc_crc) && result == 0) + { + rominfo->has_internal_crc = 1; + rominfo->internal_crc_len = 1; + rominfo->current_internal_crc = gba_chksum (); + + rominfo->internal_crc = gba_header.checksum; + rominfo->internal_crc2[0] = 0; + } + + rominfo->console_usage = gba_usage[0].help; + // We use fal_usage, but we could just as well use f2a_usage + rominfo->copier_usage = (!rominfo->buheader_len ? fal_usage[0].help : unknown_usage[0].help); + + return result; +} + + +int +gba_chksum (void) +// Note that this function only calculates the checksum of the internal header +{ + unsigned char sum = 0x19, *ptr = (unsigned char *) &gba_header + 0xa0; + + while (ptr < (unsigned char *) &gba_header + 0xbd) + sum += *ptr++; + sum = -sum; + + return sum; +} + + +int +gba_multi (int truncate_size, char *multi_fname) +// TODO: Check if 1024 Mbit multi-game files are supported by the FAL code +{ +#define BUFSIZE (32 * 1024) + int n, n_files, file_no, bytestowrite, byteswritten, totalsize = 0, done, + truncated = 0, size_pow2_lesser = 1, size_pow2 = 1, truncate_size_ispow2 = 0; + struct stat fstate; + FILE *srcfile, *destfile; + char buffer[BUFSIZE], fname[FILENAME_MAX], *fname_ptr; + + if (truncate_size == 0) + { + fprintf (stderr, "ERROR: Can't make multi-game file of 0 bytes\n"); + return -1; + } + +#if 0 + if (truncate_size != 64 * MBIT && truncate_size != 128 * MBIT && + truncate_size != 256 * MBIT && truncate_size != 512 * MBIT && + truncate_size != 1024 * MBIT) + { + fprintf (stderr, "ERROR: Truncate size must be 64, 128, 256, 512 or 1024\n"); + return -1; + } +#endif + + if (multi_fname != NULL) // -xfalmulti + { + strcpy (fname, multi_fname); + n_files = ucon64.argc; + } + else // -multi + { + strcpy (fname, ucon64.argv[ucon64.argc - 1]); + n_files = ucon64.argc - 1; + } + + ucon64_file_handler (fname, NULL, OF_FORCE_BASENAME); + if ((destfile = fopen (fname, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], fname); + return -1; + } + printf ("Creating multi-game file for FAL(/F2A): %s\n", fname); + + file_no = 0; + for (n = 1; n < n_files; n++) + { + if (access (ucon64.argv[n], F_OK)) + continue; // "file" does not exist (option) + stat (ucon64.argv[n], &fstate); + if (!S_ISREG (fstate.st_mode)) + continue; + + if (file_no == 0) + { + if (multi_fname != NULL) // -xfalmulti + { + get_property_fname (ucon64.configfile, "gbaloader", fname, "loader.bin"); + if (access (fname, F_OK)) + { + fprintf (stderr, "ERROR: Cannot open loader binary (%s)\n", fname); + return -1; + } + fname_ptr = fname; + // NOTE: loop counter is modified, because we have to insert + // loader in the file list + n--; + } + else // -multi + fname_ptr = ucon64.argv[n]; + + printf ("Loader: %s\n", fname_ptr); + if (fsizeof (fname_ptr) > 64 * 1024) + printf ("WARNING: Are you sure %s is a loader binary?\n", fname_ptr); + } + else + { + fname_ptr = ucon64.argv[n]; + printf ("ROM%d: %s\n", file_no, fname_ptr); + } + + if ((srcfile = fopen (fname_ptr, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], fname_ptr); + continue; + } + done = 0; + byteswritten = 0; // # of bytes written per file + while (!done) + { + bytestowrite = fread (buffer, 1, BUFSIZE, srcfile); + if (totalsize + bytestowrite > truncate_size) + { + bytestowrite = truncate_size - totalsize; + done = 1; + truncated = 1; + printf ("Output file is %d Mbit, truncating %s, skipping %d bytes\n", + truncate_size / MBIT, fname_ptr, + fsizeof (fname_ptr) - (byteswritten + bytestowrite)); + // DON'T use fstate.st_size, because file could be compressed + } + totalsize += bytestowrite; + if (bytestowrite == 0) + done = 1; + fwrite (buffer, 1, bytestowrite, destfile); + byteswritten += bytestowrite; + } + fclose (srcfile); + if (truncated) + break; + file_no++; + } + fclose (destfile); + + /* + Display a notification if a truncate size was specified that is not exactly + the size of one of the flash card sizes. + */ + n = truncate_size; + while (n >>= 1) + size_pow2 <<= 1; + if (truncate_size == size_pow2) + truncate_size_ispow2 = 1; + + n = totalsize - 1; + while (n >>= 1) + size_pow2_lesser <<= 1; + + size_pow2 = size_pow2_lesser << 1; + + if (totalsize > 64 * MBIT && !truncate_size_ispow2) + printf("\n" + "NOTE: This multi-game file can only be written to a card >= %d Mbit.\n" + " Use -multi=%d to create a file truncated to %d Mbit.\n" + " Current size is %.5f Mbit\n", // 5 digits to have 1 byte resolution + size_pow2 / MBIT, size_pow2_lesser / MBIT, size_pow2_lesser / MBIT, + totalsize / (float) MBIT); + + return 0; +} diff --git a/ucon64/2.0/src/console/gba.h b/ucon64/2.0/src/console/gba.h new file mode 100644 index 0000000..879acc5 --- /dev/null +++ b/ucon64/2.0/src/console/gba.h @@ -0,0 +1,37 @@ +/* +gba.h - Game Boy Advance support for uCON64 + +Copyright (c) 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef GBA_H +#define GBA_H + +#define GBA_LOGODATA_LEN 156 + +extern const st_getopt2_t gba_usage[]; +extern const unsigned char gba_logodata[]; + +extern int gba_chk (st_rominfo_t *rominfo); +extern int gba_crp (st_rominfo_t *rominfo, const char *value); +extern int gba_init (st_rominfo_t *rominfo); +extern int gba_logo (st_rominfo_t *rominfo); +extern int gba_n (st_rominfo_t *rominfo, const char *name); +extern int gba_sram (void); +extern int gba_multi (int truncate_size, char *fname); + +#endif diff --git a/ucon64/2.0/src/console/genesis.c b/ucon64/2.0/src/console/genesis.c new file mode 100644 index 0000000..d0ad32b --- /dev/null +++ b/ucon64/2.0/src/console/genesis.c @@ -0,0 +1,1728 @@ +/* +genesis.c - Sega Genesis/Mega Drive support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2002 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +// NOTE: The people at Sega refer to their company as Sega in normal text (not as SEGA) +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include "misc/misc.h" +#include "misc/chksum.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "console/genesis.h" +#include "backup/mgd.h" +#include "backup/smd.h" + + +#define GENESIS_HEADER_START 256 +#define GENESIS_HEADER_LEN (sizeof (st_genesis_header_t)) +#define GENESIS_NAME_LEN 48 + +static int genesis_chksum (unsigned char *rom_buffer); +static unsigned char *load_rom (st_rominfo_t *rominfo, const char *name, + unsigned char *rom_buffer); +static int save_rom (st_rominfo_t *rominfo, const char *name, + unsigned char **buffer, int size); + + +const st_getopt2_t genesis_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Genesis/Sega Mega Drive/Sega CD/32X/Nomad"/*"1989/19XX/19XX Sega http://www.sega.com"*/, + NULL + }, + { + "gen", 0, 0, UCON64_GEN, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_GEN_SWITCH] + }, + { + "int", 0, 0, UCON64_INT, + NULL, "force ROM is in interleaved format (SMD)", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "int2", 0, 0, UCON64_INT2, + NULL, "force ROM is in interleaved format 2 (MGD)", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "nint", 0, 0, UCON64_NINT, + NULL, "force ROM is not in interleaved format (BIN/RAW)", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "n", 1, 0, UCON64_N, + "NEW_NAME", "change foreign ROM name to NEW_NAME", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "n2", 1, 0, UCON64_N2, + "NEW_NAME", "change Japanese ROM name to NEW_NAME", + &ucon64_wf[WF_OBJ_GEN_DEFAULT] + }, + { + "smd", 0, 0, UCON64_SMD, + NULL, "convert to Super Magic Drive/SMD", + &ucon64_wf[WF_OBJ_ALL_DEFAULT_NO_SPLIT] + }, + { + "smds", 0, 0, UCON64_SMDS, + NULL, "convert emulator (*.srm) SRAM to Super Magic Drive/SMD", + NULL + }, + { + "bin", 0, 0, UCON64_BIN, + NULL, "convert to Magicom/BIN/RAW", + &ucon64_wf[WF_OBJ_GEN_DEFAULT_NO_SPLIT] + }, + { + "mgd", 0, 0, UCON64_MGD, + NULL, "convert to Multi Game*/MGD2/MGH", + &ucon64_wf[WF_OBJ_ALL_DEFAULT_NO_SPLIT] + }, +#if 0 + { + "gf", 0, 0, UCON64_GF, + NULL, "convert Sega CD country code to Europe; ROM=$CD_IMAGE", + NULL + }, + { + "ga", 0, 0, UCON64_GA, + NULL, "convert Sega CD country code to U.S.A.; ROM=$CD_IMAGE", + NULL + }, + { + "gym", 0, 0, UCON64_GYM, + NULL, "convert GYM (Genecyst) sound to WAV; " OPTION_LONG_S "rom=GYMFILE", + NULL + }, + { + "cym", 0, 0, UCON64_CYM, + NULL, "convert CYM (Callus emulator) sound to WAV; " OPTION_LONG_S "rom=CYMFILE", + NULL + }, +#endif + { + "stp", 0, 0, UCON64_STP, + NULL, "convert SRAM from backup unit for use with an emulator\n" + OPTION_LONG_S "stp just strips the first 512 bytes", + NULL + }, + { + "j", 0, 0, UCON64_J, + NULL, "join split ROM", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE] + }, + { + "s", 0, 0, UCON64_S, + NULL, "split ROM; default part size is 8 Mb (4 Mb for SMD)", + &ucon64_wf[WF_OBJ_ALL_DEFAULT_NO_SPLIT] + }, + { + "ssize", 1, 0, UCON64_SSIZE, + "SIZE", "specify split part size in Mbit", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "f", 0, 0, UCON64_F, + NULL, "remove NTSC/PAL protection", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "chk", 0, 0, UCON64_CHK, + NULL, "fix ROM checksum", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "1991", 0, 0, UCON64_1991, + NULL, "fix old third party ROMs to work with consoles build after\n" + "October 1991 by inserting \"(C) SEGA\" and \"(C)SEGA\"", + &ucon64_wf[WF_OBJ_GEN_DEFAULT] + }, + { + "multi", 1, 0, UCON64_MULTI, + "SIZE", "make multi-game file for use with MD-PRO flash card, truncated\n" + "to SIZE Mbit; file with loader must be specified first, then\n" + "all the ROMs, multi-game file to create last", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_STOP] + }, + { + "region", 1, 0, UCON64_REGION, + "CODE", "enable region function; use with -multi\n" + "CODE=0 force NTSC/Japan for all games\n" + "CODE=1 force NTSC/U.S.A. for all games\n" + "CODE=2 force PAL for all games\n" + "CODE=x use whatever setting games expect", + &ucon64_wf[WF_OBJ_GEN_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + +const st_getopt2_t bin_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Magicom/BIN/RAW", + NULL + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + +typedef struct st_genesis_header +{ + char pad[256]; +} st_genesis_header_t; + +static st_genesis_header_t genesis_header; +static genesis_file_t type; +static int genesis_rom_size, genesis_has_ram, genesis_tv_standard, genesis_japanese; + + +genesis_file_t +genesis_get_file_type (void) +{ + return type; +} + + +int +genesis_smd (st_rominfo_t *rominfo) +{ + st_smd_header_t header; + char dest_name[FILENAME_MAX]; + unsigned char *rom_buffer = NULL; + + if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL) + return -1; + + memset (&header, 0, SMD_HEADER_LEN); + header.size = genesis_rom_size / 16384; + header.id0 = 3; + header.id1 = 0xaa; + header.id2 = 0xbb; + header.type = 6; + + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".smd"); + ucon64_file_handler (dest_name, NULL, 0); + + ucon64_fwrite (&header, 0, SMD_HEADER_LEN, dest_name, "wb"); + smd_interleave (rom_buffer, genesis_rom_size); + ucon64_fwrite (rom_buffer, SMD_HEADER_LEN, genesis_rom_size, dest_name, "ab"); + + free (rom_buffer); + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +genesis_smds (void) +{ + char dest_name[FILENAME_MAX]; + unsigned char buf[32768]; + st_smd_header_t header; + + memset (&header, 0, SMD_HEADER_LEN); + memset (&buf, 0, 32768); + ucon64_fread (buf, 0, ucon64.file_size, ucon64.rom); + + header.id1 = 0xaa; + header.id2 = 0xbb; + header.type = 7; // SRAM file + + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".sav"); + ucon64_file_handler (dest_name, NULL, 0); + + ucon64_fwrite (&header, 0, SMD_HEADER_LEN, dest_name, "wb"); + smd_interleave (buf, 32768); // SMD SRAM files are interleaved (Are they? - dbjh) + ucon64_fwrite (buf, SMD_HEADER_LEN, 32768, dest_name, "ab"); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +genesis_bin (st_rominfo_t *rominfo) +{ + char dest_name[FILENAME_MAX]; + unsigned char *rom_buffer = NULL; + + if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL) + return -1; + + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".bin"); + ucon64_file_handler (dest_name, NULL, 0); + + ucon64_fwrite (rom_buffer, 0, genesis_rom_size, dest_name, "wb"); + + free (rom_buffer); + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +// see src/backup/mgd.h for the file naming scheme +int +genesis_mgd (st_rominfo_t *rominfo) +{ +#define CC (const char) + unsigned char *rom_buffer = NULL; + char dest_name[FILENAME_MAX]; +#if 0 // TODO: We need more info about the Multi Game Hunter + int x, y; + char mgh[512], buf[FILENAME_MAX]; + const char mghcharset[1024] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* ! */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* & */ 0x3c, 0x66, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x02, + /* ' */ 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + /* ( */ 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0c, 0x00, + /* ) */ 0x30, 0x18, 0x18, 0x18, 0x18, 0x18, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* , */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x08, + /* - */ 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + /* . */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, + /* / */ 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x60, 0x00, + /* 0 */ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, + /* 1 */ 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, + /* 2 */ 0x3c, 0x66, 0x06, 0x06, 0x7c, 0x60, 0x7e, 0x00, + /* 3 */ 0x3c, 0x66, 0x06, 0x1c, 0x06, 0x66, 0x3c, 0x00, + /* 4 */ 0x18, 0x38, 0x58, 0x7c, 0x18, 0x18, 0x3c, 0x00, + /* 5 */ 0x7e, 0x60, 0x7c, 0x06, 0x06, 0x66, 0x3c, 0x00, + /* 6 */ 0x3c, 0x66, 0x60, 0x7c, 0x66, 0x66, 0x3c, 0x00, + /* 7 */ 0x7e, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x18, 0x00, + /* 8 */ 0x3c, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x3c, 0x00, + /* 9 */ 0x3c, 0x66, 0x66, 0x3e, 0x06, 0x66, 0x3c, 0x00, + /* : */ 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00, + /* ; */ 0x00, 0x00, 0x18, 0x00, 0x18, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* = */ 0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* A */ 0x18, 0x3c, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x00, + /* B */ 0x7c, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x7c, 0x00, + /* C */ 0x3c, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3c, 0x00, + /* D */ 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, + /* E */ 0x7e, 0x60, 0x60, 0x7e, 0x60, 0x60, 0x7e, 0x00, + /* F */ 0x7e, 0x60, 0x60, 0x7e, 0x60, 0x60, 0x60, 0x00, + /* G */ 0x3e, 0x60, 0x60, 0x6e, 0x66, 0x66, 0x3e, 0x00, + /* H */ 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00, + /* I */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, + /* J */ 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, + /* K */ 0x66, 0x6c, 0x78, 0x70, 0x78, 0x6c, 0x66, 0x00, + /* L */ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00, + /* M */ CC 0xc6, CC 0xee, CC 0xfe, CC 0xd6, CC 0xc6, CC 0xc6, CC 0xc6, 0x00, + /* N */ 0x66, 0x76, 0x7e, 0x7e, 0x6e, 0x66, 0x66, 0x00, + /* O */ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, + /* P */ 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x00, + /* Q */ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x6e, 0x3e, 0x00, + /* R */ 0x7c, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x00, + /* S */ 0x3c, 0x66, 0x60, 0x3c, 0x06, 0x66, 0x3c, 0x00, + /* T */ 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, + /* U */ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, + /* V */ 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x3c, 0x00, + /* W */ 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x2c, 0x00, + /* X */ 0x66, 0x66, 0x3c, 0x10, 0x3c, 0x66, 0x66, 0x00, + /* Y */ 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00, + /* Z */ 0x7e, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* _ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* a */ 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00, + /* b */ 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x00, + /* c */ 0x00, 0x00, 0x3c, 0x66, 0x60, 0x66, 0x3c, 0x00, + /* d */ 0x06, 0x06, 0x6e, 0x66, 0x66, 0x66, 0x3e, 0x00, + /* e */ 0x00, 0x00, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, + /* f */ 0x0c, 0x18, 0x18, 0x3c, 0x18, 0x18, 0x3c, 0x00, + /* g */ 0x00, 0x00, 0x3c, 0x66, 0x66, 0x3e, 0x06, 0x7c, + /* h */ 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x00, + /* i */ 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, + /* j */ 0x00, 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0x6c, 0x38, + /* k */ 0x60, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0x00, + /* l */ 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, + /* m */ 0x00, 0x00, 0x5c, 0x76, 0x56, 0x56, 0x56, 0x00, + /* n */ 0x00, 0x00, 0x6c, 0x7e, 0x66, 0x66, 0x66, 0x00, + /* o */ 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00, + /* p */ 0x00, 0x00, 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60, + /* q */ 0x00, 0x00, 0x3e, 0x66, 0x66, 0x3e, 0x06, 0x06, + /* r */ 0x00, 0x00, 0x18, 0x1a, 0x18, 0x18, 0x3c, 0x00, + /* s */ 0x00, 0x00, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x00, + /* t */ 0x00, 0x18, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, + /* u */ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3a, 0x00, + /* v */ 0x00, 0x00, 0x66, 0x66, 0x3c, 0x3c, 0x18, 0x00, + /* w */ 0x00, 0x00, 0x56, 0x56, 0x56, 0x56, 0x7e, 0x00, + /* x */ 0x00, 0x00, 0x66, 0x66, 0x18, 0x66, 0x66, 0x00, + /* y */ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x7c, + /* z */ 0x00, 0x00, 0x7e, 0x0c, 0x18, 0x30, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* | */ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; +#endif + + // pad the file to the next valid MGD2 size if it doesn't have a valid size + if (genesis_rom_size <= 1 * MBIT) + genesis_rom_size = 1 * MBIT; + else if (genesis_rom_size <= 2 * MBIT) + genesis_rom_size = 2 * MBIT; + else if (genesis_rom_size <= 4 * MBIT) + genesis_rom_size = 4 * MBIT; + else if (genesis_rom_size <= 8 * MBIT) + genesis_rom_size = 8 * MBIT; + else if (genesis_rom_size <= 16 * MBIT) + genesis_rom_size = 16 * MBIT; + else if (genesis_rom_size <= 20 * MBIT) + genesis_rom_size = 20 * MBIT; + else if (genesis_rom_size <= 24 * MBIT) + genesis_rom_size = 24 * MBIT; + else + genesis_rom_size = 32 * MBIT; + + if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL) + return -1; + + mgd_make_name (ucon64.rom, UCON64_GEN, genesis_rom_size, dest_name); + ucon64_file_handler (dest_name, NULL, OF_FORCE_BASENAME); + + mgd_interleave (&rom_buffer, genesis_rom_size); + ucon64_fwrite (rom_buffer, 0, genesis_rom_size, dest_name, "wb"); + + printf (ucon64_msg[WROTE], dest_name); + free (rom_buffer); + +#if 0 // TODO: We need more info about the Multi Game Hunter (and a new option) + // automatically create MGH name file + memset (mgh, 0, sizeof (mgh)); + mgh[0] = 'M'; + mgh[1] = 'G'; + mgh[2] = 'H'; + mgh[3] = 0x1a; + mgh[4] = 0x06; + mgh[5] = (char) 0xf0; + mgh[31] = (char) 0xff; + + // In addition to the above, uCON also does "memcpy (mgh + 16, "MGH By uCON/chp", 15);" + + memcpy (buf, rominfo->name, 15); // copy first 15 bytes (don't use strlen() or strcpy()) + for (x = 0; x < 15; x++) + { + for (y = 0; y < 4; y++) + mgh[((x + 2) * 16) + y + 4] = mghcharset[(((unsigned char *) buf)[x] * 8) + y]; + for (y = 4; y < 8; y++) + mgh[((x + 2) * 16) + y + 244] = mghcharset[(((unsigned char *) buf)[x] * 8) + y]; + } + + set_suffix (dest_name, ".mgh"); + ucon64_output_fname (dest_name, OF_FORCE_BASENAME); + /* + If a backup would be created it would overwrite the backup of the ROM. The + ROM backup is more important, so we don't write a backup of the MGH file. + */ + ucon64_fwrite (mgh, 0, sizeof (mgh), dest_name, "wb"); +#endif + mgd_write_index_file ((char *) basename2 (dest_name), 1); + + return 0; +} + + +int +genesis_s (st_rominfo_t *rominfo) +{ + st_smd_header_t smd_header; + char dest_name[FILENAME_MAX], *p; + int x, nparts, surplus, size = ucon64.file_size - rominfo->buheader_len, + part_size; + + if (UCON64_ISSET (ucon64.part_size)) + { + part_size = ucon64.part_size; + // Don't allow too small part sizes, see src/console/snes.c (snes_s()) + if (part_size < 4 * MBIT) + { + fprintf (stderr, + "ERROR: Split part size must be larger than or equal to 4 Mbit\n"); + return -1; + } + } + else + { + if (type == SMD) + part_size = 4 * MBIT; // SMD uses 4 Mb parts + else + part_size = 8 * MBIT; // MGD and Magicom ("BIN") use + } // 8 Mb parts + + if (size <= part_size) + { + printf ( + "NOTE: ROM size is smaller than or equal to %d Mbit -- won't be split\n", + part_size / MBIT); + return -1; + } + + nparts = size / part_size; + surplus = size % part_size; + + if (type == SMD) + { + ucon64_fread (&smd_header, 0, SMD_HEADER_LEN, ucon64.rom); + + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".1"); + ucon64_output_fname (dest_name, 0); + p = strrchr (dest_name, '.') + 1; + + smd_header.size = part_size / 16384; + smd_header.id0 = 3; + // if smd_header.split bit 6 == 0 -> last file of the ROM + smd_header.split |= 0x40; + for (x = 0; x < nparts; x++) + { + if (surplus == 0 && x == nparts - 1) + smd_header.split &= ~0x40; // last file -> clear bit 6 + + // don't write backups of parts, because one name is used + ucon64_fwrite (&smd_header, 0, SMD_HEADER_LEN, dest_name, "wb"); + fcopy (ucon64.rom, x * part_size + rominfo->buheader_len, part_size, dest_name, "ab"); + printf (ucon64_msg[WROTE], dest_name); + + (*p)++; + } + + if (surplus != 0) + { + smd_header.size = surplus / 16384; + smd_header.split &= ~0x40; // last file -> clear bit 6 + + // don't write backups of parts, because one name is used + ucon64_fwrite (&smd_header, 0, SMD_HEADER_LEN, dest_name, "wb"); + fcopy (ucon64.rom, x * part_size + rominfo->buheader_len, surplus, dest_name, "ab"); + printf (ucon64_msg[WROTE], dest_name); + } + } + else if (type == MGD_GEN) + { + char suffix[5], *p_index, name[9], *names[16], names_mem[16][9]; + int n, offset, size, name_i = 0; + + for (n = 0; n < 16; n++) + names[n] = names_mem[n]; + + mgd_make_name (ucon64.rom, UCON64_GEN, genesis_rom_size, dest_name); + strcpy (suffix, (char *) get_suffix (dest_name)); + if ((p = strchr (dest_name, '.'))) + *p = 0; + n = strlen (dest_name); + if (n > 7) + n = 7; + dest_name[n] = 'A'; + dest_name[n + 1] = 0; + + strcpy (name, dest_name); + p_index = &name[n]; + + strcat (dest_name, suffix); + ucon64_output_fname (dest_name, OF_FORCE_BASENAME); + p = strrchr (dest_name, '.') - 1; + + if (surplus) + nparts++; + for (x = 0; x < nparts; x++) + { + offset = x * (part_size / 2); + size = part_size / 2; + if (offset + size > ucon64.file_size / 2) + size = ucon64.file_size / 2 - offset; + // don't write backups of parts, because one name is used + // write first half of file + fcopy (ucon64.rom, offset, size, dest_name, "wb"); + // write second half of file; don't do: "(nparts / 2) * part_size"! + fcopy (ucon64.rom, ucon64.file_size / 2 + offset, size, dest_name, "ab"); + printf (ucon64_msg[WROTE], dest_name); + + (*p)++; + + strcpy (names[name_i], name); + name_i++; + (*p_index)++; + } + mgd_write_index_file (names, name_i); + } + else // type == BIN + { + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".r01"); + ucon64_output_fname (dest_name, 0); + p = strrchr (dest_name, '.') + 3; + + for (x = 0; x < nparts; x++) + { + if (surplus == 0 && x == nparts - 1) + *p = '0'; // last file should have suffix ".r00" + fcopy (ucon64.rom, x * part_size + rominfo->buheader_len, part_size, dest_name, "wb"); + printf (ucon64_msg[WROTE], dest_name); + (*p)++; + } + + if (surplus != 0) + { + *p = '0'; + fcopy (ucon64.rom, x * part_size + rominfo->buheader_len, surplus, dest_name, "wb"); + printf (ucon64_msg[WROTE], dest_name); + } + } + + return 0; +} + + +int +genesis_j (st_rominfo_t *rominfo) +{ + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX], *p; + int block_size, total_size = 0; + + if (type == SMD) + { + unsigned char buf[3]; + + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".smd"); + ucon64_file_handler (dest_name, NULL, 0); + + strcpy (src_name, ucon64.rom); + p = strrchr (src_name, '.') + 1; + fcopy (src_name, 0, rominfo->buheader_len, dest_name, "wb"); + block_size = ucon64.file_size - rominfo->buheader_len; + while (fcopy (src_name, rominfo->buheader_len, block_size, dest_name, "ab") != -1) + { + printf ("Joined: %s\n", src_name); + total_size += block_size; + (*p)++; + block_size = fsizeof (src_name) - rominfo->buheader_len; + } + + if (rominfo->buheader_len) + { // fix header + buf[0] = total_size / 16384; // # 16 kB blocks + buf[1] = 3; // ID 0 + buf[2] = 0; // last file -> clear bit 6 + ucon64_fwrite (buf, 0, 3, dest_name, "r+b"); + buf[0] = 0xaa; // ID 1 + buf[1] = 0xbb; // ID 2 + buf[2] = 6; // type Genesis + ucon64_fwrite (buf, 8, 3, dest_name, "r+b"); + } + + printf (ucon64_msg[WROTE], dest_name); + } + else if (type == MGD_GEN) + { + /* + file1 file2 file3 file4 + 1/2 3/4 5/6 7/8 (1st half/2nd half) + joined file + 1/3/5/7/2/4/6/8 + */ + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".tmp"); // users should use -mgd to get + ucon64_file_handler (dest_name, NULL, 0); // a correct name + remove (dest_name); + + strcpy (src_name, ucon64.rom); + p = strrchr (src_name, '.') - 1; + block_size = (ucon64.file_size - rominfo->buheader_len) / 2; + while (fcopy (src_name, rominfo->buheader_len, block_size, dest_name, "ab") != -1) + { + (*p)++; + // BUG ALERT: Assume all parts have the same header length + block_size = (fsizeof (src_name) - rominfo->buheader_len) / 2; + } + + strcpy (src_name, ucon64.rom); + p = strrchr (src_name, '.') - 1; + block_size = (ucon64.file_size - rominfo->buheader_len) / 2; + while (fcopy (src_name, rominfo->buheader_len + block_size, + block_size, dest_name, "ab") != -1) + { + printf ("Joined: %s\n", src_name); // print this here, not in the + (*p)++; // previous loop + // BUG ALERT: Assume all parts have the same header length + block_size = (fsizeof (src_name) - rominfo->buheader_len) / 2; + } + + printf (ucon64_msg[WROTE], dest_name); + } + else if (type == BIN) + { + int tried_r00 = 0; + + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".bin"); + ucon64_file_handler (dest_name, NULL, 0); + remove (dest_name); + + strcpy (src_name, ucon64.rom); + p = strrchr (src_name, '.') + 3; + *(p - 1) = '0'; // be user friendly and avoid confusion + *p = '1'; // (.r01 is first file, not .r00) + block_size = ucon64.file_size - rominfo->buheader_len; + while (fcopy (src_name, rominfo->buheader_len, block_size, dest_name, "ab") != -1) + { + printf ("Joined: %s\n", src_name); + if (tried_r00) + break; // quit after joining last file + (*p)++; + if (!tried_r00 && access (src_name, F_OK) != 0) + { // file does not exist -> try .r00 + *p = '0'; + tried_r00 = 1; + } + block_size = fsizeof (src_name) - rominfo->buheader_len; + } + + printf (ucon64_msg[WROTE], dest_name); + } + + return 0; +} + + +static int +genesis_name (st_rominfo_t *rominfo, const char *name1, const char *name2) +{ + unsigned char *rom_buffer = NULL; + char buf[FILENAME_MAX]; + + if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL) + return -1; + + if (name1) + { + memset (buf, ' ', GENESIS_NAME_LEN); + strncpy (buf, name1, strlen (name1) > GENESIS_NAME_LEN ? + GENESIS_NAME_LEN : strlen (name1)); + memcpy (&rom_buffer[GENESIS_HEADER_START + 32 + GENESIS_NAME_LEN], buf, + GENESIS_NAME_LEN); + } + if (name2) + { + memset (buf, ' ', GENESIS_NAME_LEN); + strncpy (buf, name2, strlen (name2) > GENESIS_NAME_LEN ? + GENESIS_NAME_LEN : strlen (name2)); + memcpy (&rom_buffer[GENESIS_HEADER_START + 32], buf, GENESIS_NAME_LEN); + } + + strcpy (buf, ucon64.rom); + ucon64_file_handler (buf, NULL, 0); + save_rom (rominfo, buf, &rom_buffer, genesis_rom_size); + + free (rom_buffer); + printf (ucon64_msg[WROTE], buf); + return 0; +} + + +int +genesis_n (st_rominfo_t *rominfo, const char *name) +{ + return genesis_name (rominfo, name, NULL); +} + + +int +genesis_n2 (st_rominfo_t *rominfo, const char *name) +{ + return genesis_name (rominfo, NULL, name); +} + + +int +genesis_1991 (st_rominfo_t *rominfo) +{ + return genesis_name (rominfo, "(C)SEGA", "(C) SEGA"); +} + + +int +genesis_chk (st_rominfo_t *rominfo) +{ + unsigned char *rom_buffer = NULL; + char dest_name[FILENAME_MAX]; + + if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL) + return -1; + + rom_buffer[GENESIS_HEADER_START + 143] = rominfo->current_internal_crc; // low byte of checksum + rom_buffer[GENESIS_HEADER_START + 142] = rominfo->current_internal_crc >> 8; // high byte of checksum + + dumper (stdout, &rom_buffer[GENESIS_HEADER_START + 0x8e], 2, GENESIS_HEADER_START + 0x8e, DUMPER_HEX); + + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + save_rom (rominfo, dest_name, &rom_buffer, genesis_rom_size); + + free (rom_buffer); + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +static int +genesis_fix_pal_protection (st_rominfo_t *rominfo) +/* + This function searches for PAL protection codes. If it finds one it will + fix the code so that the game will run on a Genesis (NTSC). +*/ +{ + char fname[FILENAME_MAX]; + unsigned char *rom_buffer = NULL; + int offset = 0, block_size, n = 0, n_extra_patterns, n2; + st_cm_pattern_t *patterns = NULL; + + strcpy (fname, "genpal.txt"); + // First try the current directory, then the configuration directory + if (access (fname, F_OK | R_OK) == -1) + sprintf (fname, "%s" FILE_SEPARATOR_S "genpal.txt", ucon64.configdir); + n_extra_patterns = build_cm_patterns (&patterns, fname, ucon64.quiet == -1 ? 1 : 0); + if (n_extra_patterns >= 0) + printf ("Found %d additional code%s in %s\n", + n_extra_patterns, n_extra_patterns != 1 ? "s" : "", fname); + + puts ("Attempting to fix PAL protection code..."); + + if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL) + return -1; + + while ((n2 = ucon64.file_size - offset) > 0) + { + block_size = n2 >= 16 * 1024 ? 16 * 1024 : n2; + for (n2 = 0; n2 < n_extra_patterns; n2++) + n += change_mem2 ((char *) rom_buffer + offset, block_size, + patterns[n2].search, + patterns[n2].search_size, + patterns[n2].wildcard, + patterns[n2].escape, + patterns[n2].replace, + patterns[n2].replace_size, + patterns[n2].offset, + patterns[n2].sets); + offset += 16 * 1024; + } + cleanup_cm_patterns (&patterns, n_extra_patterns); + + strcpy (fname, ucon64.rom); + ucon64_file_handler (fname, NULL, 0); + save_rom (rominfo, fname, &rom_buffer, genesis_rom_size); + + free (rom_buffer); + printf ("Found %d pattern%s\n", n, n != 1 ? "s" : ""); + printf (ucon64_msg[WROTE], fname); + return n; +} + + +static int +genesis_fix_ntsc_protection (st_rominfo_t *rominfo) +/* + This function searches for NTSC protection codes. If it finds one it will + fix the code so that the game will run on a Mega Drive (PAL). +*/ +{ + char fname[FILENAME_MAX]; + unsigned char *rom_buffer = NULL; + int offset = 0, block_size, n = 0, n_extra_patterns, n2; + st_cm_pattern_t *patterns = NULL; + + strcpy (fname, "mdntsc.txt"); + // First try the current directory, then the configuration directory + if (access (fname, F_OK | R_OK) == -1) + sprintf (fname, "%s" FILE_SEPARATOR_S "mdntsc.txt", ucon64.configdir); + n_extra_patterns = build_cm_patterns (&patterns, fname, ucon64.quiet == -1 ? 1 : 0); + if (n_extra_patterns >= 0) + printf ("Found %d additional code%s in %s\n", + n_extra_patterns, n_extra_patterns != 1 ? "s" : "", fname); + + puts ("Attempting to fix NTSC protection code..."); + + if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL) + return -1; + + while ((n2 = ucon64.file_size - offset) > 0) + { + block_size = n2 >= 16 * 1024 ? 16 * 1024 : n2; + for (n2 = 0; n2 < n_extra_patterns; n2++) + n += change_mem2 ((char *) rom_buffer + offset, block_size, + patterns[n2].search, + patterns[n2].search_size, + patterns[n2].wildcard, + patterns[n2].escape, + patterns[n2].replace, + patterns[n2].replace_size, + patterns[n2].offset, + patterns[n2].sets); + offset += 16 * 1024; + } + cleanup_cm_patterns (&patterns, n_extra_patterns); + + strcpy (fname, ucon64.rom); + ucon64_file_handler (fname, NULL, 0); + save_rom (rominfo, fname, &rom_buffer, genesis_rom_size); + + free (rom_buffer); + printf ("Found %d pattern%s\n", n, n != 1 ? "s" : ""); + printf (ucon64_msg[WROTE], fname); + return n; +} + + +int +genesis_f (st_rominfo_t *rominfo) +{ + /* + In the Philipines the television standard is NTSC, but do games made + for the Philipines exist? + Just like with SNES we don't guarantee anything for files that needn't be + fixed/cracked/patched. + */ + if (genesis_tv_standard == 0) // NTSC (Japan, U.S.A. or Brazil ('4')) + return genesis_fix_ntsc_protection (rominfo); + else + return genesis_fix_pal_protection (rominfo); +} + + +unsigned char * +load_rom (st_rominfo_t *rominfo, const char *name, unsigned char *rom_buffer) +{ + FILE *file; + int bytesread; + + if ((file = fopen (name, "rb")) == NULL) + return NULL; + if (!(rom_buffer = (unsigned char *) malloc (genesis_rom_size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], genesis_rom_size); + fclose (file); + return NULL; + } + fseek (file, rominfo->buheader_len, SEEK_SET); // don't do this only for SMD! + bytesread = fread (rom_buffer, 1, genesis_rom_size, file); + if (bytesread < genesis_rom_size) + memset (rom_buffer + bytesread, 0, genesis_rom_size - bytesread); + + if (type != BIN) + { + if (ucon64.fcrc32 == 0) + ucon64.fcrc32 = crc32 (ucon64.fcrc32, rom_buffer, genesis_rom_size); + + if (type == SMD) + smd_deinterleave (rom_buffer, bytesread); + else // type == MGD_GEN + mgd_deinterleave (&rom_buffer, bytesread, genesis_rom_size); + } + + if (ucon64.crc32 == 0) // calculate the CRC32 only once + ucon64.crc32 = crc32 (0, rom_buffer, bytesread); + + fclose (file); + return rom_buffer; +} + + +int +save_rom (st_rominfo_t *rominfo, const char *name, unsigned char **buffer, int size) +{ + if (type == SMD) + { + int n; + + /* + Copy the complete backup unit header length, no matter how strange or + how large the size. fcopy() doesn't copy if source and destination are + one and the same. Copying is necessary if source and destination are + different so that we can use "r+b" when writing the contents of buffer + to disk. + */ + fcopy (ucon64.rom, 0, rominfo->buheader_len, name, "wb"); + + smd_interleave (*buffer, size); + n = ucon64_fwrite (*buffer, rominfo->buheader_len, size, name, "r+b"); + if (size < (int) (ucon64.file_size - rominfo->buheader_len)) + truncate2 (name, rominfo->buheader_len + size); + return n; + } + else if (type == MGD_GEN) + { + mgd_interleave (buffer, size); // allocates new buffer + return ucon64_fwrite (*buffer, 0, size, name, "wb"); + } + else // type == BIN + return ucon64_fwrite (*buffer, 0, size, name, "wb"); +} + + +static void +write_game_table_entry (FILE *destfile, int file_no, st_rominfo_t *rominfo, + int totalsize) +{ + static int sram_page = 0, file_no_sram = 0; + int n; + unsigned char name[0x1c], flags = 0; // SRAM/region flags: F, D (reserved), E, P, V, T, S1, S0 + + fseek (destfile, 0x8000 + (file_no - 1) * 0x20, SEEK_SET); + fputc (0xff, destfile); // 0x0 = 0xff + memcpy (name, rominfo->name, 0x1c); + for (n = 0; n < 0x1c; n++) + { + if (!isprint ((int) name[n])) + name[n] = '.'; + else + name[n] = toupper (name[n]); // according to Leo, MDPACKU4.BIN + } // only supports upper case characters + fwrite (name, 1, 0x1c, destfile); // 0x1 - 0x1c = name + fputc (0, destfile); // 0x1d = 0 + fputc (totalsize / (2 * MBIT), destfile); // 0x1e = bank code + + flags = 0x80; // set F (?, default) + if (genesis_has_ram) + { + if (sram_page == 3) + file_no_sram = file_no; + else if (sram_page > 3) + { + printf ("WARNING: This ROM will share SRAM with ROM %d\n", file_no_sram); + sram_page = 3; + } + flags |= sram_page++; + if ((ucon64.file_size - rominfo->buheader_len) <= 16 * MBIT) + flags &= ~0x80; // clear F (<=16 Mb & SRAM) + } + else + flags |= 4; // set T (no SRAM) + + /* + J(apan) would probably be a more logical name for bit E(urope). I (dbjh) + base that on information of SamIAm. According to him there are three types + of the console: + American Genesis NTSC/no Japanese switch + Japanese Genesis NTSC/Japanese switch + Mega Drive (rest of the world?) PAL/no Japanese switch + So, besides PAL and NTSC there is the variable whether the console contains + a Japanese "switch". The region function is used to make the game "think" + it's running on another type of console. For example, a Japanese game + running on a (European) Mega Drive should have the P and E bit set to 0. + */ + if (UCON64_ISSET (ucon64.region)) + { + if (!genesis_japanese) + flags |= 0x20; // set E(urope) + if (genesis_tv_standard == 1) + flags |= 0x10; // set P(AL) + + flags |= 8; // set V (enable region function) + } + + fputc (flags, destfile); // 0x1f = flags +} + + +int +genesis_multi (int truncate_size, char *fname) +{ +#define BUFSIZE (32 * 1024) // must be a multiple of 16 kB + int n, n_files, file_no, bytestowrite, byteswritten, totalsize = 0, done, + truncated = 0, paddedsize, org_do_not_calc_crc = ucon64.do_not_calc_crc; + struct stat fstate; + FILE *srcfile, *destfile; + char destname[FILENAME_MAX]; + unsigned char buffer[BUFSIZE]; + + if (truncate_size == 0) + { + fprintf (stderr, "ERROR: Can't make multi-game file of 0 bytes\n"); + return -1; + } + + if (fname != NULL) + { + strcpy(destname, fname); + n_files = ucon64.argc; + } + else + { + strcpy(destname, ucon64.argv[ucon64.argc - 1]); + n_files = ucon64.argc - 1; + } + + ucon64_file_handler (destname, NULL, OF_FORCE_BASENAME); + if ((destfile = fopen (destname, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], destname); + return -1; + } + + // do this check here, because one error message is enough (not for every game) + if (UCON64_ISSET (ucon64.region)) + switch (ucon64.region) + { + case 0: // NTSC/Japan + case 1: // NTSC/U.S.A. + case 2: // PAL + case 256: + break; + default: + printf ("WARNING: Invalid region code specified, using values games expect\n"); + } + + printf ("Creating multi-game file for MD-PRO: %s\n", destname); + + file_no = 0; + for (n = 1; n < n_files; n++) + { + if (access (ucon64.argv[n], F_OK)) + continue; // "file" does not exist (option) + stat (ucon64.argv[n], &fstate); + if (!S_ISREG (fstate.st_mode)) + continue; + if (file_no == 32) // loader + 31 games + { + printf ("WARNING: A multi-game file can contain a maximum of 31 games. The other files\n" + " are ignored.\n"); + break; + } + + ucon64.console = UCON64_UNKNOWN; + ucon64.rom = ucon64.argv[n]; + ucon64.file_size = fsizeof (ucon64.rom); + // DON'T use fstate.st_size, because file could be compressed + ucon64.rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? + ucon64.buheader_len : 0; + ucon64.rominfo->interleaved = UCON64_ISSET (ucon64.interleaved) ? + ucon64.interleaved : 0; + ucon64.do_not_calc_crc = 1; + if (genesis_init (ucon64.rominfo) != 0) + printf ("WARNING: %s does not appear to be a Genesis ROM\n", ucon64.rom); + /* + NOTE: This is NOT the place to mess with ucon64.console. When this + function is entered ucon64.console must have been UCON64_GEN. We + modify ucon64.console temporarily only to be able to help detect + problems with incorrect files. + */ + if (UCON64_ISSET (ucon64.region)) + switch (ucon64.region) + { + case 0: // NTSC/Japan + genesis_tv_standard = 0; + genesis_japanese = 1; + break; + case 1: // NTSC/U.S.A. + genesis_tv_standard = 0; + genesis_japanese = 0; + break; + case 2: // PAL + genesis_tv_standard = 1; + genesis_japanese = 0; + break; + case 256: + // Do nothing. Use whatever values we found for genesis_tv_standard and + // genesis_japanese. + break; + // no default case, because we already checked value of ucon64.region + } + + if ((srcfile = fopen (ucon64.rom, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], ucon64.rom); + continue; + } + if (ucon64.rominfo->buheader_len) + fseek (srcfile, ucon64.rominfo->buheader_len, SEEK_SET); + + if (file_no == 0) + { + printf ("Loader: %s\n", ucon64.rom); + if (ucon64.file_size - ucon64.rominfo->buheader_len != MBIT) + printf ("WARNING: Are you sure %s is a loader binary?\n", ucon64.rom); + } + else + { + printf ("ROM%d: %s\n", file_no, ucon64.rom); + write_game_table_entry (destfile, file_no, ucon64.rominfo, totalsize); + fseek (destfile, totalsize, SEEK_SET); // restore file pointer + } + file_no++; + + done = 0; + byteswritten = 0; // # of bytes written per file + while (!done) + { + if (ucon64.rominfo->interleaved == 2) + bytestowrite = fread_mgd (buffer, 1, BUFSIZE, srcfile); + else + { + bytestowrite = fread (buffer, 1, BUFSIZE, srcfile); + if (ucon64.rominfo->interleaved) + smd_deinterleave (buffer, BUFSIZE); + // yes, BUFSIZE. bytestowrite might not be n * 16 kB + } + if (totalsize + bytestowrite > truncate_size) + { + bytestowrite = truncate_size - totalsize; + done = 1; + truncated = 1; + printf ("Output file is %d Mbit, truncating %s, skipping %d bytes\n", + truncate_size / MBIT, ucon64.rom, ucon64.file_size - + ucon64.rominfo->buheader_len - (byteswritten + bytestowrite)); + } + totalsize += bytestowrite; + if (bytestowrite == 0) + done = 1; + fwrite (buffer, 1, bytestowrite, destfile); + byteswritten += bytestowrite; + } + fclose (srcfile); + if (truncated) + break; + + // games have to be aligned to (start at) a 2 Mbit boundary + paddedsize = (totalsize + 2 * MBIT - 1) & ~(2 * MBIT - 1); +// printf ("paddedsize: %d (%f); totalsize: %d (%f)\n", +// paddedsize, paddedsize / (1.0 * MBIT), totalsize, totalsize / (1.0 * MBIT)); + if (paddedsize > totalsize) + { + // I (dbjh) don't think it is really necessary to pad to the truncate + // size, but it won't hurt + if (paddedsize > truncate_size) + { + truncated = 1; // not *really* truncated + paddedsize = truncate_size; + } + + memset (buffer, 0, BUFSIZE); + while (totalsize < paddedsize) + { + bytestowrite = paddedsize - totalsize > BUFSIZE ? + BUFSIZE : paddedsize - totalsize; + fwrite (buffer, 1, bytestowrite, destfile); + totalsize += bytestowrite; + } + } + if (truncated) + break; + } + // fill the next game table entry + fseek (destfile, 0x8000 + (file_no - 1) * 0x20, SEEK_SET); + fputc (0, destfile); // indicate no next game + fclose (destfile); + ucon64.console = UCON64_GEN; + ucon64.do_not_calc_crc = org_do_not_calc_crc; + + return 0; +} + + +static int +genesis_testinterleaved (st_rominfo_t *rominfo) +{ + unsigned char buf[16384] = { 0 }; + + ucon64_fread (buf, rominfo->buheader_len, 8192 + (GENESIS_HEADER_START + 4) / 2, ucon64.rom); + if (!memcmp (buf + GENESIS_HEADER_START, "SEGA", 4)) + return 0; + + smd_deinterleave (buf, 16384); + if (!memcmp (buf + GENESIS_HEADER_START, "SEGA", 4)) + return 1; + + q_fread_mgd (buf, rominfo->buheader_len + GENESIS_HEADER_START, 4, ucon64.rom); + if (!memcmp (buf, "SEGA", 4)) + return 2; + + return 0; // unknown, act as if it's BIN +} + + +int +genesis_init (st_rominfo_t *rominfo) +{ + int result = -1, value = 0, x, y; + unsigned char *rom_buffer = NULL, buf[MAXBUFSIZE], name[GENESIS_NAME_LEN + 1], + smd_header_split; + static char maker[9], country[200]; // 200 characters should be enough for 5 country names + static const char *genesis_maker[0x100] = + { + NULL, "Accolade/Infogrames", "Virgin Games", "Parker Brothers", "Westone", + NULL, NULL, NULL, NULL, "Westone", + "Takara", "Taito/Accolade", "Capcom", "Data East", "Namco/Tengen", + "Sunsoft", "Bandai", "Dempa", "Technosoft", "Technosoft", + "Asmik", NULL, "Extreme/Micronet", "Vic Tokai", "American Sammy", + "NCS", "Sigma Enterprises", "Toho", NULL, "Kyugo", + NULL, NULL, "Wolfteam", "Kaneko", NULL, + "Toaplan", "Tecmo", NULL, NULL, NULL, + "Toaplan", "Unipac", "UFL Company Ltd.", "Human", NULL, + "Game Arts", "Hot-B", "Sage's Creation", "Tengen/Time Warner", + "Renovation/Telenet", + "Electronic Arts", NULL, NULL, NULL, NULL, + "Psygnosis", "Razorsoft", NULL, "Mentrix", NULL, + "JVC/Victor Musical Industries", NULL, NULL, NULL, "IGS Corp.", + NULL, NULL, "CRI/Home Data", "Arena", "Virgin Games", + NULL, "Nichibutsu", NULL, "Soft Vision", "Palsoft", + NULL, "KOEI", NULL, NULL, "U.S. Gold", + NULL, "Acclaim/Flying Edge", NULL, "Gametek", NULL, + NULL, "Absolute", "Mindscape", "Domark", "Parker Brothers", + NULL, NULL, NULL, "Sony Imagesoft", "Sony Imagesoft", + "Konami", NULL, "Tradewest/Williams", NULL, "Codemasters", + "T*HQ Software", "TecMagik", NULL, "Takara", NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, "Hi Tech Entertainment/Designer Software", "Psygnosis", NULL, + NULL, NULL, NULL, NULL, "Accolade", + "Code Masters", NULL, NULL, NULL, "Spectrum HoloByte", + "Interplay", NULL, NULL, NULL, NULL, + "Activision", NULL, "Shiny & Playmates", NULL, NULL, + NULL, NULL, NULL, NULL, "Viacom International", + NULL, NULL, NULL, NULL, "Atlus", + NULL, NULL, NULL, NULL, NULL, + NULL, "Infogrames", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, "Fox Interactive", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, "Psygnosis", + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, "Disney Interactive", + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL + }, +#define GENESIS_COUNTRY_MAX 0x57 + *genesis_country[GENESIS_COUNTRY_MAX] = + { + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, "Brazil", NULL, NULL, // Brazil NTSC + NULL, "Hong Kong", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + "Asia", "Brazil", NULL, NULL, "Europe", // Brazil PAL + "France", NULL, NULL, NULL, "Japan", + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + "U.S.A.", NULL + }, +#define GENESIS_IO_MAX 0x58 + *genesis_io[GENESIS_IO_MAX] = + { + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "Joystick for MS", NULL, + NULL, NULL, "Team Play", NULL, "6 Button Pad", + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, "Control Ball", "CD-ROM", NULL, NULL, + "Floppy Disk Drive", NULL, NULL, NULL, "3 Button Pad", + "Keyboard", "Activator", "Mega Mouse", NULL, NULL, + "Printer", NULL, "Serial RS232C", NULL, "Tablet", + NULL, "Paddle Controller", NULL + }; + + ucon64_fread (buf, 0, 11, ucon64.rom); + smd_header_split = buf[2]; + if (buf[8] == 0xaa && buf[9] == 0xbb && buf[10] == 7) + { + rominfo->buheader_len = SMD_HEADER_LEN; + strcpy (rominfo->name, "Name: N/A"); + rominfo->console_usage = NULL; + rominfo->copier_usage = smd_usage[0].help; + rominfo->maker = "Publisher: You?"; + rominfo->country = "Country: Your country?"; + rominfo->has_internal_crc = 0; + strcat (rominfo->misc, "Type: Super Magic Drive SRAM file\n"); + ucon64.split = 0; // SRAM files are never split + type = SMD; + return 0; // rest is nonsense for SRAM file + } + + if (buf[8] == 0xaa && buf[9] == 0xbb && buf[10] == 6) + { + type = SMD; + rominfo->buheader_len = SMD_HEADER_LEN; + } + + if (UCON64_ISSET (ucon64.buheader_len)) // -hd, -nhd or -hdn option was specified + rominfo->buheader_len = ucon64.buheader_len; + + rominfo->interleaved = UCON64_ISSET (ucon64.interleaved) ? + ucon64.interleaved : genesis_testinterleaved (rominfo); + + if (rominfo->interleaved == 0) + type = BIN; + else if (rominfo->interleaved == 1) + type = SMD; + else if (rominfo->interleaved == 2) + type = MGD_GEN; + + if (type == SMD) + { + genesis_rom_size = ((ucon64.file_size - rominfo->buheader_len) / 16384) * 16384; + if (genesis_rom_size != ucon64.file_size - rominfo->buheader_len) + rominfo->data_size = genesis_rom_size; + + memset (buf, 0, 16384); + ucon64_fread (buf, rominfo->buheader_len, + 8192 + (GENESIS_HEADER_START + GENESIS_HEADER_LEN) / 2, ucon64.rom); + smd_deinterleave (buf, 16384); // buf will contain the deinterleaved data + memcpy (&genesis_header, buf + GENESIS_HEADER_START, GENESIS_HEADER_LEN); + } + else if (type == MGD_GEN) + { + // We use rominfo->buheader_len to make it user definable. Normally it + // should be 0 for MGD_GEN. + genesis_rom_size = ucon64.file_size - rominfo->buheader_len; + q_fread_mgd (&genesis_header, rominfo->buheader_len + GENESIS_HEADER_START, + GENESIS_HEADER_LEN, ucon64.rom); + } + else // type == BIN + { + // We use rominfo->buheader_len to make it user definable. + genesis_rom_size = ucon64.file_size - rominfo->buheader_len; + ucon64_fread (&genesis_header, rominfo->buheader_len + GENESIS_HEADER_START, + GENESIS_HEADER_LEN, ucon64.rom); + } + + if (!UCON64_ISSET (ucon64.split)) + { + if (type == SMD) + { + // This code does not work for the last part of a file + int split = 0; + if (smd_header_split & 0x40) + split = ucon64_testsplit (ucon64.rom); + ucon64.split = split; // force displayed info to be correct + } // if not split (see ucon64.c) + else if (type == BIN) + { + // This code isn't fool-proof, but it's not that important + const char *ptr = get_suffix (ucon64.rom); + if (strlen (ptr) == 4 && ptr[1] == 'r' && + ptr[2] >= '0' && ptr[2] <= '9' && ptr[3] >= '0' && ptr[3] <= '9') + { + int n_parts = 0; + char fname[FILENAME_MAX], *digits; + + strcpy (fname, ucon64.rom); + digits = fname + strlen (fname) - 2; + do + { + sprintf (digits, "%02d", n_parts); + if (access (fname, F_OK) == 0) + n_parts++; + else + break; + } + while (n_parts < 100); + + if (n_parts) + ucon64.split = n_parts; + } + } + } + + if (!memcmp (&OFFSET (genesis_header, 0), "SEGA", 4) || + ucon64.console == UCON64_GEN) + result = 0; + else + result = -1; + + rominfo->header_start = GENESIS_HEADER_START; + rominfo->header_len = GENESIS_HEADER_LEN; + rominfo->header = &genesis_header; + + // internal ROM name + memcpy (rominfo->name, &OFFSET (genesis_header, 80), GENESIS_NAME_LEN); + rominfo->name[GENESIS_NAME_LEN] = 0; + + // ROM maker + memcpy (maker, &OFFSET (genesis_header, 16), 8); + if (maker[3] == 'T' && maker[4] == '-') + { + sscanf (&maker[5], "%03d", &value); + rominfo->maker = NULL_TO_UNKNOWN_S (genesis_maker[value & 0xff]); + } + else + { + // Don't use genesis_maker here. If it would be corrected/updated an + // incorrect publisher name would be displayed. + rominfo->maker = + (!strncmp (maker, "(C)ACLD", 7)) ? "Ballistic" : + (!strncmp (maker, "(C)AESI", 7)) ? "ASCII" : + (!strncmp (maker, "(C)ASCI", 7)) ? "ASCII" : + (!strncmp (maker, "(C)KANEKO", 9)) ? "Kaneko" : + (!strncmp (maker, "(C)PPP", 6)) ? "Gametek" : + (!strncmp (maker, "(C)RSI", 6)) ? "Razorsoft" : // or is it "(C)1RSI"? + (!strncmp (maker, "(C)SEGA", 7)) ? "Sega" : + (!strncmp (maker, "(C)TREC", 7)) ? "Treco" : + (!strncmp (maker, "(C)VRGN", 7)) ? "Virgin Games" : + (!strncmp (maker, "(C)WADN", 7)) ? "Parker Brothers" : + (!strncmp (maker, "(C)WSTN", 7)) ? "Westone" : NULL; + if (!rominfo->maker) + { + maker[8] = 0; + rominfo->maker = maker; + } + } + + genesis_tv_standard = 1; // default to PAL; NTSC has higher precedence + genesis_japanese = 0; + + country[0] = 0; + // ROM country + for (x = 0; x < 5; x++) + { + int country_code = OFFSET (genesis_header, 240 + x); + + if ((x > 0 && country_code == 0) || country_code == ' ') + continue; + if (country_code == 'J') + genesis_japanese = 1; + if (genesis_japanese || country_code == 'U' || country_code == '4') + genesis_tv_standard = 0; // Japan, the U.S.A. and Brazil ('4') use NTSC + strcat (country, NULL_TO_UNKNOWN_S + (genesis_country[MIN (country_code, GENESIS_COUNTRY_MAX - 1)])); + strcat (country, ", "); + } + x = strlen (country); + if (x >= 2 && country[x - 2] == ',' && country[x - 1] == ' ') + country[x - 2] = 0; + rominfo->country = country; + + // misc stuff + memcpy (name, &OFFSET (genesis_header, 32), GENESIS_NAME_LEN); + name[GENESIS_NAME_LEN] = 0; + sprintf ((char *) buf, "Japanese game name: %s\n", name); + strcat (rominfo->misc, (char *) buf); + + sprintf ((char *) buf, "Date: %.8s\n", &OFFSET (genesis_header, 24)); + strcat (rominfo->misc, (char *) buf); + + x = (OFFSET (genesis_header, 160) << 24) + + (OFFSET (genesis_header, 161) << 16) + + (OFFSET (genesis_header, 162) << 8) + + OFFSET (genesis_header, 163); + y = (OFFSET (genesis_header, 164) << 24) + + (OFFSET (genesis_header, 165) << 16) + + (OFFSET (genesis_header, 166) << 8) + + OFFSET (genesis_header, 167); + sprintf ((char *) buf, "Internal size: %.4f Mb\n", (float) (y - x + 1) / MBIT); + strcat (rominfo->misc, (char *) buf); + + sprintf ((char *) buf, "ROM start: %08x\n", x); + strcat (rominfo->misc, (char *) buf); + + sprintf ((char *) buf, "ROM end: %08x\n", y); + strcat (rominfo->misc, (char *) buf); + + genesis_has_ram = OFFSET (genesis_header, 176) == 'R' && + OFFSET (genesis_header, 177) == 'A'; + if (genesis_has_ram) + { + x = (OFFSET (genesis_header, 180) << 24) + + (OFFSET (genesis_header, 181) << 16) + + (OFFSET (genesis_header, 182) << 8) + + OFFSET (genesis_header, 183); + y = (OFFSET (genesis_header, 184) << 24) + + (OFFSET (genesis_header, 185) << 16) + + (OFFSET (genesis_header, 186) << 8) + + OFFSET (genesis_header, 187); + sprintf ((char *) buf, "Cartridge RAM: Yes, %d kBytes (%s)\n", + (y - x + 1) >> 10, + OFFSET (genesis_header, 178) & 0x40 ? "backup" : "non-backup"); + strcat (rominfo->misc, (char *) buf); + + sprintf ((char *) buf, "RAM start: %08x\n", x); + strcat (rominfo->misc, (char *) buf); + + sprintf ((char *) buf, "RAM end: %08x\n", y); + strcat (rominfo->misc, (char *) buf); + } + else + strcat (rominfo->misc, "Cartridge RAM: No\n"); + + /* + Only checking for 'G' seems to give better results than checking for "GM". + "Officially" "GM" indicates it's a game and "Al" that it's educational. + */ + sprintf ((char *) buf, "Product type: %s\n", + (OFFSET (genesis_header, 128) == 'G') ? "Game" : "Educational"); + strcat (rominfo->misc, (char *) buf); + + sprintf ((char *) buf, "I/O device(s): %s", + NULL_TO_UNKNOWN_S (genesis_io[MIN ((int) OFFSET (genesis_header, 144), GENESIS_IO_MAX - 1)])); + for (x = 0; x < 3; x++) + { + const char *io_device = genesis_io[MIN (OFFSET (genesis_header, 145 + x), GENESIS_IO_MAX - 1)]; + if (!io_device) + continue; + strcat ((char *) buf, ", "); + strcat ((char *) buf, io_device); + } + strcat ((char *) buf, "\n"); + strcat (rominfo->misc, (char *) buf); + + sprintf ((char *) buf, "Modem data: %.10s\n", &OFFSET (genesis_header, 188)); + strcat (rominfo->misc, (char *) buf); + + sprintf ((char *) buf, "Memo: %.40s\n", &OFFSET (genesis_header, 200)); + strcat (rominfo->misc, (char *) buf); + + sprintf ((char *) buf, "Product code: %.8s\n", &OFFSET (genesis_header, 131)); + strcat (rominfo->misc, (char *) buf); + + sprintf ((char *) buf, "Version: 1.%c%c", OFFSET (genesis_header, 140), OFFSET (genesis_header, 141)); + strcat (rominfo->misc, (char *) buf); + + // internal ROM crc + if (!UCON64_ISSET (ucon64.do_not_calc_crc) && result == 0) + { + if ((rom_buffer = load_rom (rominfo, ucon64.rom, rom_buffer)) == NULL) + return -1; + + rominfo->has_internal_crc = 1; + rominfo->internal_crc_len = 2; + + rominfo->current_internal_crc = genesis_chksum (rom_buffer); + rominfo->internal_crc = OFFSET (genesis_header, 143); // low byte of checksum + rominfo->internal_crc += (OFFSET (genesis_header, 142)) << 8; // high byte of checksum + + rominfo->internal_crc2[0] = 0; + free (rom_buffer); + } + rominfo->console_usage = genesis_usage[0].help; + if (type == SMD) + rominfo->copier_usage = smd_usage[0].help; + else if (type == MGD_GEN) + rominfo->copier_usage = mgd_usage[0].help; + else // type == BIN + rominfo->copier_usage = bin_usage[0].help; + + return result; +} + + +int +genesis_chksum (unsigned char *rom_buffer) +{ + int i, len = genesis_rom_size - 2; + unsigned short checksum = 0; + + for (i = 512; i <= len; i += 2) + checksum += (rom_buffer[i] << 8) + (rom_buffer[i + 1]); + + return checksum; +} diff --git a/ucon64/2.0/src/console/genesis.h b/ucon64/2.0/src/console/genesis.h new file mode 100644 index 0000000..884c9cf --- /dev/null +++ b/ucon64/2.0/src/console/genesis.h @@ -0,0 +1,43 @@ +/* +genesis.h - Sega Genesis/Mega Drive support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2002 - 2003 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef GENESIS_H +#define GENESIS_H + +typedef enum { SMD = 1, BIN, MGD_GEN } genesis_file_t; + +extern const st_getopt2_t genesis_usage[]; + +extern genesis_file_t genesis_get_file_type (void); +extern int genesis_1991 (st_rominfo_t *rominfo); +extern int genesis_chk (st_rominfo_t *rominfo); +extern int genesis_j (st_rominfo_t *rominfo); +extern int genesis_n (st_rominfo_t *rominfo, const char *name); +extern int genesis_n2 (st_rominfo_t *rominfo, const char *name); +extern int genesis_s (st_rominfo_t *rominfo); +extern int genesis_smd (st_rominfo_t *rominfo); +extern int genesis_smds (void); +extern int genesis_bin (st_rominfo_t *rominfo); +extern int genesis_mgd (st_rominfo_t *rominfo); +extern int genesis_multi (int truncate_size, char *fname); +extern int genesis_init (st_rominfo_t *rominfo); +extern int genesis_f (st_rominfo_t *rominfo); +#endif diff --git a/ucon64/2.0/src/console/jaguar.c b/ucon64/2.0/src/console/jaguar.c new file mode 100644 index 0000000..dbb5538 --- /dev/null +++ b/ucon64/2.0/src/console/jaguar.c @@ -0,0 +1,109 @@ +/* +jaguar.c - Atari Jaguar support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include "misc/misc.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "jaguar.h" + + +const st_getopt2_t jaguar_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Panther(32bit prototype)/Jaguar64/Jaguar64 CD"/*"1989 Flare2/1993 Atari/1995 Atari"*/, + NULL + }, + { + "jag", 0, 0, UCON64_JAG, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_JAG_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} +}; + + +typedef struct st_jaguar +{ + char pad[16]; +} st_jaguar_t; +#define JAGUAR_HEADER_START 0x400 +#define JAGUAR_HEADER_LEN (sizeof (st_jaguar_t)) + +st_jaguar_t jaguar_header; + + +int +jaguar_init (st_rominfo_t *rominfo) +{ + int result = -1, x, value; + + rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? + ucon64.buheader_len : 0; + + ucon64_fread (&jaguar_header, JAGUAR_HEADER_START + + rominfo->buheader_len, JAGUAR_HEADER_LEN, ucon64.rom); + value = 0; + for (x = 0; x < 12; x++) + value += OFFSET (jaguar_header, x); + if (value == 0xb0) + result = 0; + else + { + rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? + ucon64.buheader_len : (int) UNKNOWN_HEADER_LEN; + + ucon64_fread (&jaguar_header, JAGUAR_HEADER_START + + rominfo->buheader_len, JAGUAR_HEADER_LEN, ucon64.rom); + value = 0; + for (x = 0; x < 12; x++) + value += OFFSET (jaguar_header, x); + + if (value == 0xb0) + result = 0; + else + result = -1; + } + if (ucon64.console == UCON64_JAG) + result = 0; + + rominfo->header_start = JAGUAR_HEADER_START; + rominfo->header_len = JAGUAR_HEADER_LEN; + rominfo->header = &jaguar_header; + + rominfo->console_usage = jaguar_usage[0].help; + rominfo->copier_usage = unknown_usage[0].help; + + return result; +} diff --git a/ucon64/2.0/src/console/jaguar.h b/ucon64/2.0/src/console/jaguar.h new file mode 100644 index 0000000..48d8ceb --- /dev/null +++ b/ucon64/2.0/src/console/jaguar.h @@ -0,0 +1,27 @@ +/* +jaguar.h - Atari Jaguar support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef JAGUAR_H +#define JAGUAR_H + +extern const st_getopt2_t jaguar_usage[]; +extern int jaguar_init (st_rominfo_t *rominfo); + +#endif diff --git a/ucon64/2.0/src/console/lynx.c b/ucon64/2.0/src/console/lynx.c new file mode 100644 index 0000000..a4a0178 --- /dev/null +++ b/ucon64/2.0/src/console/lynx.c @@ -0,0 +1,357 @@ +/* +lynx.c - Atari Lynx support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2002 - 2003 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "misc/bswap.h" +#include "misc/file.h" +#include "misc/misc.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "lynx.h" + + +const st_getopt2_t lynx_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Handy (prototype)/Lynx/Lynx II"/*"1987 Epyx/1989 Atari/1991 Atari"*/, + NULL + }, + { + "lynx", 0, 0, UCON64_LYNX, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_LYNX_SWITCH] + }, + { + "lyx", 0, 0, UCON64_LYX, + NULL, "convert to LYX/RAW (strip 64 Bytes LNX header)", + &ucon64_wf[WF_OBJ_LYNX_DEFAULT] + }, + { + "lnx", 0, 0, UCON64_LNX, + NULL, "convert to LNX (uses default values for the header);\n" + "adjust the LNX header with the following options", + &ucon64_wf[WF_OBJ_LYNX_DEFAULT] + }, + { + "n", 1, 0, UCON64_N, + "NEW_NAME", "change internal ROM name to NEW_NAME (LNX only)", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "nrot", 0, 0, UCON64_NROT, + NULL, "set no rotation (LNX only)", + &ucon64_wf[WF_OBJ_LYNX_DEFAULT] + }, + { + "rotl", 0, 0, UCON64_ROTL, + NULL, "set rotation left (LNX only)", + &ucon64_wf[WF_OBJ_LYNX_DEFAULT] + }, + { + "rotr", 0, 0, UCON64_ROTR, + NULL, "set rotation right (LNX only)", + &ucon64_wf[WF_OBJ_LYNX_DEFAULT] + }, + { + "b0", 1, 0, UCON64_B0, + "N", "change Bank0 kBytes size to N={0,64,128,256,512} (LNX only)", + &ucon64_wf[WF_OBJ_LYNX_DEFAULT] + }, + { + "b1", 1, 0, UCON64_B1, + "N", "change Bank1 kBytes size to N={0,64,128,256,512} (LNX only)", + &ucon64_wf[WF_OBJ_LYNX_DEFAULT] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} +}; + +const char *lynx_lyx_desc = "convert to LYX/RAW (strip 64 Bytes LNX header)"; + +//static const char *lnx_usage[] = "LNX header"; +#define LNX_HEADER_START 0 +#define LNX_HEADER_LEN (sizeof (st_lnx_header_t)) + +st_lnx_header_t lnx_header; + + +int +lynx_lyx (st_rominfo_t *rominfo) +{ + char dest_name[FILENAME_MAX]; + + if (!rominfo->buheader_len) + { + fprintf (stderr, "ERROR: This is no LNX file\n\n"); + return -1; + } + + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".lyx"); + + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, rominfo->buheader_len, ucon64.file_size, dest_name, "wb"); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +lynx_lnx (st_rominfo_t *rominfo) +{ + st_lnx_header_t header; + char dest_name[FILENAME_MAX]; + int size = ucon64.file_size; + + if (rominfo->buheader_len != 0) + { + fprintf (stderr, "ERROR: This seems to already be an LNX file\n\n"); + return -1; + } + + header.page_size_bank0 = size > 4 * MBIT ? 4 * MBIT / 256 : size / 256; + header.page_size_bank1 = size > 4 * MBIT ? (size - (4 * MBIT)) / 256 : 0; +#ifdef WORDS_BIGENDIAN + header.page_size_bank0 = bswap_16 (header.page_size_bank0); + header.page_size_bank1 = bswap_16 (header.page_size_bank1); +#endif + + memset (header.cartname, 0, sizeof (header.cartname)); + memset (header.manufname, 0, sizeof (header.manufname)); + memset (header.spare, 0, sizeof (header.spare)); + +#ifdef WORDS_BIGENDIAN + header.version = bswap_16 (1); +#else + header.version = 1; +#endif + + memcpy (header.magic, "LYNX", 4); + header.rotation = 0; + strncpy (header.cartname, ucon64.rom, sizeof (header.cartname)); + strcpy (header.manufname, "Atari"); + + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".lnx"); + + ucon64_file_handler (dest_name, NULL, 0); + ucon64_fwrite (&header, 0, sizeof (st_lnx_header_t), dest_name, "wb"); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "ab"); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +static int +lynx_rot (st_rominfo_t *rominfo, int rotation) +{ + st_lnx_header_t header; + char dest_name[FILENAME_MAX]; + + if (!rominfo->buheader_len) + { + fprintf (stderr, "ERROR: This is no LNX file\n\n"); + return -1; + } + + ucon64_fread (&header, 0, sizeof (st_lnx_header_t), ucon64.rom); + + header.rotation = rotation; // header.rotation is an 8-bit field + + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + ucon64_fwrite (&header, 0, sizeof (st_lnx_header_t), dest_name, "r+b"); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +lynx_nrot (st_rominfo_t *rominfo) +{ + return lynx_rot (rominfo, 0); // no rotation +} + + +int +lynx_rotl (st_rominfo_t *rominfo) +{ + return lynx_rot (rominfo, 1); // rotate left +} + + +int +lynx_rotr (st_rominfo_t *rominfo) +{ + return lynx_rot (rominfo, 2); // rotate right +} + + +int +lynx_n (st_rominfo_t *rominfo, const char *name) +{ + st_lnx_header_t header; + char dest_name[FILENAME_MAX]; + + if (!rominfo->buheader_len) + { + fprintf (stderr, "ERROR: This is no LNX file\n\n"); + return -1; + } + + ucon64_fread (&header, 0, sizeof (st_lnx_header_t), ucon64.rom); + + memset (header.cartname, 0, sizeof (header.cartname)); + strncpy (header.cartname, name, sizeof (header.cartname)); + + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + ucon64_fwrite (&header, 0, sizeof (st_lnx_header_t), dest_name, "r+b"); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +static int +lynx_b (st_rominfo_t *rominfo, int bank, const char *value) +{ + st_lnx_header_t header; + short int *bankvar; + char dest_name[FILENAME_MAX]; + + if (!rominfo->buheader_len) + { + fprintf (stderr, "ERROR: This is no LNX file\n\n"); + return -1; + } + + ucon64_fread (&header, 0, sizeof (st_lnx_header_t), ucon64.rom); + + bankvar = (bank == 0 ? &header.page_size_bank0 : &header.page_size_bank1); + if ((atol (value) % 64) != 0 || (atol (value) > 512)) + *bankvar = 0; + else +#ifdef WORDS_BIGENDIAN + *bankvar = bswap_16 (atol (value) * 4); +#else + *bankvar = atol (value) * 4; +#endif + + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + ucon64_fwrite (&header, 0, sizeof (st_lnx_header_t), dest_name, "r+b"); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +lynx_b0 (st_rominfo_t *rominfo, const char *value) +{ + return lynx_b (rominfo, 0, value); +} + + +int +lynx_b1 (st_rominfo_t *rominfo, const char *value) +{ + return lynx_b (rominfo, 1, value); +} + + +int +lynx_init (st_rominfo_t *rominfo) +{ + int result = -1; + + rominfo->console_usage = lynx_usage[0].help; + rominfo->copier_usage = unknown_usage[0].help; + + ucon64_fread (&lnx_header, 0, LNX_HEADER_LEN, ucon64.rom); + if (!strncmp (lnx_header.magic, "LYNX", 4)) + result = 0; + else + result = -1; + if (ucon64.console == UCON64_LYNX) + result = 0; + + if (!strncmp (lnx_header.magic, "LYNX", 4)) + { + rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? + ucon64.buheader_len : (int) LNX_HEADER_LEN; + + if (UCON64_ISSET (ucon64.buheader_len) && !ucon64.buheader_len) + return ucon64.console == UCON64_LYNX ? 0 : result; + + ucon64_fread (&lnx_header, 0, LNX_HEADER_LEN, ucon64.rom); + rominfo->buheader = &lnx_header; + + // internal ROM name + strcpy (rominfo->name, lnx_header.cartname); + + // ROM maker + rominfo->maker = lnx_header.manufname; + + // misc stuff + sprintf (rominfo->misc, + "Internal Size: Bank0 %hd Bytes (%.4f Mb)\n" + " Bank1 %hd Bytes (%.4f Mb)\n" + "Version: %hd\n" + "Rotation: %s", +#ifdef WORDS_BIGENDIAN + bswap_16 (lnx_header.page_size_bank0) * 256, + TOMBIT_F (bswap_16 (lnx_header.page_size_bank0) * 256), + bswap_16 (lnx_header.page_size_bank1) * 256, + TOMBIT_F (bswap_16 (lnx_header.page_size_bank1) * 256), + bswap_16 (lnx_header.version), +#else + lnx_header.page_size_bank0 * 256, + TOMBIT_F (lnx_header.page_size_bank0 * 256), + lnx_header.page_size_bank1 * 256, + TOMBIT_F (lnx_header.page_size_bank1 * 256), + lnx_header.version, +#endif + (!lnx_header.rotation) ? "No" : ((lnx_header.rotation == 1) ? "Left" : "Right")); + } + + return result; +} diff --git a/ucon64/2.0/src/console/lynx.h b/ucon64/2.0/src/console/lynx.h new file mode 100644 index 0000000..865a43e --- /dev/null +++ b/ucon64/2.0/src/console/lynx.h @@ -0,0 +1,54 @@ +/* +lynx.h - Atari Lynx support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef LYNX_H +#define LYNX_H +typedef struct st_lnx_header +{ + char magic[4]; + short int page_size_bank0; + short int page_size_bank1; + short int version; + char cartname[32]; + char manufname[16]; + unsigned char rotation; + unsigned char spare[5]; +} st_lnx_header_t; + + +extern int lynxer_main(const char *FileName); + +extern int lynx_b0 (st_rominfo_t *rominfo, const char *value); +extern int lynx_b1 (st_rominfo_t *rominfo, const char *value); + +extern int lynx_lnx (st_rominfo_t *rominfo); + +extern int lynx_lyx (st_rominfo_t *rominfo); +extern const char *lynx_lyx_desc; + +extern int lynx_n (st_rominfo_t *rominfo, const char *name); +extern int lynx_nrot (st_rominfo_t *rominfo); +extern int lynx_rotl (st_rominfo_t *rominfo); +extern int lynx_rotr (st_rominfo_t *rominfo); + +extern int lynx_init (st_rominfo_t *rominfo); + +extern const st_getopt2_t lynx_usage[]; +#endif // LYNX_H diff --git a/ucon64/2.0/src/console/n64.c b/ucon64/2.0/src/console/n64.c new file mode 100644 index 0000000..10523b7 --- /dev/null +++ b/ucon64/2.0/src/console/n64.c @@ -0,0 +1,743 @@ +/* +n64.c - Nintendo 64 support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2002 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "misc/chksum.h" +#include "misc/file.h" +#include "misc/misc.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "n64.h" +#include "patch/ips.h" +#include "patch/aps.h" +#include "backup/doctor64.h" +#include "backup/doctor64jr.h" +#include "backup/cd64.h" +#include "backup/dex.h" +#include "backup/z64.h" + + +#define N64_HEADER_LEN (sizeof (st_n64_header_t)) +#define N64_SRAM_SIZE 512 +#define N64_NAME_LEN 20 +#define N64_BC_SIZE (0x1000 - N64_HEADER_LEN) +#define LAC_ROM_SIZE 1310720 + +const st_getopt2_t n64_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Nintendo 64"/*"1996 Nintendo http://www.nintendo.com"*/, + NULL + }, + { + "n64", 0, 0, UCON64_N64, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_N64_SWITCH] + }, + { + "int", 0, 0, UCON64_INT, + NULL, "force ROM is in interleaved format (2143, V64)", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "nint", 0, 0, UCON64_NINT, + NULL, "force ROM is not in interleaved format (1234, Z64)", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "n", 1, 0, UCON64_N, + "NEW_NAME", "change internal ROM name to NEW_NAME", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "v64", 0, 0, UCON64_V64, + NULL, "convert to Doctor V64 (and compatibles/interleaved)", + &ucon64_wf[WF_OBJ_N64_DEFAULT] + }, + { + "z64", 0, 0, UCON64_Z64, + NULL, "convert to Mr. Backup Z64 (not interleaved)", + &ucon64_wf[WF_OBJ_N64_DEFAULT] + }, + { + "dint", 0, 0, UCON64_DINT, + NULL, "convert ROM to (non-)interleaved format (1234 <-> 2143)", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE] + }, + { + "swap", 0, 0, UCON64_SWAP, + NULL, "same as " OPTION_LONG_S "dint, byte-swap ROM", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE] + }, + { + "swap2", 0, 0, UCON64_SWAP2, + NULL, "word-swap ROM (1234 <-> 3412)", + NULL + }, +#if 0 + { + "f", 0, 0, UCON64_F, + NULL, "remove NTSC/PAL protection", + NULL + }, +#endif + { + "bot", 1, 0, UCON64_BOT, + "BOOTCODE", "replace/extract BOOTCODE (4032 Bytes) in/from ROM;\n" + "extracts automatically if BOOTCODE does not exist", + &ucon64_wf[WF_OBJ_N64_DEFAULT] + }, + { + "lsram", 1, 0, UCON64_LSRAM, + "SRAM", "LaC's SRAM upload tool; ROM should be LaC's ROM image\n" + "the SRAM must have a size of 512 Bytes\n" + "this option generates a ROM which can be used to transfer\n" + "SRAMs to your cartridge's SRAM (EEPROM)", + &ucon64_wf[WF_OBJ_N64_INIT_PROBE] + }, + { + "usms", 1, 0, UCON64_USMS, + "SMSROM", "Jos Kwanten's UltraSMS (Sega Master System/Game Gear emulator);\n" + "ROM should be Jos Kwanten's UltraSMS ROM image\n" + "works only for SMS ROMs which are <= 4 Mb in size", + &ucon64_wf[WF_OBJ_N64_DEFAULT] + }, + { + "chk", 0, 0, UCON64_CHK, + NULL, "fix ROM checksum\n" + "supports only 6102 and 6105 boot codes", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, +#if 0 + { + "bios", 1, 0, UCON64_BIOS, + "BIOS", "enable backup in Doctor V64 BIOS", + NULL + }, +#endif + {NULL, 0, 0, 0, NULL, NULL, NULL} +}; + + +typedef struct st_n64_header +{ + unsigned char pad[64]; +#if 0 + unsigned char validation[2]; + unsigned char compression; + unsigned char pad1; + unsigned long clockrate; + unsigned long programcounter; + unsigned long release; + unsigned long crc1; + unsigned long crc2; + unsigned char pad2[8]; + unsigned char name[20]; + unsigned char pad3[7]; + unsigned char maker; + unsigned char cartridgeid[2]; + unsigned char countrycode; + unsigned char pad4; +#endif +} st_n64_header_t; + +st_n64_header_t n64_header; + +typedef struct st_n64_chksum +{ + unsigned int crc1; + unsigned int crc2; +} st_n64_chksum_t; + +static st_n64_chksum_t n64crc; +static int n64_chksum (st_rominfo_t *rominfo, const char *filename); + + +int +n64_v64 (st_rominfo_t *rominfo) +{ + char dest_name[FILENAME_MAX]; + + if (rominfo->interleaved) + { + fprintf (stderr, "ERROR: Already in V64 format\n"); + exit (1); + } + + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".v64"); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + ucon64_fbswap16 (dest_name, 0, ucon64.file_size); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +n64_z64 (st_rominfo_t *rominfo) +{ + char dest_name[FILENAME_MAX]; + + if (!rominfo->interleaved) + { + fprintf (stderr, "ERROR: Already in Z64 format\n"); + exit (1); + } + + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".z64"); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + ucon64_fbswap16 (dest_name, 0, ucon64.file_size); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +n64_n (st_rominfo_t *rominfo, const char *name) +{ + char buf[N64_NAME_LEN], dest_name[FILENAME_MAX]; + + memset (buf, ' ', N64_NAME_LEN); + strncpy (buf, name, strlen (name) > N64_NAME_LEN ? N64_NAME_LEN : strlen (name)); + + if (rominfo->interleaved) + ucon64_bswap16_n (buf, N64_NAME_LEN); + + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + ucon64_fwrite (buf, rominfo->buheader_len + 32, N64_NAME_LEN, dest_name, "r+b"); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +n64_f (st_rominfo_t *rominfo) +{ + // TODO: PAL/NTSC fix + (void) rominfo; // warning remover + fputs ("ERROR: The function for cracking N64 region protections is not yet implemented\n", stderr); + return 0; +} + + +static void +n64_update_chksum (st_rominfo_t *rominfo, const char *filename, char *buf) +{ + uint64_t crc; + int x; + + // n64crc is set by n64_chksum() when called from n64_init() + crc = (((uint64_t) n64crc.crc1) << 32) | n64crc.crc2; + for (x = 0; x < 8; x++) + { + buf[x] = (char) (crc >> 56); + crc <<= 8; + } + if (rominfo->interleaved) + ucon64_bswap16_n (buf, 8); + ucon64_fwrite (buf, rominfo->buheader_len + 16, 8, filename, "r+b"); +} + + +int +n64_chk (st_rominfo_t *rominfo) +{ + char buf[8], dest_name[FILENAME_MAX]; + + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + + n64_update_chksum (rominfo, dest_name, buf); + dumper (stdout, buf, 8, rominfo->buheader_len + 16, DUMPER_HEX); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +n64_sram (st_rominfo_t *rominfo, const char *sramfile) +// Function to insert an SRAM file in LaC's SRAM upload tool (which is an N64 +// program) +{ + char sram[N64_SRAM_SIZE], dest_name[FILENAME_MAX], buf[8]; + + if (access (sramfile, F_OK)) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], sramfile); + exit (1); + } + + if (fsizeof (sramfile) != N64_SRAM_SIZE || ucon64.file_size != LAC_ROM_SIZE) + { + fprintf (stderr, "ERROR: ROM is not %d bytes and/or SRAM is not %d bytes\n", + LAC_ROM_SIZE, N64_SRAM_SIZE); + exit (1); + } + + ucon64_fread (sram, 0, N64_SRAM_SIZE, sramfile); + + if (rominfo->interleaved) + ucon64_bswap16_n (sram, N64_SRAM_SIZE); + + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + ucon64_fwrite (sram, 0x286c0, N64_SRAM_SIZE, dest_name, "r+b"); + n64_chksum (rominfo, dest_name); // calculate the checksum of the modified file + n64_update_chksum (rominfo, dest_name, buf); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +n64_bot (st_rominfo_t *rominfo, const char *bootfile) +{ + char buf[N64_BC_SIZE], dest_name[FILENAME_MAX]; + + if (!access (bootfile, F_OK)) + { + strcpy (dest_name, ucon64.rom); + ucon64_fread (buf, 0, N64_BC_SIZE, bootfile); + + if (rominfo->interleaved) + ucon64_bswap16_n (buf, N64_BC_SIZE); + + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + ucon64_fwrite (buf, rominfo->buheader_len + N64_HEADER_LEN, N64_BC_SIZE, + dest_name, "r+b"); + } + else + { + strcpy (dest_name, bootfile); +// set_suffix (dest_name, ".bot"); + ucon64_file_handler (dest_name, NULL, OF_FORCE_BASENAME | OF_FORCE_SUFFIX); + fcopy (ucon64.rom, rominfo->buheader_len + N64_HEADER_LEN, N64_BC_SIZE, + dest_name, "wb"); + + if (rominfo->interleaved) + ucon64_fbswap16 (dest_name, 0, fsizeof (dest_name)); + } + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +n64_usms (st_rominfo_t *rominfo, const char *smsrom) +{ + char dest_name[FILENAME_MAX], *usmsbuf; + int size; + + if (access (smsrom, F_OK)) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], smsrom); + exit (1); + } + + size = fsizeof (smsrom); + // must be smaller than 4 Mbit, 524288 bytes will be inserted + // from 0x1b410 to 0x9b40f (0x7ffff) + if (size > 4 * MBIT) + { + fprintf (stderr, "ERROR: The Sega Master System/Game Gear ROM must be 524288 bytes or less\n"); + exit (1); + } + + if (!(usmsbuf = (char *) malloc (4 * MBIT))) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], 4 * MBIT); + exit (1); + } + memset (usmsbuf, 0xff, 4 * MBIT); + ucon64_fread (usmsbuf, 0, size, smsrom); + + if (rominfo->interleaved) + ucon64_bswap16_n (usmsbuf, size); + + // Jos Kwanten's rominserter.exe produces a file named Patched.v64 + strcpy (dest_name, "Patched.v64"); + ucon64_file_handler (dest_name, NULL, OF_FORCE_BASENAME | OF_FORCE_SUFFIX); + fcopy (ucon64.rom, rominfo->buheader_len, ucon64.file_size, dest_name, "wb"); + ucon64_fwrite (usmsbuf, rominfo->buheader_len + 0x01b410, 4 * MBIT, dest_name, "r+b"); + + free (usmsbuf); + printf (ucon64_msg[WROTE], dest_name); + + return 0; +} + + +int +n64_init (st_rominfo_t *rominfo) +{ + int result = -1, x; + unsigned int value = 0; +#define N64_MAKER_MAX 0x50 + const char *n64_maker[N64_MAKER_MAX] = + { + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, "Nintendo", NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "Nintendo", NULL + }, +#define N64_COUNTRY_MAX 0x5a + *n64_country[N64_COUNTRY_MAX] = + { + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "Germany", "U.S.A.", + "France", NULL, NULL, "Italy", "Japan", + NULL, NULL, NULL, NULL, NULL, + "Europe", NULL, NULL, "Spain", NULL, + "Australia", NULL, NULL, "France, Germany, The Netherlands", NULL // Holland is an incorrect name for The Netherlands + }; + + rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? ucon64.buheader_len : 0; + + ucon64_fread (&n64_header, rominfo->buheader_len, N64_HEADER_LEN, ucon64.rom); + + value = OFFSET (n64_header, 0); + value += OFFSET (n64_header, 1) << 8; + value += OFFSET (n64_header, 2) << 16; + value += OFFSET (n64_header, 3) << 24; + /* + 0x41123780 and 0x12418037 can be found in te following files: + 2 Blokes & An Armchair - Nintendo 64 Remix Remix (PD) + Zelda Boot Emu V1 (PD) + Zelda Boot Emu V2 (PD) + */ + if (value == 0x40123780 || value == 0x41123780) // 0x80371240, 0x80371241 + { + rominfo->interleaved = 0; + result = 0; + } + else if (value == 0x12408037 || value == 0x12418037) // 0x37804012, 0x37804112 + { + rominfo->interleaved = 1; + result = 0; + } + else + result = -1; + + if (UCON64_ISSET (ucon64.interleaved)) + rominfo->interleaved = ucon64.interleaved; + if (ucon64.console == UCON64_N64) + result = 0; + + // internal ROM header + rominfo->header_start = 0; + rominfo->header_len = N64_HEADER_LEN; + rominfo->header = &n64_header; + + // internal ROM name + strncpy (rominfo->name, (char *) &OFFSET (n64_header, 32), N64_NAME_LEN); + if (rominfo->interleaved) + ucon64_bswap16_n (rominfo->name, N64_NAME_LEN); + rominfo->name[N64_NAME_LEN] = 0; + + // ROM maker + rominfo->maker = NULL_TO_UNKNOWN_S (n64_maker[MIN (OFFSET + (n64_header, 59 ^ rominfo->interleaved), N64_MAKER_MAX - 1)]); + + // ROM country + rominfo->country = NULL_TO_UNKNOWN_S (n64_country[MIN (OFFSET + (n64_header, 63 ^ (!rominfo->interleaved)), N64_COUNTRY_MAX - 1)]); + + // CRC stuff + if (!UCON64_ISSET (ucon64.do_not_calc_crc) && result == 0) + { + rominfo->has_internal_crc = 1; + rominfo->internal_crc_len = 4; + + n64_chksum (rominfo, ucon64.rom); + rominfo->current_internal_crc = n64crc.crc1; + + value = 0; + for (x = 0; x < 4; x++) + { + rominfo->internal_crc <<= 8; + rominfo->internal_crc += OFFSET (n64_header, 16 + (x ^ rominfo->interleaved)); + value <<= 8; + value += OFFSET (n64_header, 20 + (x ^ rominfo->interleaved)); + } + + sprintf (rominfo->internal_crc2, + "2nd Checksum: %s, 0x%08x (calculated) %c= 0x%08x (internal)%s", +#ifdef USE_ANSI_COLOR + ucon64.ansi_color ? + ((n64crc.crc2 == value) ? + "\x1b[01;32mOk\x1b[0m" : "\x1b[01;31mBad\x1b[0m") + : + ((n64crc.crc2 == value) ? "Ok" : "Bad"), +#else + (n64crc.crc2 == value) ? "Ok" : "Bad", +#endif + n64crc.crc2, + (n64crc.crc2 == value) ? '=' : '!', value, + (n64crc.crc2 != value) ? + "\nNOTE: The checksum routine supports only 6102 and 6105 boot codes" : + ""); + } + + rominfo->console_usage = n64_usage[0].help; + rominfo->copier_usage = (!rominfo->buheader_len ? + ((!rominfo->interleaved) ? z64_usage[0].help : doctor64_usage[0].help) : unknown_usage[0].help); + + return result; +} + + +/* + ROM check sum routine is based on chksum64 V1.2 by Andreas Sterbenz + , a program to calculate the ROM checksum of + Nintendo 64 ROMs. +*/ +#define ROL(i, b) (((i) << (b)) | ((i) >> (32 - (b)))) +#define BYTES2LONG(b, s) ( (b)[0^(s)] << 24 | \ + (b)[1^(s)] << 16 | \ + (b)[2^(s)] << 8 | \ + (b)[3^(s)] ) + +#define CHECKSUM_START 0x1000 //(N64_HEADER_LEN + N64_BC_SIZE) +#define CHECKSUM_LENGTH 0x100000 +#define CHECKSUM_STARTVALUE1 0xf8ca4ddc +#define CHECKSUM_STARTVALUE2 0xdf26f436 +#define CALC_CRC32 // see this as a marker, don't disable + +int +n64_chksum (st_rominfo_t *rominfo, const char *filename) +{ + unsigned char bootcode_buf[CHECKSUM_START], chunk[MAXBUFSIZE & ~3]; // size must be a multiple of 4 + unsigned int i, c1, k1, k2, t1, t2, t3, t4, t5, t6, clen = CHECKSUM_LENGTH, + rlen = (ucon64.file_size - rominfo->buheader_len) - CHECKSUM_START, + n = 0, bootcode; // using ucon64.file_size is ok for n64_init() & n64_sram() + FILE *file; +#ifdef CALC_CRC32 + unsigned int scrc32 = 0, fcrc32 = 0; // search CRC32 & file CRC32 + unsigned char *crc32_mem; +#endif + + if (rlen < CHECKSUM_START + CHECKSUM_LENGTH) + { +#ifdef CALC_CRC32 + n = ucon64.file_size - rominfo->buheader_len; + if ((crc32_mem = (unsigned char *) malloc (n)) == NULL) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], n); + return -1; + } + ucon64_fread (crc32_mem, rominfo->buheader_len, n, filename); + if (!rominfo->interleaved) + { + ucon64.fcrc32 = crc32 (0, crc32_mem, n); + ucon64_bswap16_n (crc32_mem, n); + } + ucon64.crc32 = crc32 (0, crc32_mem, n); + free (crc32_mem); +#endif + return -1; // ROM is too small + } + + if (!(file = fopen (filename, "rb"))) + return -1; + +#ifdef CALC_CRC32 + if (!rominfo->interleaved) + { + if ((crc32_mem = (unsigned char *) malloc (MAXBUFSIZE)) == NULL) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], MAXBUFSIZE); + fclose (file); + return -1; + } + } + else + crc32_mem = chunk; + + fseek (file, rominfo->buheader_len, SEEK_SET); + fread (crc32_mem, 1, CHECKSUM_START, file); + memcpy (bootcode_buf, crc32_mem + N64_HEADER_LEN, N64_BC_SIZE); + if (!rominfo->interleaved) + { + fcrc32 = crc32 (0, crc32_mem, CHECKSUM_START); + ucon64_bswap16_n (crc32_mem, CHECKSUM_START); + } + else + ucon64_bswap16_n (bootcode_buf, N64_BC_SIZE); + scrc32 = crc32 (0, crc32_mem, CHECKSUM_START); +#else + fseek (file, rominfo->buheader_len + N64_HEADER_LEN, SEEK_SET); + fread (bootcode_buf, 1, N64_BC_SIZE, file); + if (rominfo->interleaved) + ucon64_bswap16_n (bootcode_buf, N64_BC_SIZE); +#endif + if (crc32 (0, bootcode_buf, N64_BC_SIZE) == 0x98bc2c86) + { + bootcode = 6105; + i = CHECKSUM_STARTVALUE2; + } + else + { + bootcode = 0; // everything else + i = CHECKSUM_STARTVALUE1; + } + + t1 = i; + t2 = i; + t3 = i; + t4 = i; + t5 = i; + t6 = i; + + while (1) + { + if (rlen > 0) + { + if ((n = fread (chunk, 1, MIN (sizeof (chunk), clen), file))) + { +#ifdef CALC_CRC32 + if (!rominfo->interleaved) + { + memcpy (crc32_mem, chunk, n); + fcrc32 = crc32 (fcrc32, crc32_mem, n); + ucon64_bswap16_n (crc32_mem, n); + } + scrc32 = crc32 (scrc32, crc32_mem, n); +#endif + } + } + else + n = MIN (sizeof (chunk), clen); + + n &= ~3; + if (n == 0) + break; + for (i = 0; i < n; i += 4) + { + c1 = BYTES2LONG (&chunk[i], rominfo->interleaved); + k1 = t6 + c1; + if (k1 < t6) + t4++; + t6 = k1; + t3 ^= c1; + k2 = c1 & 0x1f; + k1 = ROL (c1, k2); + t5 += k1; + if (c1 < t2) + t2 ^= k1; + else + t2 ^= t6 ^ c1; + + if (bootcode == 6105) + { + k1 = 0x710 + (i & 0xff); + //t1 += BYTES2LONG (&bootcode_buf[k1], 0) ^ c1; + t1 += ((bootcode_buf[k1] << 24) | (bootcode_buf[k1 + 1] << 16) | + (bootcode_buf[k1 + 2] << 8) | (bootcode_buf[k1 + 3])) ^ c1; + } + else + t1 += c1 ^ t5; + } + if (rlen > 0) + { + rlen -= n; + if (rlen <= 0) + memset (chunk, 0, sizeof (chunk)); + } + clen -= n; + } + n64crc.crc1 = t6 ^ t4 ^ t3; + n64crc.crc2 = t5 ^ t2 ^ t1; + +#ifdef CALC_CRC32 + if (!rominfo->interleaved) + { + free (crc32_mem); + crc32_mem = chunk; + } + while ((n = fread (crc32_mem, 1, sizeof (chunk), file))) + { + if (!rominfo->interleaved) + { + fcrc32 = crc32 (fcrc32, crc32_mem, n); + ucon64_bswap16_n (crc32_mem, n); + } + scrc32 = crc32 (scrc32, crc32_mem, n); + } + + ucon64.crc32 = scrc32; + if (!rominfo->interleaved) + ucon64.fcrc32 = fcrc32; +#endif + + fclose (file); + return 0; +} diff --git a/ucon64/2.0/src/console/n64.h b/ucon64/2.0/src/console/n64.h new file mode 100644 index 0000000..df5a79e --- /dev/null +++ b/ucon64/2.0/src/console/n64.h @@ -0,0 +1,37 @@ +/* +n64.h - Nintendo 64 support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef N64_H +#define N64_H + +extern const st_getopt2_t n64_usage[]; + +extern int n64_bot (st_rominfo_t *rominfo, const char *bootfile); +extern int n64_chk (st_rominfo_t *rominfo); + +extern int n64_f (st_rominfo_t *rominfo); +extern int n64_init (st_rominfo_t *rominfo); +extern int n64_n (st_rominfo_t *rominfo, const char *name); +extern int n64_sram (st_rominfo_t *rominfo, const char *sramfile); +extern int n64_usms (st_rominfo_t *rominfo, const char *smsrom); +extern int n64_v64 (st_rominfo_t *rominfo); +extern int n64_z64 (st_rominfo_t *rominfo); + +#endif diff --git a/ucon64/2.0/src/console/neogeo.c b/ucon64/2.0/src/console/neogeo.c new file mode 100644 index 0000000..d461731 --- /dev/null +++ b/ucon64/2.0/src/console/neogeo.c @@ -0,0 +1,259 @@ +/* +neogeo.c - NeoGeo support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "misc/misc.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "neogeo.h" + + +const st_getopt2_t neogeo_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Neo Geo/Neo Geo CD(Z)/MVS"/*"1990/1994 SNK http://www.neogeo.co.jp"*/, + NULL + }, + { + "ng", 0, 0, UCON64_NG, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_NG_SWITCH] + }, +#if 0 + " " OPTION_LONG_S "ns force ROM is not split\n" + "TODO: " OPTION_LONG_S "mgd convert to Multi Game Doctor/MGD2/RAW\n" + "TODO: " OPTION_LONG_S "mvs convert to Arcade/MVS\n" +#endif + { + "bios", 1, 0, UCON64_BIOS, + "BIOS", "convert NeoCD BIOS to work with NeoCD emulator" /*;\n" + "http://www.illusion-city.com/neo/"*/, + &ucon64_wf[WF_OBJ_NG_DEFAULT] + }, +#if 0 + "TODO: " OPTION_S "j join split ROM" + "TODO: " OPTION_S "s split ROM into 4Mb parts (for backup unit(s) with fdd)" + "TODO: " OPTION_LONG_S "ngs convert Neo Geo sound to WAV; " OPTION_LONG_S "rom=*_m1.rom or *_v*.rom" +#endif + { + "sam", 1, 0, UCON64_SAM, + "SAMFILE", "convert SAM/M.A.M.E. sound to WAV", + &ucon64_wf[WF_OBJ_NG_DEFAULT] + }, +// "TODO: " OPTION_LONG_S "chkm check/fix Multiple Arcade Machine Emulator/M.A.M.E. ROMs;\n" +// " " OPTION_LONG_S "rom=DIRECTORY" + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +static int sam2wav (const char *filename); + +#define NEOGEO_HEADER_START 0 +#define NEOGEO_HEADER_LEN 0 + + +int +neogeo_bios (const char *fname) +{ + char dest_name[FILENAME_MAX]; + + strcpy (dest_name, fname); + set_suffix (dest_name, ".tmp"); + + ucon64_file_handler (dest_name, NULL, 0); + fcopy (fname, 0, MBIT, dest_name, "wb"); + ucon64_fbswap16 (dest_name, 0, MBIT); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +neogeo_sam (const char *fname) +{ + if (sam2wav (fname) == -1) + fprintf (stderr, "ERROR: SAM header seems to be corrupt\n"); + + return 0; +} + + +int +neogeo_mgd (void) +{ + return 0; +} + + +int +neogeo_mvs (void) +{ + return 0; +} + + +int +neogeo_s (void) +{ + return 0; +} + + +int +neogeo_init (st_rominfo_t *rominfo) +{ + int result = -1; + + rominfo->console_usage = neogeo_usage[0].help; + rominfo->copier_usage = unknown_usage[0].help; + + return result; +} + + +int +sam2wav (const char *filename) +{ + unsigned char buf[32]; + FILE *fh, *fh2; + unsigned datasize, wavesize, riffsize, freq, bits, rate; + + if (fsizeof (filename) < 16) + return -1; + if (!(fh = fopen (filename, "rb"))) + return -1; + + strcpy ((char *) buf, filename); + set_suffix ((char *) buf, ".wav"); + + if (!(fh2 = fopen ((char *) buf, "wb"))) + return -1; + fread (buf, 1, 4, fh); + + if (strncmp ((char *) buf, "MAME", 4) != 0) + return -1; + fread (buf, 1, 4, fh); + + datasize = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24); + wavesize = /* "fmt " */ 4 + 4 + 4 + 2 + 2; + riffsize = /* RIFF */ 4 + 4 + 4 + 4 + 4 + wavesize + datasize; + + buf[0] = riffsize & 0xff; + buf[1] = (riffsize >> 8) & 0xff; + buf[2] = (riffsize >> 16) & 0xff; + buf[3] = (riffsize >> 24) & 0xff; + + fwrite ("RIFF", 1, 4, fh2); + fwrite (buf, 1, 4, fh2); + + fwrite ("WAVE", 1, 4, fh2); + fwrite ("fmt ", 1, 4, fh2); + + buf[0] = wavesize & 0xff; + buf[1] = (wavesize >> 8) & 0xff; + buf[2] = (wavesize >> 16) & 0xff; + buf[3] = (wavesize >> 24) & 0xff; + fwrite (buf, 1, 4, fh2); + + /* number of channels - alway 1 for MAME samples */ + fwrite ("\001\000\001\000", 1, 4, fh2); + + /* read frequency */ + fread (buf, 1, 4, fh); + freq = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24); + + /* write frequency */ + fwrite (buf, 1, 4, fh2); + + /* read bits, amplitude and extension */ + fread (buf, 1, 4, fh); + + bits = buf[0]; + + if (bits == 16) + rate = freq * 2; + else + rate = freq; + + buf[0] = rate & 0xff; + buf[1] = (rate >> 8) & 0xff; + buf[2] = (rate >> 16) & 0xff; + buf[3] = (rate >> 24) & 0xff; + + /* write bytes per second */ + fwrite (buf, 1, 4, fh2); + + /* write alignment */ + if (bits == 16) + fwrite ("\002\000", 1, 2, fh2); + else + fwrite ("\001\000", 1, 2, fh2); + + buf[0] = bits & 0xff; + buf[1] = (bits >> 8) & 0xff; + + /* write bits per sample */ + fwrite (buf, 1, 2, fh2); + fwrite ("data", 1, 4, fh2); + + buf[0] = datasize & 0xff; + buf[1] = (datasize >> 8) & 0xff; + buf[2] = (datasize >> 16) & 0xff; + buf[3] = (datasize >> 24) & 0xff; + fwrite (buf, 1, 4, fh2); + + if (bits == 16) + { + for (;;) + { + if (fread (buf, 1, 2, fh) != 2) + break; + fwrite (buf, 1, 2, fh2); + } + } + else + { + for (;;) + { + if (fread (buf, 1, 1, fh) != 1) + break; + buf[0] = buf[0] ^ 0x80; + fwrite (buf, 1, 1, fh2); + } + } + fclose (fh2); + fclose (fh); + return 0; +} diff --git a/ucon64/2.0/src/console/neogeo.h b/ucon64/2.0/src/console/neogeo.h new file mode 100644 index 0000000..84bdc78 --- /dev/null +++ b/ucon64/2.0/src/console/neogeo.h @@ -0,0 +1,33 @@ +/* +neogeo.h - NeoGeo support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef NEOGEO_H +#define NEOGEO_H + +extern int neogeo_bios (const char *fname); +extern int neogeo_init (st_rominfo_t *rominfo); +extern int neogeo_mgd (void); +extern int neogeo_mvs (void); +extern int neogeo_s (void); +extern int neogeo_sam (const char *fname); + +extern const st_getopt2_t neogeo_usage[]; + +#endif diff --git a/ucon64/2.0/src/console/nes.c b/ucon64/2.0/src/console/nes.c new file mode 100644 index 0000000..dfc84a3 --- /dev/null +++ b/ucon64/2.0/src/console/nes.c @@ -0,0 +1,7707 @@ +/* +nes.c - Nintendo Entertainment System support for uCON64 + +Copyright (c) 1999 - 2003 NoisyB +Copyright (c) 2002 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include // va_list() +#ifdef HAVE_UNISTD_H +#include +#endif +#include "misc/bswap.h" +#include "misc/misc.h" +#include "misc/string.h" +#include "misc/chksum.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "nes.h" +#include "backup/smc.h" + + +#define STD_COMMENT "Written with uCON64 " // first part of text to put in READ chunk + + +const st_getopt2_t nes_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Nintendo Entertainment System/NES/Famicom/Game Axe (Redant)" + /*"1983 Nintendo http://www.nintendo.com"*/, + NULL + }, + { + "nes", 0, 0, UCON64_NES, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_NES_SWITCH] + }, + { + "n", 1, 0, UCON64_N, + "NEW_NAME", "change internal ROM name to NEW_NAME (UNIF only)", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "unif", 0, 0, UCON64_UNIF, + NULL, "convert to UNIF format/UNF (uses default values)", + &ucon64_wf[WF_OBJ_NES_DEFAULT] + }, + { + "ines", 0, 0, UCON64_INES, + NULL, "convert to iNES format/NES (uses default values)", + &ucon64_wf[WF_OBJ_NES_DEFAULT] + }, + { + "ineshd", 0, 0, UCON64_INESHD, + NULL, "extract iNES header from ROM (16 Bytes)", + &ucon64_wf[WF_OBJ_NES_DEFAULT] + }, + { + "j", 0, 0, UCON64_J, + NULL, "join Pasofami/PRM/700/PRG/CHR/split ROM (Pasofami -> iNES)", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE] + }, + { + "pasofami", 0, 0, UCON64_PASOFAMI, + NULL, "convert to Pasofami/PRM/700/PRG/CHR", + &ucon64_wf[WF_OBJ_NES_DEFAULT] + }, + { + "s", 0, 0, UCON64_S, + NULL, "convert/split to Pasofami/PRM/700/PRG/CHR (iNES -> Pasofami)", + &ucon64_wf[WF_OBJ_ALL_DEFAULT_NO_SPLIT] + }, + { + "ffe", 0, 0, UCON64_FFE, + NULL, "convert to FFE format (Super Magic Card)", + &ucon64_wf[WF_OBJ_NES_DEFAULT] + }, + { + "mapr", 1, 0, UCON64_MAPR, + "MAPR", "specify board name or mapper number for conversion options\n" + "MAPR must be a board name for UNIF or a number for Pasofami\n" + "and iNES", + &ucon64_wf[WF_OBJ_NES_SWITCH] + }, + { + "dint", 0, 0, UCON64_DINT, + NULL, "deinterleave ROM (regardless whether the ROM is interleaved)", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_NO_SPLIT] + }, + { + "ctrl", 1, 0, UCON64_CTRL, + "TYPE", "specify controller type (UNIF only)\n" + "TYPE=0 regular joypad\n" + "TYPE=1 zapper\n" + "TYPE=2 R.O.B.\n" + "TYPE=3 Arkanoid controller\n" + "TYPE=4 powerpad\n" + "TYPE=5 four-score adapter", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "ntsc", 0, 0, UCON64_NTSC, + NULL, "specify TV standard is NTSC (UNIF only)", + &ucon64_wf[WF_OBJ_NES_SWITCH] + }, + { + "pal", 0, 0, UCON64_PAL, + NULL, "specify TV standard is PAL (UNIF only)", + &ucon64_wf[WF_OBJ_NES_SWITCH] + }, + { + "bat", 0, 0, UCON64_BAT, + NULL, "specify battery is present", + &ucon64_wf[WF_OBJ_NES_SWITCH] + }, + { + "nbat", 0, 0, UCON64_NBAT, + NULL, "specify battery is not present", + &ucon64_wf[WF_OBJ_NES_SWITCH] + }, + { + "vram", 0, 0, UCON64_VRAM, + NULL, "specify VRAM override (UNIF only)", + &ucon64_wf[WF_OBJ_NES_SWITCH] + }, + { + "nvram", 0, 0, UCON64_NVRAM, + NULL, "specify no VRAM override (UNIF only)", + &ucon64_wf[WF_OBJ_NES_SWITCH] + }, + { + "mirr", 1, 0, UCON64_MIRR, + "MTYPE", "specify mirroring type\n" + "MTYPE=0 horizontal mirroring\n" + "MTYPE=1 vertical mirroring\n" + "MTYPE=2 mirror all pages from $2000 (UNIF only)\n" + "MTYPE=3 mirror all pages from $2400 (UNIF only)\n" + "MTYPE=4 four screens of VRAM\n" + "MTYPE=5 mirroring controlled by mapper hardware (UNIF only)", + &ucon64_wf[WF_OBJ_NES_SWITCH] + }, +#if UNIF_REVISION > 7 + { + "cmnt", 1, 0, UCON64_CMNT, + "TEXT", "specify that TEXT should be used as comment (UNIF only)", + &ucon64_wf[WF_OBJ_NES_SWITCH] + }, +#endif + { + "dumpinfo", 1, 0, UCON64_DUMPINFO, + "FILE", "use dumper info from FILE when converting to UNIF", + &ucon64_wf[WF_OBJ_NES_SWITCH] + }, + { + "fds", 0, 0, UCON64_FDS, + NULL, "convert Famicom Disk System file (diskimage) from FAM to FDS", + &ucon64_wf[WF_OBJ_NES_DEFAULT] + }, + { + "fdsl", 0, 0, UCON64_FDSL, + NULL, "list Famicom Disk System/FDS (diskimage) contents", + &ucon64_wf[WF_OBJ_NES_DEFAULT] + }, +#if 0 + { + "fam", 0, 0, UCON64_FAM, + NULL, "convert Famicom Disk System file (diskimage) from FDS to FAM", + NULL + }, + { + "tr", 0, 0, UCON64_TR, + NULL, "truncate doubled PRG/CHR", + NULL + }, + { + "nfs", 0, 0, UCON64_NFS, + NULL, "convert NFS sound to WAV; " OPTION_LONG_S "rom=NFSFILE", + NULL + }, +#endif + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + +#if 0 +const char *nes_boardtypes = + { + "351258: UNROM\n" + "351298: UNROM\n" + "351908\n" + "352026: TLROM (w/ LS32 for VROM enable control)\n" + "51555: Acclaim, MMC3B mapper, PRG ROM, CHR ROM\n" + "53361\n" + "54425\n" + "55741\n" + "56504\n" + "AMROM: LS161, VRAM, PRG-ROM\n" + "ANROM: LS161+LS02 mapper, PRG-ROM, CHR-RAM\n" + "AOROM: LS161 mapper, PRG-ROM, CHR-ROM\n" + "BNROM: LS161, VRAM, PRG-ROM (Different LS161 bits? Only used on Deadly Towers)\n" + "CNROM: LS161 mapper, PRG-ROM, CHR-ROM?/CHR-RAM\n" + "COB: \"Glop Top\" style board\n" + "CPROM: LS04, LS08, LS161, 32K ROM, 16K VRAM (bankswitched, Videomation only)\n" + "DEIROM\n" + "DEROM\n" + "DRROM: MMC3, 4K of nametable RAM (for 4-screen), PRG-ROM, CHR-ROM (only in Gauntlet)\n" + "EKROM\n" + "ELROM: MMC5, PRG-ROM, CHR-ROM\n" + "ETROM: MMC5, PRG-ROM, CHR-ROM, 2x 8k optionnal RAM (battery)\n" + "EWROM: MMC5, PRG-ROM, CHR-ROM, 32k optionnal RAM (battery)\n" + "GNROM: LS161 mapper, PRG ROM, CHR ROM\n" + "HKROM: MMC6B, PRG-ROM, CHR-ROM, Battery\n" + "MHROM: LS161 mapper, black blob chips. Mario Bros / Duck Hunt multi\n" + "NES-B4: Same as TLROM\n" + "NES-BTR: Sunsoft FME7 mapper, PRG ROM, CHR ROM, 8k optionnal RAM \n" + "NES-QJ:\n" + "NES-RROM: Same as NROM (Only used in Clu Clu land)\n" + "NROM: No mapper, PRG-ROM, CHR-ROM\n" + "PNROM: MMC2, PRG-ROM, CHR-ROM\n" + "SAROM: MMC1B, PRG ROM, CHR ROM, optional 8k of RAM (battery)\n" + "SBROM: MMC1A, PRG ROM, CHR ROM (only 32K of CHR ROM)\n" + "SCEOROM\n" + "SC1ROM: MMC1B, PRG ROM, CHR ROM\n" + "SCROM: LS161, LS02, VRAM, PRG-ROM (Similar to UNROM)\n" + "SEROM: MMC1B, PRG ROM, CHR ROM\n" + "SFROM\n" + "SGROM: MMC1B, PRG ROM, 8k CHR RAM\n" + "SHROM\n" + "SJROM\n" + "SKROM: MMC1B, PRG ROM, CHR ROM, 8k optional RAM (battery)\n" + "SL1ROM: MMC3, PRG ROM, CHR ROM, LS32 (for 128K 28 pin CHR ROMs)\n" + "SL2ROM\n" + "SL3ROM\n" + "SLROM: MMC1A, PRG ROM, CHR ROM\n" + "SLRROM\n" + "SN1-ROM AW (Overlord only)\n" + "SNROM: MMC1A, PRG ROM, CHR ROM/RAM ?, 8k optional RAM (battery) \n" + "SOROM: MMC1B2, PRG ROM, VRAM, 16K of WRAM (Battery) Only 8K battery-backed\n" + "SVROM: MMC1B2, PRG ROM, VRAM, WRAM (Battery)\n" + "SUROM: MMC1B2, PRG ROM, CHR RAM/(ROM?), 8k battery-backed RAM (DW4?)\n" + "TEROM: MMC3A, PRG ROM, CHR ROM, (32k ROMs)\n" + "TFROM: MMC3B, PRG ROM, CHR ROM (64K of CHR only)\n" + "TGROM: MMC3C, PRG ROM, VRAM (512K of PRG)\n" + "TKROM: MMC3A, PRG ROM, CHR ROM, 8k optional RAM (battery)\n" + "TL1ROM: Same as TLROM\n" + "TLROM: MMC3B, PRG ROM, CHR ROM\n" + "TLSROM: Same as TLROM\n" + "TQROM: MMC3B+74HC32, PRG ROM, CHR ROM + 8k of CHR-RAM\n" + "TSROM: MMC3A, PRG ROM, CHR ROM, 8k optionnal RAM\n" + "TVROM: MMC3B, PRG ROM, CHR ROM, 4K of Nametable RAM (4-screen)\n" + "UNROM: 74LS32+74LS161 mapper, 128k PRG, 8k CHR-RAM\n" + "UOROM\n" + }; +#endif + +#define NES_MAKER_MAX 203 +static const char *nes_maker[NES_MAKER_MAX] = + { + NULL, "A-Wave", "ASCII", "Absolute Entertainment", "Acclaim", + "Active Enterprises", "Activision", "Activision/Bullet Proof", "Akujin", "Altron", + "American Game Carts", "American Sammy Corp.", "American Sammy", "American Softworks", + "American Technos", + "American Video", "Angel", "Arcadia Systems", "Asder", "Ask Koudansha", + "Asmik", "Athena", "Atlus", "BPS", "Bandai", + "Banpresto", "Beam Software", "Beam Software/Mattel", "Bothtec", "Broderbund", + "Bullet-Proof", "Bunch Games", "CBS Sony Group", "Camerica", "Capcom", + "Cart-Saint", "Casady", "Character Soft", "Chris Covell", "Coconuts Japan", + "Color Dreams", "Cony", "Crush Productions", "Culture Brain", "DB Soft", + "Darknight13", "Data East", "Disconnected Translations", "Dragoon-X", "EAV", + "Electro Brain", "Electronic Arts", "Elite", "Enix", "Epic-Sony Records", + "Epoch Yashiro", "Eurocom", "FCI", "FCI/Pony Canyon", "Face", + "Fuji Television", "G.O 1", "Gaijin Productions", "Gakushuu Kenkyuusha", "Galoob", + "GameTek", "Grimlick", "Hal America Inc.", "Hal America", "Hal Kenkyuujo", + "Hal", "Halken", "Hector", "Hero", "Hi Tech Expressions", + "High Score Media Work", "Hot B", "Hudson Soft", "Human", "HummingBird Soft", + "I'Max", "IGS", "INTV", "Ian Bell", "Imagineer", + "Induction Produce", "Infocom", "Infogrames", "Interplay", "Irem", + "J2E Translations", "JVC", "Jaleco", "K Amusement Lease", "Kakenhi", + "Kasady", "Kawada", "Kemco", "King Records", "Koei", + "Konami", "Kotobuki System", "Kyuugo Boueki", "LHN", "LJN", + "Lucasfilm Games", "M&M", "Manipulate", "Masiya", "Matchbox", + "Mattel", "Meldac", "Memblers", "Microprose", "Milton Bradley", + "Mindscape", "NTVIC", "Namco", "Namcot", "Nameo", + "Natsume", "Naxat", "Neo Demiforce", "Nexoft", "Nihon Bussan", + "Nihon Computer System", "Nintendo", "Ocean", "Pack-In Video", "Palcom", + "Panesian", "Parker Brothers", "Pixel", "Pony Canyon", "Pope Hentai", + "Proto", "Quest", "RCM Group", "RKH Translation", "RPGe", + "Rage Games", "Rare Ltd.", "Romstar", "SNK", "Sammy", + "Sanritsu Denki", "Seta USA", "Seta", "Sigma Shouji", "Sigma", + "Sofel", "Soft Pro", "Software Toolworks", "Sony Imagesoft", "Square Soft", + "Square", "Sunsoft", "SuperVision", "T*HQ", "TSS", + "Taito", "Takara", "Takeru", "Taxan", "Technos Japan", + "Tecmo", "Tengen", "Tengen/Mindscape", "Tennessee Carmel-Veilleux", "The Mad Hacker", + "The Sales Curve", "The Spoony Bard", "Titus Software", "Toho", "Tokuma Shoten", + "Tomy", "Tonkin House", "Tony Young", "Touei Douga", "Toukyou Shoseki", + "Toushiba EMI", "Towa Chiki", "Tradewest", "TransBRC", "Triffix", + "Tsang Hai", "UBI Soft", "UPL", "Ultra Games", "Ultra Games/Konami", + "Use", "Vap", "Varie", "Vic Tokai", "Victor Musical Industries", + "Victor Ongaku Sangyou", "Virgin Games", "Visco", "Wakd Hacks", "Will B.", + "Wisdom Tree", "Yonezawa", "Yutaka" + }; + +#define NES_COUNTRY_MAX 4 +static const char *nes_country[NES_COUNTRY_MAX] = + { + "Japan", + "U.S.A.", + "Europe, Oceania & Asia", // Australia is part of Oceania + NULL + }; + +typedef struct +{ + uint32_t crc32; + uint8_t maker; + uint8_t country; + uint16_t date; +} st_nes_data_t; + +static const st_nes_data_t nes_data[] = +{ + {0x00161afd, 195, 0, 0}, + {0x0021ed29, 133, 0, 0}, + {0x003a1bd1, 133, 0, 0}, + {0x004e5381, 117, 0, 0}, + {0x005682d5, 63, 0, 0}, + {0x005ff9a8, 0, 1, 0}, + {0x00837960, 100, 1, 692}, + {0x0091afbe, 92, 0, 0}, + {0x009af6be, 65, 1, 390}, + {0x009da4c0, 81, 0, 0}, + {0x00ad1189, 129, 2, 0}, + {0x00b82d5c, 166, 1, 0}, + {0x00bec4d7, 104, 1, 1092}, + {0x00e95d86, 186, 1, 1193}, + {0x00f0fbbc, 126, 0, 0}, + {0x011adaa1, 140, 0, 0}, + {0x0123bffe, 196, 1, 793}, + {0x0147766c, 143, 0, 86}, + {0x014a755a, 126, 2, 0}, + {0x0154c43f, 193, 0, 0}, + {0x016c93d8, 77, 0, 0}, + {0x018a8699, 104, 1, 1091}, + {0x01901d84, 95, 0, 0}, + {0x01934171, 0, 1, 0}, + {0x01a1509a, 131, 1, 690}, + {0x01b418c5, 77, 1, 988}, + {0x01b4ca89, 154, 1, 989}, + {0x01be8b6f, 0, 1, 0}, + {0x01e06ab1, 46, 0, 0}, + {0x01ee0720, 40, 1, 0}, + {0x021193b2, 53, 0, 0}, + {0x022589b9, 165, 0, 0}, + {0x02361407, 179, 0, 0}, + {0x023a5a32, 126, 1, 1085}, + {0x023c7774, 46, 1, 88}, + {0x02589598, 155, 0, 0}, + {0x026a41ee, 4, 1, 992}, + {0x026c5fca, 24, 0, 0}, + {0x026e41c5, 115, 1, 790}, + {0x02738c68, 117, 0, 0}, + {0x02863604, 178, 0, 0}, + {0x028912ba, 166, 1, 0}, + {0x02931525, 34, 1, 690}, + {0x0296e5f4, 0, 1, 0}, + {0x02a7ad0f, 195, 0, 0}, + {0x02b0b405, 92, 0, 0}, + {0x02b20ca7, 191, 0, 0}, + {0x02b26e69, 64, 0, 0}, + {0x02b9e7c2, 24, 1, 689}, + {0x02cc3973, 24, 1, 1086}, + {0x02cceca7, 44, 0, 0}, + {0x02d7976b, 2, 0, 0}, + {0x02e0ada4, 92, 2, 91}, // Totally Rad (E) + {0x02e20d38, 0, 1, 0}, + {0x02e67223, 143, 1, 87}, + {0x02ed6298, 166, 1, 0}, + {0x02ee3706, 34, 1, 789}, + {0x03272e9b, 193, 1, 191}, + {0x03349045, 0, 1, 0}, + {0x0336f9f3, 146, 1, 89}, + {0x035cc54b, 92, 0, 0}, + {0x035dc2e9, 126, 1, 1085}, + {0x0364c3ec, 91, 1, 690}, + {0x0373a432, 66, 0, 0}, + {0x03840ec5, 142, 1, 1289}, + {0x039b4a9c, 156, 0, 0}, + {0x03d56cf7, 84, 0, 0}, + {0x03dcfddb, 72, 0, 0}, + {0x03e2898f, 126, 0, 0}, + {0x03ec46af, 156, 1, 1291}, + {0x03eeaeb7, 131, 1, 591}, + {0x03f899cd, 74, 1, 191}, + {0x03fb57b6, 111, 1, 991}, + {0x040c8685, 126, 1, 387}, + {0x04109355, 89, 0, 0}, + {0x04142764, 52, 2, 0}, + {0x042c6f99, 153, 1, 993}, + {0x042e9c4d, 12, 1, 990}, + {0x042f17c4, 2, 0, 0}, + {0x045e8cd8, 128, 0, 0}, + {0x04766130, 97, 1, 1092}, + {0x049325d9, 18, 0, 0}, + {0x04977e62, 156, 0, 0}, + {0x04be2206, 77, 0, 0}, + {0x04d0d47c, 126, 0, 0}, + {0x04d6b4f6, 97, 0, 0}, + {0x04f3354d, 104, 1, 289}, + {0x0504b007, 126, 1, 1085}, + {0x0516375e, 126, 1, 1085}, + {0x051cd5f2, 39, 0, 0}, + {0x0537322a, 4, 1, 890}, + {0x05378607, 92, 1, 391}, + {0x053c4699, 20, 0, 0}, + {0x0546bd12, 173, 0, 0}, + {0x054cb4eb, 92, 1, 390}, + {0x0556dc12, 161, 0, 0}, + {0x055fb6e5, 0, 1, 0}, + {0x0573f281, 155, 0, 0}, + {0x058f23a2, 89, 1, 790}, + {0x059e0cdf, 50, 1, 1191}, + {0x05a688c8, 150, 1, 1089}, + {0x05b05500, 0, 1, 0}, + {0x05b3f461, 173, 1, 790}, + {0x05b7b507, 0, 1, 0}, + {0x05c97f64, 182, 0, 0}, + {0x05ce560c, 24, 1, 192}, + {0x05cf9eb0, 34, 0, 0}, + {0x05d45a1a, 95, 0, 0}, + {0x05d70600, 166, 1, 0}, + {0x05de5afd, 193, 1, 690}, + {0x05f04eac, 160, 0, 0}, + {0x05f76a57, 0, 1, 0}, + {0x05fe773b, 182, 1, 489}, + {0x06093b5e, 34, 0, 0}, + {0x060f6e75, 77, 0, 0}, + {0x06144b4a, 89, 0, 0}, + {0x062ec2b9, 24, 0, 0}, + {0x0630c234, 200, 1, 0}, + {0x0639e88e, 0, 1, 0}, + {0x063e5653, 127, 1, 893}, + {0x066f5a83, 116, 1, 190}, + {0x067bd24c, 99, 0, 0}, + {0x06961be4, 51, 1, 990}, + {0x06a4345c, 115, 1, 790}, + {0x06b0556c, 77, 1, 992}, + {0x06bb007a, 156, 0, 0}, + {0x06c4fd92, 51, 1, 990}, + {0x06cae67f, 100, 1, 192}, + {0x06d72c83, 163, 1, 489}, + {0x06e97649, 1, 0, 0}, + {0x06f05358, 133, 0, 0}, + {0x06f9c714, 92, 0, 0}, + {0x0719260c, 72, 0, 0}, + {0x071f2e80, 57, 1, 490}, + {0x07259ba7, 97, 0, 0}, + {0x07299793, 126, 1, 1093}, + {0x072b8659, 132, 0, 0}, + {0x0736e382, 46, 1, 1190}, + {0x074ec424, 43, 0, 0}, + {0x075a69e6, 160, 2, 0}, + {0x076b00ab, 120, 0, 0}, + {0x07838ef9, 126, 0, 0}, + {0x07854b3f, 126, 1, 1088}, + {0x0786471d, 120, 0, 0}, + {0x078ced30, 43, 0, 0}, + {0x0794f2a5, 53, 0, 0}, + {0x07964c82, 90, 0, 0}, + {0x07977186, 43, 0, 0}, + {0x07af9301, 46, 0, 0}, + {0x07dbd082, 74, 1, 191}, + {0x07e20334, 115, 1, 993}, + {0x07ee6d8f, 160, 0, 0}, + {0x08077383, 165, 1, 787}, + {0x082d63c7, 100, 0, 0}, + {0x083c02c1, 0, 1, 0}, + {0x083e4fc1, 129, 2, 0}, + {0x0847c623, 74, 1, 189}, + {0x0857df48, 166, 1, 0}, + {0x085de7c9, 6, 1, 192}, + {0x08626711, 93, 0, 0}, + {0x0897021b, 156, 0, 0}, + {0x08b04364, 100, 0, 0}, + {0x08e2af80, 127, 1, 91}, + {0x08ebde64, 15, 1, 0}, + {0x0902c8f0, 97, 0, 0}, + {0x0905e0d5, 39, 0, 0}, + {0x091ed5a9, 32, 0, 0}, + {0x092b5371, 24, 1, 1086}, + {0x092ec15c, 179, 0, 0}, + {0x093311aa, 166, 1, 0}, + {0x0939852f, 115, 1, 990}, + {0x093e845f, 124, 0, 0}, + {0x09499f4d, 24, 0, 0}, + {0x094afab5, 156, 0, 0}, + {0x0971cc4f, 100, 0, 0}, + {0x0973f714, 195, 0, 0}, + {0x09874777, 114, 1, 389}, + {0x098c672a, 117, 0, 0}, + {0x09948780, 100, 0, 0}, + {0x099b8caa, 120, 0, 0}, + {0x09a6ff02, 34, 0, 0}, + {0x09ae2e75, 89, 0, 0}, + {0x09c083b7, 100, 1, 689}, + {0x09c1fc7d, 34, 0, 0}, + {0x09c31cd4, 31, 1, 0}, + {0x09e4c3e0, 34, 2, 0}, + {0x09eefde3, 165, 0, 0}, + {0x09efe54b, 174, 0, 0}, + {0x09ffdf45, 77, 0, 0}, + {0x0a0926bd, 109, 1, 192}, + {0x0a1bcd91, 133, 0, 0}, + {0x0a2490af, 126, 1, 387}, + {0x0a42d84f, 76, 0, 0}, + {0x0a640eea, 24, 0, 0}, + {0x0a68452e, 43, 0, 0}, + {0x0a686042, 46, 1, 491}, + {0x0a73a792, 181, 0, 0}, + {0x0a7767eb, 16, 0, 0}, + {0x0a7e62d4, 113, 1, 1292}, + {0x0a866c94, 100, 1, 891}, + {0x0ab06c51, 68, 1, 1088}, + {0x0ab26db6, 200, 1, 0}, + {0x0ab39900, 174, 0, 0}, + {0x0abdd5ca, 23, 0, 0}, + {0x0ac1aa8f, 100, 1, 587}, + {0x0ac631ba, 100, 1, 89}, + {0x0ac65c40, 100, 0, 0}, + {0x0ae3cc5e, 46, 0, 0}, + {0x0ae6c9e2, 184, 1, 691}, + {0x0af937d1, 160, 1, 1291}, + {0x0afb395e, 100, 0, 0}, + {0x0b0d4d1b, 2, 0, 0}, + {0x0b3d7b44, 165, 0, 0}, + {0x0b404915, 115, 1, 991}, + {0x0b4c197a, 92, 0, 0}, + {0x0b4c2ddc, 117, 0, 0}, + {0x0b561ad2, 72, 0, 0}, + {0x0b5667e9, 126, 2, 0}, + {0x0b57cfda, 0, 1, 0}, + {0x0b58880c, 126, 0, 0}, + {0x0b6443d4, 195, 0, 0}, + {0x0b65a917, 126, 0, 0}, + {0x0b7f1947, 77, 0, 0}, + {0x0b8e8649, 43, 0, 0}, + {0x0b97b2af, 126, 1, 1085}, + {0x0b9fa342, 126, 1, 1088}, + {0x0baf01d0, 160, 0, 0}, + {0x0bb5b3a0, 21, 0, 0}, + {0x0bbacf8f, 100, 0, 0}, + {0x0bbd85ff, 100, 0, 0}, + {0x0bbf80cb, 160, 0, 0}, + {0x0bc73114, 156, 0, 0}, + {0x0bcaa4d7, 113, 1, 292}, + {0x0bdd8dd9, 65, 1, 690}, + {0x0bdf73aa, 66, 0, 0}, + {0x0be0a328, 24, 0, 0}, + {0x0be230b7, 46, 1, 1291}, + {0x0bf306d1, 164, 0, 0}, + {0x0bf31a3d, 16, 0, 0}, + {0x0c1792da, 117, 0, 0}, + {0x0c187747, 89, 0, 0}, + {0x0c198d4f, 43, 0, 0}, + {0x0c2e7863, 115, 1, 1290}, + {0x0c401790, 77, 0, 0}, + {0x0c47946d, 117, 0, 0}, + {0x0c4b76ec, 181, 0, 0}, + {0x0c5a6297, 156, 0, 0}, + {0x0c5c353a, 77, 0, 0}, + {0x0c5e6280, 157, 0, 0}, + {0x0c5f40a6, 100, 1, 0}, + {0x0c6c71a3, 100, 0, 0}, + {0x0c90a3cd, 143, 0, 87}, + {0x0c935dfe, 63, 0, 0}, + {0x0cabbf0f, 120, 0, 0}, + {0x0caf2f15, 124, 0, 0}, + {0x0cb8d92d, 97, 0, 0}, + {0x0cc9ffec, 100, 0, 0}, + {0x0ccd28d5, 188, 1, 689}, + {0x0cd79b71, 2, 0, 0}, + {0x0d3482d7, 160, 0, 0}, + {0x0d365112, 164, 0, 0}, + {0x0d44bacc, 43, 0, 0}, + {0x0d52e2e6, 69, 0, 0}, + {0x0d65e7c7, 156, 0, 0}, + {0x0d6b2b30, 51, 1, 990}, + {0x0d6cf078, 164, 0, 0}, + {0x0d86628e, 96, 0, 0}, + {0x0d8a30a2, 100, 0, 0}, + {0x0d9f5bd1, 92, 1, 990}, + {0x0da00298, 69, 0, 0}, + {0x0daf8a80, 100, 1, 0}, + {0x0db4b382, 92, 0, 0}, + {0x0dc53188, 98, 0, 0}, + {0x0e0c4221, 156, 2, 0}, + {0x0e1683c5, 160, 0, 0}, + {0x0e2ae6e1, 127, 1, 893}, + {0x0e8c28f9, 38, 0, 0}, + {0x0e8cfd62, 59, 0, 0}, + {0x0e997cf6, 76, 0, 0}, + {0x0eaa7515, 92, 0, 0}, + {0x0eb63e83, 0, 1, 0}, + {0x0ebf76e2, 117, 0, 0}, + {0x0ec6c023, 99, 1, 392}, + {0x0ed96f42, 156, 1, 1090}, + {0x0ef730e7, 12, 1, 190}, + {0x0efde8c0, 97, 0, 0}, + {0x0f0d64b7, 9, 0, 0}, + {0x0f0f9c73, 15, 1, 0}, + {0x0f141525, 160, 0, 0}, + {0x0f1c3afc, 34, 1, 787}, + {0x0f1cc048, 182, 1, 688}, + {0x0f4ff4a0, 122, 0, 0}, + {0x0f5410e3, 160, 0, 0}, + {0x0f5f1f86, 193, 0, 0}, + {0x0f6c01e3, 160, 1, 887}, + {0x0f86feb4, 173, 1, 591}, + {0x0fae316f, 117, 0, 0}, + {0x0fb244c8, 20, 0, 0}, + {0x0fb6032a, 97, 0, 0}, + {0x0fc72a80, 158, 1, 191}, + {0x0fc8e9b7, 97, 0, 0}, + {0x0fcfc04d, 34, 1, 689}, + {0x0fd5aeeb, 40, 1, 0}, + {0x0fd6bfc8, 197, 0, 0}, + {0x0fe4fc74, 104, 1, 292}, + {0x0fec90d2, 34, 1, 787}, + {0x0ff6a3b5, 156, 0, 0}, + {0x0ffd2cb9, 34, 1, 1092}, + {0x0ffde258, 166, 1, 0}, + {0x10092781, 0, 1, 0}, + {0x10119e6b, 156, 0, 0}, + {0x10180072, 43, 1, 291}, + {0x1027c432, 77, 0, 0}, + {0x102823a7, 92, 1, 190}, + {0x1028fc27, 99, 0, 0}, + {0x10327e0e, 176, 0, 0}, + {0x103e7e7f, 34, 1, 1289}, + {0x106af52a, 180, 0, 0}, + {0x109f1acc, 133, 0, 0}, + {0x10b0f8b0, 160, 0, 0}, + {0x10baeef3, 34, 0, 0}, + {0x10bb8f9a, 24, 0, 0}, + {0x10bbd4ba, 126, 2, 0}, + {0x10c8f2fa, 117, 0, 0}, + {0x10c9a789, 117, 0, 0}, + {0x10d20915, 6, 1, 490}, + {0x112140a4, 100, 0, 0}, + {0x11469ce3, 54, 0, 0}, + {0x116ccd44, 32, 0, 0}, + {0x1170392a, 202, 0, 0}, + {0x119dacf3, 99, 0, 0}, + {0x11b038fe, 117, 0, 0}, + {0x11c2e664, 21, 0, 0}, + {0x11c48002, 0, 1, 0}, + {0x11d08cc6, 40, 1, 0}, + {0x11dc4071, 158, 1, 91}, + {0x11eaad26, 99, 1, 991}, + {0x11fc8686, 156, 0, 0}, + {0x1202414b, 160, 0, 0}, + {0x12078afd, 77, 1, 1090}, + {0x1208e754, 84, 0, 0}, + {0x1221f97c, 66, 0, 0}, + {0x1248326d, 97, 0, 0}, + {0x124af039, 31, 1, 0}, + {0x1253ea03, 34, 1, 390}, + {0x1255036f, 182, 1, 791}, + {0x126ea4a0, 121, 0, 0}, + {0x126ebf66, 97, 1, 990}, + {0x127436fc, 15, 1, 0}, + {0x12748678, 115, 1, 1090}, + {0x12906664, 123, 1, 989}, + {0x1294ab5a, 117, 0, 0}, + {0x12b2c361, 104, 1, 989}, + {0x12bba335, 49, 0, 0}, + {0x12c6d5c7, 34, 1, 1088}, + {0x12c90991, 143, 0, 0}, + {0x12ee543e, 160, 0, 0}, + {0x12f048df, 92, 0, 0}, + {0x12f405cf, 143, 1, 291}, + {0x1300a8b7, 117, 0, 0}, + {0x131a18cb, 44, 0, 0}, + {0x13332bfa, 126, 1, 1085}, + {0x1335cb05, 143, 1, 790}, + {0x134c1a50, 165, 0, 0}, + {0x1352f1b9, 196, 1, 792}, + {0x1353a134, 129, 2, 0}, + {0x13556927, 69, 0, 0}, + {0x135adf7c, 117, 0, 0}, + {0x137b27d8, 77, 0, 0}, + {0x1388aeb9, 160, 1, 589}, + {0x1394e1a2, 160, 0, 0}, + {0x139b07db, 34, 1, 990}, + {0x139b15ba, 61, 0, 0}, + {0x139eb5b5, 166, 1, 0}, + {0x13be9a74, 46, 1, 1091}, + {0x13c6617e, 156, 1, 290}, + {0x13cf3d5d, 127, 1, 191}, + {0x13d5b1a4, 114, 1, 990}, + {0x13da2122, 117, 0, 0}, + {0x13dd7461, 160, 0, 0}, + {0x13e9b20a, 160, 1, 0}, + {0x13ec1c90, 117, 0, 0}, + {0x14105c13, 200, 1, 0}, + {0x1411005b, 164, 0, 0}, + {0x14238d61, 163, 1, 489}, + {0x1425d7f4, 12, 1, 690}, + {0x142f7f3f, 92, 0, 0}, + {0x144a9380, 113, 1, 292}, + {0x144ca9e5, 182, 2, 0}, + {0x144d31cd, 92, 0, 0}, + {0x145a9a6c, 126, 0, 0}, + {0x1461d1f8, 41, 0, 0}, + {0x146fb9c3, 24, 0, 0}, + {0x14942c06, 117, 0, 0}, + {0x1495414c, 150, 0, 0}, + {0x149c0ec3, 80, 0, 0}, + {0x14a01c70, 144, 0, 0}, + {0x14a45522, 164, 0, 0}, + {0x14a81635, 31, 1, 0}, + {0x14cbb22a, 126, 1, 686}, + {0x14cd576e, 120, 0, 0}, + {0x1500e835, 160, 0, 0}, + {0x1512e0b1, 126, 0, 0}, + {0x15141401, 20, 0, 0}, + {0x15249a59, 117, 0, 0}, + {0x153cdc91, 104, 1, 990}, + {0x153dd212, 158, 1, 1292}, + {0x154a31b6, 118, 0, 86}, + {0x1554fd9d, 143, 1, 689}, + {0x15662081, 77, 0, 0}, + {0x156c75ff, 143, 1, 0}, // "Proto" + {0x158fc675, 24, 0, 0}, + {0x15be6e58, 117, 0, 0}, + {0x15e9d230, 126, 1, 1085}, + {0x15f0d3f1, 158, 1, 191}, + {0x15fe6d0f, 99, 1, 1290}, + {0x16192bf2, 12, 1, 1191}, + {0x161d717b, 46, 1, 790}, + {0x16342ad0, 178, 0, 0}, + {0x16388a44, 126, 1, 0}, + {0x163a5eda, 0, 0, 93}, // Batman 3 [p1] + {0x163e86c0, 126, 1, 1189}, + {0x163eccae, 100, 0, 0}, + {0x1644c162, 39, 0, 0}, + {0x16a0a3a3, 34, 0, 0}, + {0x16aa4e2d, 126, 0, 0}, + {0x16d10b24, 2, 0, 0}, + {0x16d55a59, 165, 1, 289}, + {0x16e93f39, 54, 0, 0}, + {0x16eba50a, 188, 1, 292}, + {0x170250de, 24, 0, 0}, + {0x170739cf, 128, 0, 0}, + {0x171251e3, 34, 1, 1186}, + {0x1718473c, 188, 1, 292}, + {0x172ad0f5, 160, 0, 0}, + {0x17421900, 117, 0, 0}, + {0x175c4a3c, 92, 0, 0}, + {0x175e7161, 46, 1, 1090}, + {0x175eda0b, 197, 0, 0}, + {0x17627d4b, 160, 0, 0}, + {0x1771ea8f, 24, 1, 787}, + {0x179a0d57, 165, 1, 1291}, + {0x17ab5897, 31, 1, 0}, + {0x17ac5303, 191, 0, 0}, + {0x17b49292, 72, 0, 0}, + {0x17c111e0, 76, 1, 1190}, + {0x17f72ec8, 126, 1, 990}, + {0x1808061a, 89, 1, 987}, + {0x1814ce64, 38, 0, 0}, + {0x181ec6b4, 195, 0, 0}, + {0x1826e6d2, 46, 1, 90}, + {0x1829616a, 174, 0, 0}, + {0x182f09d8, 4, 1, 93}, + {0x183859d2, 24, 0, 0}, + {0x184c2124, 99, 0, 0}, + {0x185633d5, 84, 0, 0}, + {0x18a885b0, 34, 0, 0}, + {0x18a9f0d9, 142, 1, 792}, + {0x18b249e5, 74, 1, 0}, + {0x18ba5ac0, 165, 1, 787}, + {0x18bd1afb, 158, 1, 1292}, + {0x18c0623b, 117, 0, 0}, + {0x18c64981, 160, 2, 0}, + {0x18d44bba, 84, 0, 0}, + {0x18d5af7a, 24, 1, 988}, + {0x190439b9, 46, 0, 0}, + {0x190a3e11, 46, 0, 0}, + {0x1924f963, 143, 1, 89}, + {0x192d546f, 46, 1, 1289}, + {0x19411bd7, 133, 0, 0}, + {0x194942db, 40, 1, 0}, + {0x194f9844, 77, 1, 194}, + {0x19555d39, 6, 1, 1088}, + {0x196f05dd, 174, 0, 0}, + {0x1973aea8, 65, 1, 1091}, + {0x198237c5, 117, 0, 0}, + {0x198b4e3b, 0, 1, 0}, + {0x198c2f41, 126, 1, 1090}, + {0x198e279a, 65, 1, 390}, + {0x1992d163, 68, 1, 390}, + {0x1995ac4e, 39, 0, 0}, + {0x19b1a2e7, 97, 0, 0}, + {0x19cc71d2, 89, 0, 0}, + {0x19df419a, 126, 1, 1085}, + {0x1a2d3b17, 40, 1, 0}, + {0x1a2ea6b9, 39, 0, 0}, + {0x1a39343c, 151, 0, 0}, + {0x1a3f0a61, 15, 1, 0}, + {0x1a3f1f1c, 191, 0, 0}, + {0x1a6693db, 166, 1, 0}, + {0x1aa0479c, 100, 0, 0}, + {0x1aa0dbaf, 166, 1, 0}, + {0x1abb9511, 126, 1, 1090}, + {0x1ac701b5, 34, 0, 0}, + {0x1ae527f1, 197, 0, 0}, + {0x1ae7b933, 110, 1, 989}, + {0x1af0ec24, 187, 0, 0}, + {0x1af80f18, 0, 1, 0}, + {0x1afb26d7, 126, 1, 1294}, + {0x1b2bad13, 0, 1, 0}, + {0x1b2f3545, 173, 1, 1089}, + {0x1b3a3db3, 91, 1, 392}, + {0x1b45a73e, 92, 0, 0}, + {0x1b71ccdb, 115, 1, 990}, + {0x1b7bd879, 92, 0, 0}, + {0x1b932bea, 34, 2, 0}, + {0x1bc686a8, 33, 1, 0}, + {0x1bd39032, 0, 1, 0}, + {0x1bebf407, 160, 1, 1092}, + {0x1c059dfb, 0, 1, 0}, + {0x1c212e9d, 0, 2, 0}, + {0x1c238ad9, 142, 1, 792}, + {0x1c2a58ff, 192, 0, 0}, + {0x1c309d0a, 61, 0, 0}, + {0x1c3a73f0, 25, 0, 0}, + {0x1c4f2651, 137, 1, 90}, + {0x1c66baf6, 133, 0, 0}, + {0x1c6ba3b2, 126, 0, 0}, + {0x1c6f3036, 114, 1, 88}, + {0x1c779cd7, 15, 1, 0}, + {0x1caae58d, 160, 0, 0}, + {0x1cac1791, 69, 0, 0}, + {0x1cba7eb4, 126, 1, 493}, + {0x1cc26e7e, 100, 0, 0}, + {0x1ccba824, 0, 1, 0}, + {0x1ced086f, 99, 0, 0}, + {0x1cee0c21, 114, 1, 1290}, + {0x1d0f4d6b, 76, 1, 989}, + {0x1d17ad3b, 45, 0, 0}, + {0x1d20a5c6, 6, 1, 291}, + {0x1d27f817, 60, 0, 0}, + {0x1d2d93ff, 163, 1, 191}, + {0x1d2e5018, 34, 0, 0}, + {0x1d41cc8c, 188, 1, 289}, + {0x1d4a46a4, 74, 1, 292}, + {0x1d5b03a5, 100, 1, 988}, + {0x1d5fe853, 69, 0, 0}, + {0x1d62f13f, 96, 0, 0}, + {0x1d6deccc, 24, 1, 591}, + {0x1d873633, 15, 1, 0}, + {0x1d8b2f59, 43, 0, 0}, + {0x1d8bf724, 192, 0, 0}, + {0x1dac6e97, 69, 0, 0}, + {0x1db07c0d, 24, 1, 988}, + {0x1dbd1d2b, 2, 0, 0}, + {0x1dc0f740, 117, 0, 0}, + {0x1dd6619b, 117, 0, 0}, + {0x1dea55eb, 39, 0, 0}, + {0x1df00462, 100, 0, 0}, + {0x1e0a01ea, 92, 1, 1292}, + {0x1e0c7ea3, 133, 0, 0}, + {0x1e1fbd63, 68, 1, 489}, + {0x1e2cb8cc, 24, 0, 0}, + {0x1e2f89c8, 190, 0, 0}, + {0x1e3685d0, 126, 1, 1085}, + {0x1e438d52, 100, 0, 0}, + {0x1e4afd90, 117, 0, 0}, + {0x1e4d3831, 126, 0, 0}, + {0x1e598a14, 126, 1, 290}, + {0x1e5fdfa3, 165, 1, 1187}, + {0x1e7ff743, 66, 0, 0}, + {0x1e9ebb00, 76, 0, 0}, + {0x1ea0052e, 77, 0, 0}, + {0x1ea703cb, 92, 0, 0}, + {0x1eaf333c, 0, 1, 0}, + {0x1eb4a920, 15, 1, 0}, + {0x1ebb5b42, 77, 1, 293}, + {0x1ec1bf08, 66, 0, 0}, + {0x1ed48c5c, 197, 0, 0}, + {0x1ed5c801, 126, 2, 0}, + {0x1edadc3d, 0, 1, 0}, + {0x1efcac48, 34, 1, 9}, + {0x1f0d03f8, 160, 2, 0}, + {0x1f2f4861, 81, 0, 0}, + {0x1f3d2cdf, 0, 1, 0}, + {0x1f59ef24, 156, 0, 0}, + {0x1f5f88dd, 40, 1, 0}, + {0x1f6ea423, 43, 1, 390}, + {0x1f818018, 100, 0, 0}, + {0x1f864492, 92, 0, 0}, + {0x1fa8c4a4, 97, 1, 1288}, + {0x1fc5740d, 24, 1, 689}, + {0x1fdc664c, 117, 0, 0}, + {0x1ff7fc0d, 100, 1, 992}, + {0x2001f0ed, 100, 0, 0}, + {0x20028703, 12, 1, 190}, + {0x2015226d, 151, 0, 0}, + {0x2019fe65, 156, 0, 0}, + {0x202d70f8, 92, 1, 391}, + {0x202df297, 160, 0, 0}, + {0x203d32ab, 92, 0, 0}, + {0x203d32b5, 166, 1, 0}, + {0x2046c4e9, 92, 1, 391}, + {0x2055971a, 152, 1, 793}, + {0x20574509, 74, 1, 293}, + {0x2061772a, 46, 0, 0}, + {0x2063f8be, 191, 0, 0}, + {0x2078dce4, 126, 0, 0}, + {0x207e618c, 156, 0, 0}, + {0x209b4bed, 100, 0, 0}, + {0x209f3587, 127, 1, 191}, + {0x20a5219b, 20, 1, 1190}, + {0x20a9e4a2, 3, 1, 89}, + {0x20af7e1a, 22, 0, 0}, + {0x20b04883, 123, 1, 691}, + {0x20c5d187, 161, 0, 0}, + {0x20cc079d, 126, 0, 0}, + {0x20d22251, 192, 0, 0}, + {0x20e781c2, 165, 1, 1192}, + {0x20fc87a0, 165, 1, 787}, + {0x212b4ba0, 74, 1, 0}, + {0x213cb3fb, 164, 0, 0}, + {0x21490e20, 120, 0, 0}, + {0x219ce59d, 160, 0, 0}, + {0x219dfabf, 187, 0, 0}, + {0x21a3f190, 188, 1, 192}, + {0x21a5a053, 179, 0, 0}, + {0x21b099f3, 126, 1, 1085}, + {0x21d7517a, 6, 1, 1089}, + {0x21dd2174, 2, 0, 0}, + {0x21ddbc1c, 66, 0, 0}, + {0x21ebe345, 133, 0, 0}, + {0x21f2a1a6, 104, 2, 0}, + {0x21f85681, 100, 0, 0}, + {0x21f8c4ab, 156, 0, 0}, + {0x2220e14a, 3, 1, 1191}, + {0x2224b048, 157, 0, 0}, + {0x2225c20f, 99, 1, 190}, + {0x22276a55, 0, 1, 0}, + {0x227cf577, 158, 1, 1292}, + {0x228fc66e, 199, 0, 0}, + {0x22d6d5bd, 117, 0, 0}, + {0x22f1ca9e, 165, 0, 0}, + {0x22f95ff1, 46, 0, 0}, + {0x23040fc4, 126, 1, 1289}, + {0x2305b528, 77, 0, 0}, + {0x2328046e, 0, 1, 0}, + {0x23317897, 100, 1, 992}, + {0x23386b90, 41, 0, 0}, + {0x236cf56c, 95, 0, 0}, + {0x2370c0a9, 74, 1, 293}, + {0x2389a6b3, 124, 0, 0}, + {0x238e1848, 160, 1, 887}, + {0x23a60a62, 126, 1, 1085}, + {0x23b719c1, 95, 0, 0}, + {0x23bf0507, 193, 0, 0}, + {0x23c3fb2d, 160, 1, 790}, + {0x23d17f5e, 100, 1, 891}, + {0x23d7d48f, 182, 2, 0}, + {0x23d809af, 92, 0, 0}, + {0x23e6d471, 160, 0, 0}, + {0x23e9c736, 178, 0, 0}, + {0x23f4b48f, 34, 0, 0}, + {0x23ff7365, 0, 1, 0}, + {0x240c6de8, 174, 0, 0}, + {0x240de736, 91, 1, 392}, + {0x242a270c, 104, 1, 1187}, + {0x243a8735, 89, 0, 0}, + {0x24428a5d, 154, 1, 989}, + {0x2447e03b, 117, 0, 0}, + {0x245870ed, 124, 0, 0}, + {0x24598791, 126, 1, 1085}, + {0x245ab38b, 0, 1, 0}, + {0x245fd217, 114, 1, 390}, + {0x2470402b, 80, 0, 0}, + {0x2472c3eb, 15, 1, 0}, + {0x2477a571, 117, 1, 0}, + {0x248566a7, 4, 1, 890}, + {0x248e7693, 4, 1, 1288}, + {0x24900c64, 46, 1, 1187}, + {0x24a0aa1a, 155, 0, 0}, + {0x24ba12dd, 33, 1, 0}, + {0x24ba90ca, 46, 0, 0}, + {0x24eecc15, 74, 1, 1191}, + {0x250a8eaa, 188, 0, 0}, + {0x250f7913, 133, 0, 0}, + {0x25225c70, 0, 1, 0}, + {0x2526c943, 77, 0, 0}, + {0x252ffd12, 34, 0, 0}, + {0x2538d860, 92, 1, 690}, + {0x2545214c, 126, 1, 0}, + {0x25482ceb, 117, 0, 0}, + {0x2554f9fd, 156, 0, 0}, + {0x255b129c, 100, 0, 0}, + {0x256441eb, 178, 0, 0}, + {0x2565786d, 117, 0, 0}, + {0x25687c07, 0, 1, 0}, + {0x256cfb05, 57, 1, 1088}, + {0x25952141, 0, 1, 0}, + {0x25a44d1b, 104, 1, 992}, + {0x25da821f, 45, 0, 0}, + {0x25df361f, 57, 1, 1088}, + {0x25e389da, 4, 1, 987}, + {0x25edaf5c, 92, 0, 0}, + {0x25f872d4, 100, 1, 487}, + {0x262b5a1d, 163, 1, 189}, + {0x263ac8a0, 46, 1, 1288}, + {0x264f26b1, 24, 0, 0}, + {0x265167e1, 160, 0, 0}, + {0x2651f227, 165, 1, 1192}, + {0x26533405, 99, 1, 1191}, + {0x265d7833, 102, 0, 0}, + {0x2669ef4e, 104, 1, 1091}, + {0x26717e19, 117, 0, 0}, + {0x26796758, 92, 1, 192}, + {0x267de4cc, 0, 1, 0}, + {0x267e592f, 117, 0, 0}, + {0x26898b6a, 174, 0, 0}, + {0x268d566d, 104, 1, 1187}, + {0x268e39d0, 46, 0, 0}, + {0x269b5f1e, 93, 0, 0}, + {0x26ad7cef, 92, 1, 192}, + {0x26bb1c8c, 37, 0, 0}, + {0x26bd6ec6, 117, 0, 0}, + {0x26bfed27, 43, 0, 0}, + {0x26ccc20c, 156, 0, 0}, + {0x26cec726, 195, 0, 0}, + {0x26d3082c, 46, 1, 1292}, + {0x26d42cee, 180, 0, 0}, + {0x26de121d, 156, 0, 0}, + {0x26e39935, 72, 0, 0}, + {0x26e82008, 100, 0, 0}, + {0x26f2b268, 65, 1, 591}, + {0x26ff3ea2, 100, 0, 0}, + {0x2705eaeb, 15, 1, 0}, + {0x270eaed5, 133, 0, 0}, + {0x2716bd4b, 126, 1, 1085}, + {0x272e96a6, 93, 0, 0}, + {0x2735af2e, 158, 1, 991}, + {0x2746b39e, 126, 0, 0}, + {0x274cb4c6, 117, 0, 0}, + {0x275dcc83, 92, 0, 0}, + {0x27738241, 126, 0, 0}, + {0x27777635, 126, 1, 387}, + {0x278bf2ea, 0, 1, 0}, + {0x278db9e3, 4, 2, 0}, + {0x2795e4f6, 156, 0, 0}, + {0x279710dc, 182, 1, 691}, + {0x279b69c0, 115, 1, 592}, + {0x27aa3933, 57, 1, 1088}, + {0x27aeaf2f, 126, 1, 387}, + {0x27b29870, 69, 0, 0}, + {0x27b9cbab, 183, 0, 0}, + {0x27c16011, 93, 0, 0}, + {0x27d14a54, 104, 1, 1187}, + {0x27d34a57, 193, 0, 0}, + {0x27d3d6eb, 117, 0, 0}, + {0x27d48cb2, 66, 0, 0}, + {0x27ddf227, 143, 1, 887}, + {0x27e4d335, 126, 0, 0}, + {0x27f8d0d2, 104, 1, 1190}, + {0x281160ae, 24, 0, 0}, + {0x28154f6c, 193, 1, 191}, + {0x282aa7e0, 195, 0, 0}, + {0x283913f2, 92, 1, 390}, + {0x283ad224, 89, 0, 0}, + {0x284e65e8, 126, 1, 1085}, + {0x2856111f, 196, 1, 193}, + {0x2858933b, 69, 0, 0}, + {0x286613d8, 99, 0, 0}, + {0x286fcd20, 100, 0, 0}, + {0x288662e9, 160, 0, 0}, + {0x28aa07ba, 0, 1, 0}, + {0x28c11d24, 178, 0, 0}, + {0x28c1d3d5, 178, 0, 0}, + {0x28c2dfce, 128, 0, 0}, + {0x28c90b01, 161, 0, 0}, + {0x28f9b41f, 92, 1, 493}, + {0x28fb71ae, 188, 1, 789}, + {0x29068417, 122, 0, 0}, + {0x291bcd7d, 39, 0, 0}, + {0x29401686, 37, 0, 0}, + {0x294299ef, 16, 0, 0}, + {0x29449ba9, 99, 0, 0}, + {0x29470b20, 77, 0, 0}, + {0x2959b3fa, 89, 0, 0}, + {0x296b915f, 0, 1, 0}, + {0x297198b9, 69, 0, 0}, + {0x2989ead6, 4, 1, 991}, + {0x29c36bf4, 161, 0, 0}, + {0x29c61b41, 117, 0, 0}, + {0x29cc4dee, 44, 0, 0}, + {0x29e173ff, 160, 0, 0}, + {0x29ec0fd1, 39, 0, 0}, + {0x2a01f9d1, 117, 0, 0}, + {0x2a0769e1, 34, 0, 0}, + {0x2a1919fe, 16, 0, 0}, + {0x2a3ca509, 164, 0, 0}, + {0x2a601192, 77, 0, 0}, + {0x2a629f7d, 180, 0, 0}, + {0x2a6559a1, 160, 0, 0}, + {0x2a763c9c, 92, 0, 0}, + {0x2a79d3ba, 182, 1, 791}, + {0x2a7d3adf, 117, 0, 0}, + {0x2a83ddc5, 0, 1, 0}, + {0x2a971204, 160, 0, 0}, + {0x2ac5233c, 100, 0, 0}, + {0x2ac87283, 160, 1, 887}, + {0x2ae97660, 6, 1, 490}, + {0x2b11e0b0, 99, 0, 0}, + {0x2b1497dc, 97, 0, 0}, + {0x2b160bf0, 34, 0, 0}, + {0x2b2d47f1, 46, 1, 587}, + {0x2b378d11, 65, 1, 490}, + {0x2b462010, 126, 1, 686}, + {0x2b548d75, 99, 1, 1290}, + {0x2b6d2447, 143, 0, 0}, + {0x2b72fe7e, 191, 0, 0}, + {0x2b7fe4d7, 100, 0, 0}, + {0x2b814116, 0, 1, 0}, + {0x2b825ce1, 117, 0, 0}, + {0x2b85420e, 126, 0, 0}, + {0x2ba30713, 100, 0, 0}, + {0x2bb33c69, 0, 1, 0}, + {0x2bb3dabe, 160, 0, 0}, + {0x2bb7f34a, 165, 0, 0}, + {0x2bc25d5a, 50, 1, 392}, + {0x2bc67aa8, 34, 1, 192}, + {0x2bcf2132, 46, 0, 0}, + {0x2bd44c6a, 68, 1, 991}, + {0x2be254e9, 117, 0, 0}, + {0x2bf61c53, 160, 1, 1292}, + {0x2c003fb2, 46, 0, 0}, + {0x2c088dc5, 126, 2, 0}, + {0x2c14d55e, 97, 0, 0}, + {0x2c2ddfb4, 46, 1, 790}, + {0x2c4421b2, 24, 0, 0}, + {0x2c5908a7, 0, 1, 0}, + {0x2c609b52, 115, 1, 990}, + {0x2c818014, 126, 1, 0}, + {0x2c945f19, 100, 1, 1091}, + {0x2caae01c, 77, 1, 1092}, + {0x2cb269d5, 151, 0, 0}, + {0x2cba26ac, 24, 0, 0}, + {0x2cc9e8aa, 165, 0, 0}, + {0x2cdef25f, 143, 1, 988}, + {0x2ce45ff2, 40, 1, 0}, + {0x2d0d4ff9, 0, 1, 0}, + {0x2d2f91b8, 202, 0, 0}, + {0x2d41ef92, 104, 1, 1289}, + {0x2d664d99, 34, 0, 0}, + {0x2d69dd94, 165, 1, 90}, + {0x2d75c7a9, 126, 1, 789}, + {0x2d76a271, 126, 1, 888}, + {0x2d782595, 133, 0, 0}, + {0x2d7bf9a3, 89, 0, 0}, + {0x2d84f5b3, 14, 1, 1092}, + {0x2d8c2829, 0, 1, 0}, + {0x2db7c31e, 54, 0, 0}, + {0x2dbddd11, 126, 2, 0}, + {0x2dbfa36a, 126, 0, 0}, + {0x2dc05a6f, 2, 0, 0}, + {0x2dc27941, 134, 0, 0}, + {0x2dc331a2, 22, 0, 0}, + {0x2dc3817d, 20, 0, 0}, + {0x2dc4f189, 89, 0, 0}, + {0x2dcd824f, 92, 0, 0}, + {0x2ddc2dc3, 6, 1, 990}, + {0x2ddd5459, 200, 1, 0}, + {0x2dff7fdc, 158, 1, 1292}, + {0x2e069e5f, 0, 1, 0}, + {0x2e0741b6, 158, 1, 1092}, + {0x2e1790a4, 0, 2, 0}, + {0x2e1b6b3c, 0, 1, 0}, + {0x2e1e7fd8, 34, 0, 0}, + {0x2e2acae9, 20, 0, 0}, + {0x2e2f9018, 124, 0, 0}, + {0x2e326a1d, 166, 1, 0}, + {0x2e35dd2e, 152, 1, 694}, + {0x2e4ccf46, 64, 0, 0}, + {0x2e52d091, 0, 1, 0}, + {0x2e563c66, 117, 0, 0}, + {0x2e6301ed, 126, 1, 290}, + {0x2e6c3ca9, 74, 1, 989}, + {0x2e6ee98d, 65, 1, 391}, + {0x2e7f0b29, 12, 1, 690}, + {0x2e91eb15, 53, 1, 1092}, + {0x2e991109, 24, 0, 0}, + {0x2ea8cc16, 0, 1, 0}, + {0x2eb08777, 165, 0, 0}, + {0x2eb27d6e, 188, 1, 990}, + {0x2ebf2e0d, 1, 0, 0}, + {0x2ed0e5b2, 157, 0, 0}, + {0x2ee3ef15, 34, 1, 793}, + {0x2ee9e0d8, 24, 1, 690}, + {0x2f1c2b30, 0, 1, 0}, + {0x2f27cdef, 24, 0, 0}, + {0x2f2d1fa9, 146, 1, 690}, + {0x2f2e30f7, 165, 0, 0}, + {0x2f50bd38, 99, 1, 1191}, + {0x2f66e302, 114, 1, 689}, + {0x2f698c4d, 82, 1, 991}, + {0x2f737eb7, 24, 0, 0}, + {0x2f928646, 43, 1, 291}, + {0x2f97d456, 34, 1, 194}, + {0x2fa1e52a, 66, 0, 0}, + {0x2fa2df5f, 193, 1, 190}, + {0x2fb0ff59, 100, 1, 1286}, + {0x2fc1abae, 54, 0, 0}, + {0x2fdedcb5, 22, 1, 1291}, + {0x2fe20d79, 160, 1, 1291}, + {0x2ffde228, 173, 0, 0}, + {0x300e41b7, 34, 1, 0}, + {0x30310df1, 99, 0, 0}, + {0x303d4371, 114, 1, 889}, + {0x304827ec, 104, 1, 1187}, + {0x3057b904, 46, 0, 0}, + {0x305b4e62, 100, 1, 490}, + {0x306ebbde, 117, 0, 0}, + {0x3078cda9, 169, 0, 0}, + {0x307d0fc4, 0, 1, 0}, + {0x309d222b, 92, 0, 0}, + {0x30a174ac, 87, 2, 0}, + {0x30bf2dba, 92, 0, 0}, + {0x30ca59c8, 133, 0, 0}, + {0x30e64d03, 100, 0, 0}, + {0x30f39b6b, 7, 1, 89}, + {0x30fd5776, 83, 0, 0}, + {0x311b5a58, 0, 1, 0}, + {0x313cfd8a, 164, 0, 0}, + {0x314ee295, 126, 0, 0}, + {0x316eb515, 165, 1, 0}, + {0x317b3237, 38, 0, 0}, + {0x318b4122, 43, 1, 1290}, + {0x31957ae4, 76, 0, 0}, + {0x319730e3, 165, 0, 0}, + {0x319ccfcc, 22, 1, 991}, + {0x31b068c6, 95, 0, 0}, + {0x31b44c65, 165, 0, 0}, + {0x31c3e548, 100, 1, 294}, + {0x31c7ad13, 161, 0, 0}, + {0x31cd9903, 24, 0, 0}, + {0x31d239d6, 141, 1, 88}, + {0x31d6b7bd, 122, 0, 0}, + {0x32086826, 115, 1, 1288}, + {0x322c9b09, 100, 0, 0}, + {0x322f723a, 178, 0, 0}, + {0x323b547d, 43, 0, 0}, + {0x32490787, 40, 1, 0}, + {0x3256114c, 175, 0, 0}, + {0x326ab3b6, 163, 1, 190}, + {0x3272bc3c, 77, 0, 0}, + {0x3275fd7e, 190, 0, 0}, + {0x327946e3, 12, 1, 690}, + {0x3293afea, 92, 0, 0}, + {0x3296ff7a, 117, 0, 0}, + {0x32979126, 98, 0, 0}, + {0x32a1290c, 42, 0, 0}, + {0x32a33bd1, 115, 1, 190}, + {0x32b45889, 0, 1, 0}, + {0x32c1ce97, 155, 0, 0}, + {0x32c4b857, 129, 2, 0}, + {0x32cf4307, 193, 1, 390}, + {0x32e59039, 200, 1, 0}, + {0x32e8baf3, 34, 0, 0}, + {0x32f85838, 52, 2, 0}, + {0x32fa246f, 117, 0, 0}, + {0x32fb0583, 160, 1, 887}, + {0x330de468, 24, 0, 0}, + {0x3311562e, 156, 0, 0}, + {0x33159eac, 93, 0, 0}, + {0x331cc7ef, 100, 0, 0}, + {0x33258813, 4, 1, 987}, + {0x332bacdf, 160, 2, 0}, + {0x332c47e0, 165, 0, 0}, + {0x33346cd2, 117, 0, 0}, + {0x333c48a0, 46, 1, 1190}, + {0x3346e19e, 57, 1, 1088}, + {0x3348e3bd, 28, 0, 0}, + {0x3349ba0e, 152, 1, 0}, + {0x335cfba6, 24, 0, 0}, + {0x336e2a6f, 20, 0, 0}, + {0x33716316, 77, 0, 0}, + {0x339437f6, 74, 1, 189}, + {0x339c034c, 53, 0, 0}, + {0x33b899c9, 24, 0, 0}, + {0x33bd809c, 117, 0, 0}, + {0x33cd1506, 15, 1, 0}, + {0x33ce3ff0, 100, 0, 0}, + {0x33d0282f, 40, 1, 0}, + {0x33d07e45, 2, 0, 0}, + {0x3403b1fc, 97, 0, 0}, + {0x3409413f, 126, 0, 0}, + {0x3417ec46, 4, 1, 890}, + {0x3420711c, 158, 1, 192}, + {0x34338616, 166, 1, 0}, + {0x343c7bb0, 166, 1, 0}, + {0x343e9146, 2, 0, 0}, + {0x345d3a1a, 31, 1, 0}, + {0x346cd5d3, 164, 0, 0}, + {0x347d7d34, 117, 1, 1193}, + {0x3482720c, 0, 1, 0}, + {0x3484ab0c, 74, 1, 0}, + {0x348ae7d4, 126, 1, 490}, + {0x349313df, 34, 0, 0}, + {0x349391b5, 160, 1, 391}, + {0x34a516fd, 77, 0, 0}, + {0x34a8bd90, 77, 1, 988}, + {0x34bb757b, 77, 2, 0}, + {0x34cfa970, 39, 0, 0}, + {0x34d5fc6e, 4, 1, 1289}, + {0x34dfb13c, 126, 0, 0}, + {0x34eab034, 46, 1, 390}, + {0x34fbe63e, 92, 0, 0}, + {0x350bf21b, 24, 0, 0}, + {0x350d835e, 100, 1, 1286}, + {0x3515fc5b, 0, 1, 0}, + {0x351f1f5b, 195, 0, 0}, + {0x35226b99, 126, 0, 0}, + {0x3523214b, 156, 1, 989}, + {0x35476e87, 104, 1, 1091}, + {0x355ecafd, 143, 0, 0}, + {0x35893b67, 160, 0, 0}, + {0x3598238f, 117, 0, 0}, + {0x359c3631, 46, 1, 188}, + {0x35a465e5, 165, 0, 0}, + {0x35b6febf, 104, 1, 989}, + {0x35c41cd4, 68, 1, 989}, + {0x35c6f574, 92, 1, 1292}, + {0x35c9b9e0, 165, 0, 0}, + {0x35d8b101, 2, 0, 0}, + {0x35e3eea2, 76, 0, 0}, + {0x35e65a8c, 89, 0, 0}, + {0x35effd0e, 128, 0, 0}, + {0x360aa8b4, 93, 0, 0}, + {0x36305fa3, 156, 0, 88}, + {0x36520684, 24, 1, 1289}, + {0x36584c96, 81, 0, 0}, + {0x3674ffdb, 85, 0, 0}, + {0x3691c120, 92, 0, 0}, + {0x3691e09e, 16, 0, 0}, + {0x369da42d, 117, 0, 0}, + {0x36af368f, 164, 0, 0}, + {0x36b93d27, 119, 0, 0}, + {0x36c5ee93, 165, 0, 0}, + {0x36d5482b, 126, 0, 0}, + {0x36df877a, 74, 1, 91}, + {0x37088eff, 126, 0, 0}, + {0x37138039, 4, 1, 189}, + {0x3719a26d, 117, 0, 0}, + {0x371e771c, 156, 0, 0}, + {0x3746f951, 92, 0, 0}, + {0x374cb8a6, 77, 0, 0}, + {0x374d6871, 4, 1, 987}, + {0x376138d8, 160, 0, 0}, + {0x37a4552f, 25, 0, 0}, + {0x37a5eb52, 100, 2, 0}, + {0x37b3ad54, 0, 1, 0}, + {0x37b62d04, 195, 0, 0}, + {0x37ba3261, 104, 1, 990}, + {0x37bf04d7, 34, 0, 0}, + {0x37d13f96, 54, 0, 0}, + {0x37ef8319, 4, 1, 89}, + {0x37f49572, 128, 0, 0}, + {0x37f59450, 193, 0, 0}, + {0x37fc3443, 0, 1, 0}, + {0x3802276d, 0, 1, 0}, + {0x3824f7a5, 126, 2, 0}, + {0x3836eeac, 133, 0, 0}, + {0x3849d0ee, 58, 1, 0}, + {0x38810a91, 126, 1, 1085}, + {0x38946c43, 0, 1, 0}, + {0x389960db, 126, 1, 1085}, + {0x38a02977, 43, 0, 0}, + {0x38b590e4, 29, 1, 790}, + {0x38d314db, 25, 0, 0}, + {0x38ef66b5, 92, 0, 0}, + {0x38fbcc85, 33, 1, 0}, + {0x38fdb7f4, 0, 1, 0}, + {0x390e0320, 175, 0, 0}, + {0x3918e04d, 156, 0, 0}, + {0x391aa1b8, 178, 0, 0}, + {0x3940b0f9, 117, 0, 0}, + {0x394fd25a, 0, 1, 0}, + {0x3951afec, 46, 1, 491}, + {0x395f36c9, 24, 1, 1086}, + {0x396f0d59, 156, 0, 0}, + {0x398b8182, 127, 1, 1091}, + {0x39aa20f0, 28, 0, 0}, + {0x39b2ecda, 179, 0, 0}, + {0x39b5e5d2, 0, 1, 0}, + {0x39b7e9c4, 193, 1, 191}, + {0x39c879bb, 0, 1, 0}, + {0x39d1fa03, 126, 1, 1085}, + {0x39d43261, 97, 0, 0}, + {0x39d622f7, 66, 0, 0}, + {0x39e1a201, 92, 0, 0}, + {0x39f2ce4b, 99, 0, 0}, + {0x3a0965b1, 115, 1, 492}, + {0x3a15a108, 114, 1, 390}, + {0x3a15b344, 97, 0, 0}, + {0x3a1694f9, 164, 0, 0}, + {0x3a390b54, 44, 0, 0}, + {0x3a51eb04, 2, 0, 0}, + {0x3a5cc3fa, 15, 1, 0}, + {0x3a622c78, 34, 0, 0}, + {0x3a7203ae, 0, 1, 0}, + {0x3a8f81b0, 156, 0, 0}, + {0x3a94fa0b, 126, 1, 1085}, + {0x3a990ee0, 33, 1, 0}, + {0x3ab1e983, 115, 1, 190}, + {0x3ac0830a, 0, 2, 0}, + {0x3acd4bf1, 126, 1, 1085}, + {0x3ae06d19, 117, 0, 0}, + {0x3ae528ff, 37, 0, 0}, + {0x3b05ac54, 80, 0, 0}, + {0x3b0fb600, 195, 0, 0}, + {0x3b1a7eef, 147, 0, 0}, + {0x3b28386e, 156, 1, 990}, + {0x3b3f88f0, 126, 1, 889}, + {0x3b5586c3, 66, 0, 0}, + {0x3b73c254, 200, 1, 0}, + {0x3b7b3be1, 138, 0, 0}, + {0x3b7f5b3b, 127, 1, 693}, + {0x3b86ccb7, 91, 1, 690}, + {0x3b997543, 115, 2, 0}, + {0x3bb31e38, 43, 2, 0}, + {0x3bbff3a6, 2, 0, 0}, + {0x3bc3a97b, 34, 0, 0}, + {0x3bcd370e, 0, 1, 0}, + {0x3be244ef, 34, 1, 791}, + {0x3be91a23, 34, 0, 0}, + {0x3bf15767, 24, 0, 0}, + {0x3bf38115, 156, 0, 0}, + {0x3bf55966, 191, 0, 0}, + {0x3c13eaa0, 117, 0, 0}, + {0x3c361b36, 92, 0, 0}, + {0x3c54a0bf, 54, 0, 0}, + {0x3c5a512b, 2, 0, 0}, + {0x3c5ae54b, 39, 0, 0}, + {0x3c5c81d4, 166, 1, 0}, + {0x3c7e38f5, 40, 1, 0}, + {0x3c8f2d80, 0, 1, 0}, + {0x3c9e8124, 127, 1, 192}, + {0x3c9fe649, 104, 1, 1190}, + {0x3ccb5d57, 77, 0, 0}, + {0x3cd6bb0e, 161, 0, 0}, + {0x3cdea85b, 173, 0, 0}, + {0x3ced963a, 200, 1, 0}, + {0x3cf67aec, 197, 0, 0}, + {0x3cfe69c7, 117, 0, 0}, + {0x3d0996b2, 188, 1, 1091}, + {0x3d1c4894, 12, 1, 1290}, + {0x3d3a0367, 160, 0, 0}, + {0x3d3aa7b4, 196, 1, 0}, // "Proto" + {0x3d3d8f58, 142, 1, 792}, + {0x3d4b64f1, 49, 0, 0}, + {0x3d4d2ba5, 147, 0, 0}, + {0x3d564757, 126, 1, 1085}, + {0x3d731836, 54, 0, 0}, + {0x3d8c4a06, 40, 1, 0}, + {0x3d8de75f, 95, 0, 0}, + {0x3d95d866, 37, 0, 0}, + {0x3d96a1d8, 166, 1, 0}, + {0x3d9fddd5, 191, 0, 0}, + {0x3da2085e, 92, 0, 0}, + {0x3da82381, 76, 0, 0}, + {0x3dae3335, 99, 0, 0}, + {0x3db3202e, 54, 0, 0}, + {0x3db9509a, 38, 0, 0}, + {0x3dc4d6c5, 46, 0, 0}, + {0x3dd3ba1e, 34, 0, 0}, + {0x3deac303, 117, 0, 0}, + {0x3df6d493, 38, 0, 0}, + {0x3df8e170, 126, 1, 8}, + {0x3e054f19, 25, 0, 0}, + {0x3e078f29, 164, 0, 0}, + {0x3e1271d5, 15, 1, 0}, + {0x3e58a87e, 156, 1, 488}, + {0x3e785dc3, 69, 0, 0}, + {0x3e840b56, 34, 1, 990}, + {0x3e8dec2f, 0, 1, 0}, + {0x3e8ded4e, 0, 2, 0}, + {0x3e95ba25, 117, 0, 0}, + {0x3eca3dda, 92, 1, 991}, + {0x3ecaa6a3, 143, 0, 0}, + {0x3ecdb1f7, 100, 0, 0}, + {0x3ed8c6b5, 126, 0, 0}, + {0x3edcf7e8, 34, 1, 1293}, + {0x3eff62e4, 46, 0, 0}, + {0x3f0c8136, 72, 0, 0}, + {0x3f0fd764, 156, 1, 1188}, + {0x3f150354, 0, 1, 0}, + {0x3f15d20d, 24, 0, 0}, + {0x3f2bda65, 92, 0, 0}, + {0x3f39f35c, 0, 1, 0}, + {0x3f49d239, 90, 0, 0}, + {0x3f56a392, 32, 0, 0}, + {0x3f5a4dd0, 160, 0, 0}, + {0x3f78037c, 34, 1, 793}, + {0x3f88d76f, 91, 1, 690}, + {0x3f8d6889, 92, 0, 0}, + {0x3f9970f9, 143, 0, 0}, + {0x3fa96277, 165, 0, 0}, + {0x3faaa154, 77, 0, 0}, + {0x3fbefd71, 100, 0, 0}, + {0x3fbf0480, 38, 0, 0}, + {0x3fc1dc19, 21, 0, 0}, + {0x3fccdc7b, 143, 0, 0}, + {0x3fdec942, 126, 1, 1085}, + {0x3fe272fb, 126, 1, 0}, + {0x3fea656a, 133, 0, 0}, + {0x3ffa5762, 39, 0, 0}, + {0x401521f7, 22, 1, 592}, + {0x40280a0f, 126, 0, 0}, + {0x404b2e8b, 155, 1, 690}, + {0x4057c51b, 174, 0, 0}, + {0x406fe900, 126, 1, 790}, + {0x4076e7a6, 156, 2, 0}, + {0x407793c0, 111, 1, 991}, + {0x408b54ee, 100, 0, 0}, + {0x408d72db, 32, 0, 0}, + {0x40a26fdb, 25, 0, 0}, + {0x40c6e687, 0, 1, 0}, + {0x40ce2fc0, 0, 1, 0}, + {0x40d159b6, 143, 1, 789}, + {0x40dafcba, 165, 1, 690}, + {0x40ed2a9d, 182, 1, 489}, + {0x40f96469, 38, 0, 0}, + {0x411edf39, 143, 0, 0}, + {0x41243492, 163, 1, 990}, + {0x41413b06, 53, 1, 0}, + {0x41482da3, 2, 0, 0}, + {0x4156a3cd, 43, 0, 0}, + {0x415e5109, 55, 0, 0}, + {0x4187ec10, 165, 1, 490}, + {0x418d26d7, 100, 0, 0}, + {0x4192d82c, 117, 0, 0}, + {0x419461d0, 50, 1, 291}, + {0x41cc30a7, 20, 0, 0}, + {0x41cf3616, 126, 1, 0}, + {0x41f5d38d, 43, 0, 0}, + {0x41f87a9b, 39, 0, 0}, + {0x41f9e0aa, 92, 1, 0}, + {0x4220c170, 65, 1, 192}, + {0x4232c609, 135, 1, 0}, + {0x423ada8e, 188, 1, 1288}, + {0x424ec0a6, 92, 0, 0}, + {0x42520393, 158, 1, 1292}, + {0x425357bf, 166, 1, 0}, + {0x42607a97, 166, 1, 0}, + {0x42749a95, 40, 1, 0}, + {0x42791a4d, 6, 1, 1089}, + {0x428dfac6, 24, 1, 490}, + {0x429103c9, 117, 0, 0}, + {0x429124f5, 117, 0, 0}, + {0x429fd177, 117, 0, 0}, + {0x42b36609, 180, 0, 0}, + {0x42d893e4, 160, 0, 0}, + {0x42ec339a, 0, 1, 0}, + {0x42f75eae, 24, 0, 0}, + {0x4301b96c, 171, 0, 0}, + {0x4318a2f8, 126, 1, 890}, + {0x4328b273, 126, 1, 1085}, + {0x4334890e, 124, 0, 0}, + {0x4344d265, 77, 0, 0}, + {0x43504731, 134, 0, 0}, + {0x435313c3, 72, 0, 0}, + {0x43539a3c, 161, 0, 0}, + {0x4356194e, 165, 1, 787}, + {0x435aeec6, 160, 1, 1092}, + {0x437e7b69, 100, 1, 987}, + {0x43a357ef, 126, 0, 0}, + {0x43d01c10, 97, 1, 1290}, + {0x43d30c2f, 166, 1, 0}, + {0x43e95611, 178, 0, 0}, + {0x43ecfc00, 34, 1, 791}, + {0x441aeae6, 34, 0, 0}, + {0x4459ca3c, 100, 0, 0}, + {0x445dd134, 0, 1, 0}, + {0x4468ca43, 81, 0, 0}, + {0x4471d531, 76, 0, 0}, + {0x449abfed, 15, 1, 0}, + {0x449e6557, 104, 2, 0}, + {0x44b060da, 72, 0, 0}, + {0x44bb1665, 57, 1, 490}, + {0x44ca9a8d, 53, 0, 0}, + {0x44f34172, 89, 1, 987}, + {0x44f92026, 39, 0, 0}, + {0x45111a7f, 133, 0, 0}, + {0x453be513, 111, 0, 0}, + {0x454f8e59, 29, 1, 790}, + {0x4582f22e, 106, 0, 0}, + {0x458936ef, 6, 1, 192}, + {0x45917511, 34, 0, 0}, + {0x4591be98, 126, 1, 0}, + {0x459adfa8, 165, 0, 0}, + {0x459d0c2a, 120, 0, 0}, + {0x45a2acc4, 115, 1, 190}, + {0x45a2fd26, 58, 1, 91}, + {0x45a3f009, 190, 0, 0}, + {0x45a41784, 77, 1, 1290}, + {0x45ab39cd, 200, 1, 91}, + {0x45bcd662, 115, 1, 1288}, + {0x45f03d2e, 123, 1, 691}, + {0x45f4c931, 117, 0, 0}, + {0x45f4fa8c, 122, 0, 0}, + {0x45fb6d34, 100, 0, 0}, + {0x460710fa, 140, 0, 0}, + {0x460a9cb0, 15, 1, 0}, + {0x4640ebe0, 76, 0, 0}, + {0x4642dda6, 99, 1, 689}, + {0x46480432, 173, 2, 0}, + {0x465e5483, 6, 1, 192}, + {0x46652449, 126, 0, 0}, + {0x466efdc2, 155, 0, 0}, + {0x467515fe, 90, 0, 0}, + {0x46752bc8, 94, 0, 0}, + {0x4677a2be, 92, 0, 0}, + {0x467fe41e, 90, 0, 0}, + {0x468db9a0, 46, 1, 1289}, + {0x46914e3e, 126, 0, 0}, + {0x46931ea0, 126, 2, 0}, + {0x46dc6e57, 24, 0, 0}, + {0x46e83313, 124, 0, 0}, + {0x46fd7843, 117, 0, 0}, + {0x470b031b, 189, 1, 90}, + {0x47232739, 192, 0, 0}, + {0x472d4d56, 165, 1, 787}, + {0x4751a751, 65, 1, 1093}, + {0x475a574e, 0, 1, 0}, + {0x475cdbfe, 92, 0, 0}, + {0x476e022b, 116, 1, 190}, + {0x4776bb2c, 92, 0, 0}, + {0x477db261, 77, 0, 0}, + {0x4788c2a2, 6, 1, 192}, + {0x478a04d8, 0, 1, 0}, + {0x478a11ae, 117, 0, 0}, + {0x47a57fc3, 43, 0, 0}, + {0x47a60cf1, 39, 0, 0}, + {0x47b6a39f, 126, 2, 0}, + {0x47c2020b, 117, 0, 0}, + {0x47ed7979, 6, 1, 489}, + {0x47f4e00f, 182, 1, 691}, + {0x47f552cd, 76, 0, 0}, + {0x47f77a4c, 57, 1, 689}, + {0x47fd88cf, 126, 2, 0}, + {0x480b35d1, 0, 2, 0}, + {0x481519b1, 165, 0, 0}, + {0x481608a9, 117, 0, 0}, + {0x4819a595, 160, 0, 0}, + {0x4823eefe, 57, 1, 193}, + {0x48349b0b, 53, 0, 87}, // Dragon Quest 2 (J) + {0x485ac098, 37, 0, 0}, + {0x4864c304, 126, 1, 686}, + {0x489d19ab, 77, 0, 0}, + {0x489ef6a2, 4, 1, 689}, + {0x48a941f0, 50, 1, 1292}, + {0x48c8de53, 46, 0, 0}, + {0x48dc9540, 97, 0, 0}, + {0x48dc9e6a, 178, 0, 0}, + {0x48e904d0, 188, 1, 490}, // Snake's Revenge (U) + {0x48ecc48a, 188, 1, 289}, + {0x48f68d40, 126, 1, 1085}, + {0x48f6a8e2, 62, 0, 0}, + {0x48f8d55e, 24, 0, 0}, + {0x490e8a4c, 100, 0, 0}, + {0x49123146, 100, 0, 0}, + {0x491d8cdb, 117, 0, 0}, + {0x491f0de8, 100, 1, 493}, + {0x492a8782, 46, 0, 0}, + {0x49362c1e, 155, 1, 0}, + {0x493680c8, 100, 0, 0}, + {0x4937f7a1, 92, 0, 0}, + {0x493bd2ff, 69, 0, 0}, + {0x4942bda8, 144, 0, 0}, + {0x497b9d15, 38, 0, 0}, + {0x497e9336, 180, 0, 0}, + {0x498187b6, 2, 0, 0}, + {0x499bd8e2, 0, 1, 0}, + {0x49a42938, 156, 0, 0}, + {0x49aa5257, 160, 0, 0}, + {0x49aeb3a6, 126, 1, 1085}, + {0x49c84b4e, 160, 0, 0}, + {0x49c9ff12, 0, 1, 0}, + {0x49da2f76, 92, 0, 0}, + {0x49e834c3, 132, 0, 0}, + {0x49f745e0, 115, 1, 1189}, + {0x4a172848, 160, 0, 0}, + {0x4a339590, 69, 0, 0}, + {0x4a5a4e1e, 5, 1, 0}, + {0x4a5d8469, 160, 1, 1192}, + {0x4a601a2c, 100, 0, 0}, + {0x4a99b47e, 126, 0, 0}, + {0x4a9f735c, 104, 1, 1289}, + {0x4ae58f5d, 92, 0, 0}, + {0x4aea40f7, 9, 0, 0}, + {0x4afd02b9, 20, 1, 1191}, + {0x4affeee7, 34, 1, 1190}, + {0x4b041b6b, 182, 1, 490}, + {0x4b0dacce, 181, 0, 0}, + {0x4b14f688, 155, 0, 0}, + {0x4b2cc73e, 43, 2, 0}, + {0x4b2dce64, 166, 1, 0}, + {0x4b5177e9, 164, 0, 0}, + {0x4b647c55, 104, 1, 292}, + {0x4ba78222, 160, 0, 0}, + {0x4ba8fcb6, 16, 0, 0}, + {0x4bb9b840, 191, 2, 0}, + {0x4bd82f51, 0, 1, 0}, + {0x4bf3972d, 126, 0, 0}, + {0x4c03dfdd, 95, 0, 0}, + {0x4c049cfe, 156, 0, 0}, + {0x4c25d4f5, 100, 1, 689}, + {0x4c32e3ae, 46, 1, 1187}, + {0x4c5836bd, 117, 0, 0}, + {0x4c5fa6ef, 126, 0, 0}, + {0x4c6faf64, 24, 1, 1289}, + {0x4c7c1af3, 196, 1, 1292}, + {0x4c923dae, 100, 0, 0}, + {0x4c9b6d43, 126, 0, 0}, + {0x4cccd878, 165, 0, 0}, + {0x4cdcaf6b, 29, 1, 489}, + {0x4cede4e0, 160, 0, 0}, + {0x4d1ac58c, 3, 1, 190}, + {0x4d1df589, 46, 2, 0}, + {0x4d21172c, 117, 0, 0}, + {0x4d2edf70, 128, 0, 0}, + {0x4d345422, 4, 2, 0}, + {0x4d3aa180, 97, 1, 0}, // "Proto" + {0x4d3fba78, 24, 1, 489}, + {0x4d527d4a, 31, 1, 0}, + {0x4d5f2eb0, 0, 1, 0}, + {0x4d7859a9, 156, 0, 0}, + {0x4da464e9, 156, 0, 0}, + {0x4dc43d8c, 178, 0, 0}, + {0x4dc73149, 24, 1, 790}, + {0x4dc910fd, 166, 1, 0}, + {0x4dcd15ee, 159, 0, 0}, + {0x4ddb4dce, 45, 0, 0}, + {0x4de477be, 160, 0, 0}, + {0x4de66107, 104, 1, 1187}, + {0x4de7236f, 0, 1, 0}, + {0x4df3f32e, 126, 0, 0}, + {0x4dfd949e, 89, 0, 0}, + {0x4e18ca2d, 97, 0, 0}, + {0x4e1c1e3c, 46, 1, 188}, + {0x4e220484, 126, 1, 1085}, + {0x4e22368d, 100, 1, 1187}, + {0x4e2a57dd, 24, 1, 987}, + {0x4e42f13a, 34, 0, 0}, + {0x4e44ff44, 77, 1, 194}, + {0x4e5257d7, 24, 0, 0}, + {0x4e550aa9, 100, 1, 91}, + {0x4e5c1c1d, 133, 0, 0}, + {0x4e7db5af, 151, 0, 0}, + {0x4e800048, 35, 0, 0}, + {0x4e8e2951, 90, 0, 0}, + {0x4e959173, 104, 1, 1187}, + {0x4ebdb122, 160, 1, 790}, + {0x4ebe8a31, 46, 1, 987}, + {0x4ec0fecc, 156, 2, 0}, + {0x4ed0d752, 50, 1, 392}, + {0x4edc5e95, 48, 0, 0}, + {0x4ee69e3d, 16, 0, 0}, + {0x4ee735c1, 126, 1, 1085}, + {0x4f089e8a, 6, 2, 0}, + {0x4f16c504, 160, 0, 0}, + {0x4f2f1846, 117, 0, 0}, + {0x4f3ada21, 177, 0, 0}, + {0x4f467410, 143, 1, 488}, + {0x4f48b240, 34, 2, 0}, + {0x4f50457e, 34, 1, 1092}, + {0x4f593fa0, 24, 0, 0}, + {0x4f5f0a80, 0, 1, 0}, + {0x4f9bdad2, 92, 1, 1291}, + {0x4fb460cd, 164, 0, 0}, + {0x4fbbfa74, 104, 1, 1187}, + {0x4fc2f673, 100, 0, 0}, + {0x4ff64765, 81, 0, 0}, + {0x50059012, 57, 1, 193}, + {0x5012d5d0, 156, 0, 0}, + {0x501376e8, 143, 1, 789}, + {0x5030bca8, 89, 0, 0}, + {0x50322dfd, 15, 1, 0}, + {0x504c6455, 117, 0, 0}, + {0x505f9715, 4, 1, 1287}, + {0x506e259d, 53, 1, 1092}, + {0x50734772, 40, 1, 0}, + {0x5077a2d7, 46, 1, 1186}, + {0x50893b58, 165, 0, 0}, + {0x50920b03, 160, 0, 0}, + {0x50975983, 148, 0, 0}, + {0x509cf04a, 77, 0, 0}, + {0x50a1b3fe, 126, 0, 0}, + {0x50a6f31b, 155, 0, 87}, + {0x50c69602, 193, 1, 1289}, + {0x50ccda33, 179, 0, 0}, + {0x50d141fc, 34, 1, 1190}, + {0x50d296b3, 86, 1, 691}, + {0x50da4867, 133, 0, 0}, + {0x50ec5e8b, 2, 0, 0}, + {0x50fad784, 126, 0, 0}, + {0x50fd0cc6, 4, 1, 291}, + {0x5104833e, 160, 1, 192}, + {0x5112dc21, 126, 1, 1085}, + {0x514b8c43, 92, 1, 689}, + {0x515f738a, 202, 0, 0}, + {0x516b2412, 16, 0, 0}, + {0x5174dd11, 90, 0, 0}, + {0x5190c450, 46, 1, 1289}, + {0x5193fb54, 160, 0, 0}, + {0x51bee3ea, 65, 1, 591}, + {0x51bf28af, 114, 2, 0}, + {0x51c0b27e, 5, 1, 592}, + {0x51c142ff, 166, 1, 0}, + {0x51c51c35, 100, 2, 0}, + {0x51c70247, 200, 1, 0}, + {0x51c79fd7, 77, 0, 0}, + {0x51c7c66a, 117, 0, 0}, + {0x51d2112f, 100, 1, 691}, + {0x5229fcdd, 73, 0, 0}, + {0x523ca830, 66, 0, 0}, + {0x52449508, 46, 0, 0}, + {0x524a5a32, 182, 2, 0}, + {0x526ad690, 0, 1, 0}, + {0x52b58732, 126, 1, 493}, + {0x52bcf64a, 156, 1, 88}, + {0x52c851fb, 166, 1, 0}, + {0x52e2b5e0, 126, 0, 0}, + {0x530bccb4, 34, 0, 0}, + {0x530f8b13, 143, 0, 0}, + {0x5318cdb9, 16, 0, 0}, + {0x532a27e6, 12, 1, 892}, + {0x5337f73c, 195, 0, 0}, + {0x533f5707, 126, 2, 0}, + {0x534865c6, 126, 0, 0}, + {0x535a7dee, 4, 1, 987}, + {0x538218b2, 143, 2, 0}, + {0x538cd2ea, 6, 1, 1089}, + {0x53991500, 92, 0, 0}, + {0x53a94738, 28, 0, 0}, + {0x53a9e2ba, 126, 1, 0}, // "Proto" + {0x53b02f3d, 0, 1, 0}, + {0x53ca161a, 92, 0, 0}, + {0x53cb7ced, 70, 0, 87}, + {0x53d67738, 100, 0, 0}, + {0x53f86791, 114, 1, 390}, + {0x540ec028, 100, 0, 0}, + {0x54163624, 160, 0, 0}, + {0x541989e1, 200, 1, 0}, + {0x542f7b53, 29, 1, 1289}, + {0x54386491, 0, 1, 0}, + {0x5438a0ac, 0, 1, 0}, + {0x5440811c, 93, 0, 0}, + {0x54430b24, 97, 1, 689}, + {0x54531910, 21, 0, 0}, + {0x5469dba3, 46, 0, 0}, + {0x547ad451, 165, 0, 0}, + {0x547e6cc1, 160, 0, 0}, + {0x54b0e5dd, 165, 0, 0}, + {0x54b80a8a, 117, 0, 0}, + {0x54faee6e, 36, 0, 0}, + {0x5506b1ca, 0, 1, 0}, + {0x552f1143, 15, 1, 0}, + {0x55397db3, 2, 0, 0}, + {0x554704f0, 100, 1, 92}, + {0x554b51d8, 3, 1, 990}, + {0x555042b3, 37, 0, 0}, + {0x55761931, 77, 0, 0}, + {0x55773880, 24, 1, 790}, + {0x5581e835, 126, 1, 686}, + {0x558fcb6d, 97, 1, 1290}, + {0x559fc685, 178, 0, 0}, + {0x55a7321e, 92, 1, 991}, + {0x55c3589c, 92, 0, 0}, + {0x55ca12c9, 126, 1, 1085}, + {0x55d06deb, 22, 1, 592}, + {0x55db7e2a, 152, 1, 694}, + {0x55e73ce5, 12, 1, 892}, + {0x5603f579, 126, 0, 0}, + {0x560e44b9, 166, 1, 0}, + {0x563aa29d, 117, 0, 0}, + {0x563c2cc0, 160, 1, 391}, + {0x565a4681, 148, 0, 0}, + {0x565a57e5, 100, 0, 0}, + {0x565b1bdb, 126, 2, 0}, + {0x56756615, 77, 1, 291}, + {0x5679e4dc, 124, 0, 0}, + {0x567dccbd, 197, 0, 0}, + {0x567e1620, 143, 1, 291}, + {0x56b87cb5, 117, 0, 0}, + {0x56dbfd1f, 0, 1, 0}, + {0x56f01370, 97, 0, 0}, + {0x56f1760c, 126, 0, 0}, + {0x572ca03a, 117, 0, 0}, + {0x5746a461, 117, 0, 0}, + {0x5755a36c, 129, 2, 0}, + {0x575ed2fe, 126, 1, 686}, + {0x576a0de8, 74, 1, 191}, + {0x576b493c, 0, 1, 0}, + {0x576cc1a0, 0, 1, 0}, + {0x577482bb, 160, 0, 0}, + {0x57783e71, 24, 0, 0}, + {0x5795f169, 117, 0, 0}, + {0x57970078, 126, 0, 0}, + {0x579e5d0b, 34, 0, 0}, + {0x57abbf15, 51, 1, 90}, + {0x57ac67af, 126, 1, 1088}, + {0x57c12280, 160, 1, 190}, + {0x57c12c17, 72, 0, 0}, + {0x57c2ae4e, 153, 1, 1193}, + {0x57ca4dbb, 47, 0, 0}, + {0x57d162f1, 97, 0, 0}, + {0x57db9140, 117, 0, 0}, + {0x57dd23d1, 126, 1, 889}, + {0x57de2c14, 46, 1, 1289}, + {0x57dea1fd, 120, 0, 0}, + {0x57e220d0, 155, 0, 0}, + {0x57e3218b, 99, 0, 0}, + {0x57e9b21c, 148, 0, 0}, + {0x57f33f70, 99, 0, 0}, + {0x57f72adf, 77, 0, 0}, + {0x5800be2d, 166, 1, 0}, + {0x580aa5e7, 126, 0, 0}, + {0x5816ccc5, 178, 0, 0}, + {0x5820ea66, 29, 1, 1289}, + {0x583d21ca, 77, 0, 0}, + {0x58507bc9, 99, 0, 0}, + {0x5857681c, 147, 0, 0}, + {0x58581770, 117, 0, 0}, + {0x585ba83d, 4, 2, 0}, + {0x5866aebe, 115, 1, 1292}, + {0x586915a7, 190, 0, 0}, + {0x586a3277, 77, 1, 988}, + {0x588e7492, 165, 0, 0}, + {0x589b2ec8, 89, 0, 0}, + {0x58a407bb, 100, 1, 393}, + {0x58c7ddaf, 46, 1, 1291}, + {0x58e34e66, 114, 1, 390}, + {0x58e5d1ea, 38, 0, 0}, + {0x58ebd5d7, 77, 0, 0}, + {0x58fe5467, 133, 0, 0}, + {0x591364c9, 97, 1, 1289}, + {0x59138e01, 117, 0, 0}, + {0x59276597, 160, 0, 0}, + {0x59280bec, 77, 0, 0}, + {0x592e3567, 13, 1, 1291}, + {0x5931be01, 69, 0, 0}, + {0x595b2b26, 34, 0, 0}, + {0x59643ed6, 115, 1, 1288}, + {0x59794f2d, 24, 0, 0}, + {0x598a7398, 92, 0, 0}, + {0x5991b9d0, 0, 1, 0}, + {0x59977a46, 126, 1, 1085}, + {0x59a6c2ac, 120, 0, 0}, + {0x59a706e4, 123, 1, 991}, + {0x59cd0c31, 160, 0, 0}, + {0x59e3343f, 156, 1, 488}, + {0x59f0f3ca, 160, 0, 0}, + {0x5a032058, 66, 0, 0}, + {0x5a0454f3, 195, 0, 0}, + {0x5a046cdc, 95, 0, 0}, + {0x5a09134f, 104, 1, 90}, + {0x5a18f611, 195, 0, 0}, + {0x5a2d1abf, 34, 2, 0}, + {0x5a3e41d8, 156, 1, 290}, + {0x5a3ec21c, 100, 0, 0}, + {0x5a4026c8, 92, 0, 0}, + {0x5a40f532, 89, 0, 0}, + {0x5a4f156d, 153, 1, 292}, + {0x5a5a0cd9, 28, 0, 0}, + {0x5a6860f1, 72, 0, 0}, + {0x5a7b15c8, 89, 0, 0}, + {0x5a7fd507, 46, 0, 0}, + {0x5a8b4da8, 146, 1, 1190}, + {0x5a9824e0, 156, 1, 1288}, + {0x5ab54795, 120, 0, 0}, + {0x5ace8ca0, 77, 0, 0}, + {0x5adbf660, 100, 0, 0}, + {0x5b064824, 34, 1, 789}, + {0x5b0b26be, 156, 0, 0}, + {0x5b11eb85, 192, 0, 0}, + {0x5b16a3c8, 200, 1, 0}, + {0x5b2b72cb, 33, 1, 0}, + {0x5b4b6056, 34, 1, 990}, + {0x5b4c6146, 117, 0, 0}, + {0x5b58b662, 160, 0, 0}, + {0x5b5f1d5c, 0, 1, 0}, + {0x5b68abd1, 46, 0, 0}, + {0x5b6ca654, 74, 1, 1291}, + {0x5b7ac91f, 92, 0, 0}, + {0x5b80ff0c, 34, 1, 1092}, + {0x5b837e8d, 156, 0, 0}, + {0x5b838ce2, 126, 1, 889}, + {0x5bb62688, 46, 1, 987}, + {0x5bc9d7a1, 46, 1, 390}, + {0x5bf4da62, 72, 0, 0}, + {0x5bf675ba, 100, 2, 0}, + {0x5c123ef7, 2, 0, 0}, + {0x5c1d053a, 25, 0, 0}, + {0x5c2cfe0e, 69, 0, 0}, + {0x5c629297, 193, 0, 0}, + {0x5c707ac4, 126, 0, 0}, + {0x5c9063e0, 126, 1, 1085}, + {0x5caa3e61, 10, 1, 0}, + {0x5cb41fed, 102, 0, 0}, + {0x5cc1e2c6, 2, 0, 0}, + {0x5ceb1256, 115, 1, 1288}, + {0x5cf536f4, 160, 1, 391}, + {0x5cf548d3, 126, 1, 0}, + {0x5d044347, 126, 1, 1085}, + {0x5d05ccd0, 89, 1, 291}, + {0x5d105c10, 25, 0, 0}, + {0x5d1301c5, 117, 0, 0}, + {0x5d1c5f7e, 160, 1, 1092}, + {0x5d2b1962, 126, 2, 0}, + {0x5d63ed6f, 200, 1, 0}, + {0x5d656d35, 66, 0, 0}, + {0x5d6952b9, 159, 0, 0}, + {0x5d8992c6, 78, 0, 0}, + {0x5d965b46, 62, 0, 0}, + {0x5d9d9891, 53, 0, 0}, + {0x5da9cec8, 31, 1, 0}, + {0x5dbd6099, 34, 1, 690}, + {0x5dca93ef, 68, 1, 489}, + {0x5dce2eea, 34, 1, 692}, + {0x5de61639, 0, 1, 0}, + {0x5de7f27c, 165, 0, 0}, + {0x5dec84f8, 191, 0, 0}, + {0x5ded683e, 34, 1, 0}, + {0x5e023291, 34, 1, 92}, + {0x5e137c5b, 126, 2, 0}, + {0x5e24eeda, 148, 0, 0}, + {0x5e268761, 34, 1, 0}, + {0x5e33b189, 24, 1, 489}, + {0x5e345b6d, 124, 0, 0}, + {0x5e35ad1d, 100, 0, 0}, + {0x5e3f7004, 176, 0, 0}, + {0x5e5723f4, 77, 0, 0}, + {0x5e5d5963, 24, 0, 0}, + {0x5e66eaea, 158, 1, 691}, + {0x5e6d9975, 4, 2, 0}, + {0x5e863db6, 140, 0, 0}, + {0x5e900522, 160, 1, 1188}, + {0x5e98ba4a, 89, 0, 0}, + {0x5e9bc161, 160, 0, 0}, + {0x5ea7d410, 57, 1, 490}, + {0x5eb21035, 34, 0, 0}, + {0x5eb8e707, 142, 1, 991}, + {0x5eb94b11, 15, 1, 0}, + {0x5ee5a942, 104, 1, 1187}, + {0x5ee6008e, 143, 1, 690}, + {0x5eea106c, 36, 0, 0}, + {0x5ef4f30c, 126, 1, 686}, + {0x5ef7f6e2, 34, 0, 0}, + {0x5efbded8, 117, 0, 0}, + {0x5efc27d6, 126, 0, 0}, + {0x5f0bce2a, 57, 1, 193}, + {0x5f14dc48, 77, 0, 0}, + {0x5f162195, 117, 0, 0}, + {0x5f1e7b19, 165, 1, 0}, + {0x5f30fcd8, 166, 1, 0}, + {0x5f6798f4, 2, 0, 0}, + {0x5f6e8a07, 32, 0, 0}, + {0x5f7c1d17, 160, 1, 893}, + {0x5f807010, 150, 1, 192}, + {0x5f82cb7d, 100, 0, 0}, + {0x5fab6bce, 126, 0, 0}, + {0x5fbd9178, 171, 0, 0}, + {0x5fd2aab1, 46, 1, 1091}, + {0x603aaa57, 34, 1, 1190}, + {0x603c8abe, 77, 0, 0}, + {0x6055fe9b, 165, 0, 0}, + {0x6058c65d, 126, 0, 0}, + {0x605f5d51, 181, 0, 0}, + {0x607db27a, 97, 0, 0}, + {0x607f9765, 165, 1, 690}, + {0x60a01cab, 152, 1, 793}, + {0x60a3b803, 178, 0, 0}, + {0x60d88631, 19, 0, 0}, + {0x60e63537, 126, 1, 1290}, + {0x60f1fd6f, 92, 0, 0}, + {0x60ff6444, 16, 0, 0}, + {0x61179bfa, 196, 1, 894}, + {0x611d2586, 20, 0, 0}, + {0x61253d1c, 40, 1, 0}, + {0x614afbea, 0, 1, 0}, + {0x6150517c, 34, 0, 0}, + {0x615d0a50, 97, 0, 0}, + {0x619bea12, 144, 0, 0}, + {0x619bfd3e, 65, 1, 390}, + {0x61a852ea, 81, 0, 0}, + {0x61cb190c, 14, 1, 190}, + {0x61d86167, 24, 1, 689}, + {0x61f96440, 117, 0, 0}, + {0x61fbd9a6, 200, 1, 0}, + {0x62119cb5, 126, 1, 1085}, + {0x622f059d, 160, 0, 0}, + {0x6231e6df, 117, 0, 0}, + {0x6267fbd1, 160, 0, 0}, + {0x626abd49, 77, 0, 0}, + {0x6272c549, 115, 1, 1292}, + {0x627ad380, 15, 1, 0}, + {0x628cc178, 165, 0, 0}, + {0x62a94f97, 37, 0, 0}, + {0x62afe166, 0, 1, 0}, + {0x62b71912, 0, 1, 0}, + {0x62c67984, 164, 0, 0}, + {0x62dfc064, 91, 1, 690}, + {0x62e2e7fc, 50, 1, 1092}, + {0x62ef6c79, 33, 1, 0}, + {0x630be870, 143, 1, 790}, + {0x632edf4e, 6, 2, 0}, + {0x632f6053, 0, 1, 0}, + {0x6332e4ca, 10, 1, 0}, + {0x6377cb75, 133, 0, 0}, + {0x637e366a, 100, 2, 0}, + {0x637fe65c, 15, 1, 0}, + {0x63968624, 24, 1, 987}, + {0x6396b988, 99, 0, 0}, + {0x639d74e4, 173, 0, 0}, + {0x63abf889, 126, 0, 0}, + {0x63aea200, 126, 0, 0}, + {0x63af202f, 155, 0, 0}, + {0x63af464b, 117, 0, 0}, + {0x63b00ea7, 92, 0, 0}, + {0x63b7637c, 38, 0, 0}, + {0x63bb86b5, 160, 0, 0}, + {0x63c4e122, 143, 0, 0}, + {0x63ddd219, 0, 1, 0}, + {0x63e929c4, 166, 1, 0}, + {0x63e992ac, 115, 1, 294}, + {0x6414e03d, 89, 0, 0}, + {0x6438d8b0, 117, 0, 0}, + {0x644dfe2b, 178, 0, 0}, + {0x644e312b, 179, 0, 0}, + {0x6467a5c4, 15, 1, 0}, + {0x646ed7f4, 180, 0, 0}, + {0x6479e76a, 92, 0, 0}, + {0x647eafa9, 103, 1, 894}, + {0x64818fc5, 100, 0, 0}, + {0x64a02715, 100, 0, 0}, + {0x64a75e8d, 181, 0, 0}, + {0x64bd6cdb, 161, 0, 0}, + {0x64c0fa3b, 34, 0, 0}, + {0x64c96f53, 77, 0, 0}, + {0x64eb4449, 38, 0, 0}, + {0x64edbae3, 77, 0, 0}, + {0x64fc3667, 0, 1, 0}, + {0x64fd3ba6, 164, 0, 0}, + {0x650353f7, 117, 0, 0}, + {0x651700a5, 166, 1, 0}, + {0x652f3324, 92, 0, 0}, + {0x654d7a3e, 23, 0, 0}, + {0x654f4e90, 155, 2, 0}, + {0x65518eae, 127, 1, 192}, + {0x655efeed, 143, 1, 587}, + {0x656196a6, 117, 0, 0}, + {0x65636a4e, 117, 0, 0}, + {0x656d4265, 126, 1, 686}, + {0x656fa3b5, 92, 0, 0}, + {0x65b6c0a1, 93, 0, 0}, + {0x65b96d61, 180, 0, 0}, + {0x65fb428a, 91, 1, 392}, + {0x660e8b34, 164, 0, 0}, + {0x6611497f, 144, 0, 0}, + {0x6611d8f3, 160, 0, 0}, + {0x664281ac, 76, 1, 792}, + {0x664e9906, 21, 0, 0}, + {0x666be5ec, 160, 2, 0}, + {0x666e905b, 55, 0, 0}, + {0x6679648a, 15, 1, 0}, + {0x667982c1, 57, 1, 792}, + {0x667eebb4, 0, 1, 0}, + {0x66a0c7f2, 92, 0, 86}, + {0x66b2dec7, 24, 0, 0}, + {0x66d65a58, 126, 0, 0}, + {0x66dd04e1, 190, 0, 0}, + {0x66e64e41, 92, 0, 0}, + {0x66ed9c00, 161, 0, 0}, + {0x66f11648, 126, 1, 1085}, + {0x66f4d9f5, 128, 0, 0}, + {0x66f6a39e, 34, 2, 0}, + {0x6701a19f, 92, 0, 0}, + {0x67193fc8, 0, 1, 0}, + {0x671f23a8, 100, 2, 0}, + {0x6720abac, 129, 2, 0}, + {0x6756763b, 24, 0, 0}, + {0x6772ca86, 20, 0, 0}, + {0x67751094, 100, 1, 689}, + {0x67755041, 160, 0, 0}, + {0x6776a977, 54, 0, 0}, + {0x67811da6, 46, 1, 290}, + {0x67a0d115, 164, 0, 0}, + {0x67a3c362, 93, 0, 0}, + {0x67c89f06, 126, 1, 1088}, + {0x67d5c3f9, 37, 0, 0}, + {0x67e1c05c, 126, 1, 0}, + {0x67f77118, 97, 1, 690}, + {0x6800c5b3, 147, 0, 0}, + {0x6817857d, 74, 1, 0}, + {0x6823cbc8, 77, 0, 0}, + {0x682d2df1, 0, 1, 0}, + {0x68309d06, 0, 1, 0}, + {0x68383607, 65, 1, 1089}, + {0x684afccd, 97, 0, 0}, + {0x684b292f, 117, 0, 0}, + {0x685257a8, 92, 1, 690}, + {0x687ebcd7, 156, 0, 0}, + {0x6893f7b3, 150, 1, 791}, + {0x689971f9, 153, 1, 689}, + {0x68a8cf73, 92, 1, 391}, + {0x68afef5f, 130, 1, 0}, + {0x68c83aa3, 115, 1, 790}, + {0x68e8a520, 97, 0, 0}, + {0x68ec97cb, 97, 1, 689}, + {0x68ecb923, 197, 0, 0}, + {0x68f9b5f5, 129, 2, 0}, + {0x6901346e, 117, 0, 0}, + {0x6906e0da, 100, 1, 292}, + {0x69240dcf, 63, 0, 0}, + {0x692c1c3c, 100, 0, 0}, + {0x6944a01a, 116, 1, 291}, + {0x69488c31, 0, 2, 0}, + {0x69565f13, 54, 0, 0}, + {0x695cb142, 92, 0, 0}, + {0x69635a6e, 68, 1, 290}, + {0x69684e1e, 24, 1, 192}, + {0x696d7839, 165, 1, 992}, + {0x697e10f6, 74, 1, 1191}, + {0x69885e71, 126, 0, 0}, + {0x6997f5e1, 115, 1, 690}, + {0x699fa085, 4, 1, 1288}, + {0x69bcdb8b, 80, 0, 0}, + {0x69d07ddb, 92, 0, 0}, + {0x69da7649, 126, 1, 0}, + {0x69e534eb, 69, 0, 0}, + {0x6a10add2, 193, 0, 0}, + {0x6a154b68, 6, 1, 1088}, + {0x6a1f628a, 97, 1, 1289}, + {0x6a221cbf, 59, 0, 0}, + {0x6a27567e, 156, 1, 1192}, + {0x6a483073, 40, 1, 0}, + {0x6a50b553, 100, 0, 0}, + {0x6a5abfac, 34, 1, 892}, + {0x6a6b7239, 195, 0, 0}, + {0x6a80de01, 126, 2, 0}, + {0x6a82ac89, 160, 1, 188}, + {0x6a88579f, 65, 1, 1089}, + {0x6aad2985, 38, 0, 0}, + {0x6ae69227, 164, 0, 0}, + {0x6af5f21b, 100, 1, 193}, + {0x6b403c04, 97, 0, 0}, + {0x6b523bd7, 33, 1, 0}, + {0x6b53006a, 29, 1, 1289}, + {0x6b53222c, 117, 0, 0}, + {0x6b53d59a, 0, 1, 0}, + {0x6b5ca5ae, 46, 0, 0}, + {0x6b8d777d, 0, 1, 0}, + {0x6b8f23e0, 0, 1, 0}, + {0x6b999aaf, 34, 1, 1190}, + {0x6bb6a0ce, 76, 1, 1190}, + {0x6bbd9f11, 161, 0, 0}, + {0x6bbe2be5, 92, 0, 0}, + {0x6bc33d2f, 2, 1, 492}, + {0x6bc65d7e, 92, 0, 0}, + {0x6bd7dafe, 100, 0, 0}, + {0x6be54033, 100, 0, 0}, + {0x6bfe4ff1, 117, 0, 0}, + {0x6c040686, 33, 1, 0}, + {0x6c1390b4, 77, 0, 0}, + {0x6c15f90e, 196, 1, 292}, + {0x6c168561, 92, 1, 190}, + {0x6c3ca47b, 72, 0, 0}, + {0x6c42b2c6, 77, 1, 1290}, + {0x6c438bb2, 160, 0, 0}, + {0x6c5dbedf, 175, 0, 0}, + {0x6c61b622, 92, 0, 0}, + {0x6c6c2feb, 24, 0, 0}, + {0x6c70a17b, 193, 0, 0}, + {0x6c93377c, 33, 1, 0}, + {0x6cc62c06, 126, 0, 0}, + {0x6cd204b5, 77, 0, 0}, + {0x6cd46979, 24, 1, 289}, + {0x6cd9cc23, 128, 0, 0}, + {0x6cdc0cd9, 160, 0, 0}, + {0x6cf4d165, 10, 1, 0}, + {0x6d2b7846, 34, 0, 0}, + {0x6d3396e3, 0, 1, 0}, + {0x6d612468, 65, 1, 392}, + {0x6d65cac6, 124, 0, 0}, + {0x6d867d34, 126, 1, 490}, + {0x6d9ba342, 163, 1, 489}, + {0x6daaa704, 126, 0, 0}, + {0x6db30701, 46, 0, 86}, + {0x6dc28b5a, 100, 0, 0}, + {0x6dce4b23, 164, 0, 0}, + {0x6de472f4, 89, 0, 0}, + {0x6decd886, 143, 1, 689}, + {0x6e0eb43e, 50, 1, 690}, + {0x6e16adac, 126, 0, 0}, + {0x6e2e53c3, 126, 0, 0}, + {0x6e32f3c2, 97, 0, 0}, + {0x6e4697bf, 9, 0, 0}, + {0x6e4c0641, 174, 0, 0}, + {0x6e4dcfd2, 115, 1, 592}, + {0x6e53128e, 160, 0, 0}, + {0x6e53eaa9, 117, 0, 0}, + {0x6e593fc7, 0, 2, 0}, + {0x6e6490cd, 143, 1, 291}, + {0x6e68e31a, 24, 0, 0}, + {0x6e72b8ff, 166, 1, 0}, + {0x6e85d8dd, 146, 1, 889}, + {0x6e899ccf, 43, 0, 0}, + {0x6e9a5b6f, 46, 1, 188}, + {0x6ec51de5, 117, 0, 0}, + {0x6ec74e4c, 92, 0, 0}, + {0x6ed3ba25, 40, 1, 0}, + {0x6ee4bb0a, 34, 1, 1287}, + {0x6ee68312, 77, 1, 690}, + {0x6ee94d32, 4, 1, 991}, + {0x6f10097d, 4, 1, 91}, // Simpsons - Bart Vs the Space Mutants, The (U) + {0x6f27300b, 129, 2, 0}, + {0x6f365e7b, 156, 0, 0}, + {0x6f3d2737, 4, 1, 991}, + {0x6f4e4312, 99, 0, 0}, + {0x6f5d9b2a, 89, 0, 0}, + {0x6f5f60b9, 77, 0, 0}, + {0x6f605522, 77, 1, 989}, + {0x6f6686b0, 89, 0, 0}, + {0x6f742ef2, 115, 1, 993}, + {0x6f860e89, 71, 2, 0}, + {0x6f8af3e8, 100, 1, 190}, + {0x6f96ed15, 191, 0, 0}, + {0x6f97c721, 126, 1, 686}, + {0x6fa0e650, 77, 1, 291}, + {0x6fb349e2, 74, 1, 0}, + {0x6fb51ffb, 34, 0, 0}, + {0x6fd46392, 97, 1, 190}, + {0x6fd5a271, 92, 1, 391}, + {0x70080810, 126, 1, 0}, + {0x701b1adf, 77, 0, 0}, + {0x702d9b33, 195, 0, 0}, + {0x703e1948, 126, 1, 1088}, + {0x704ad587, 34, 0, 0}, + {0x705bd7c3, 133, 0, 0}, + {0x705e13ca, 77, 0, 0}, + {0x705ef660, 156, 0, 0}, + {0x7062f3fa, 100, 1, 491}, + {0x7071eef2, 75, 0, 0}, + {0x7077a85d, 0, 1, 0}, + {0x7077b075, 127, 1, 493}, + {0x7080d1f8, 174, 0, 0}, + {0x708ea2be, 126, 0, 0}, + {0x7090b851, 53, 0, 0}, + {0x7095ac65, 100, 0, 0}, + {0x709c9399, 165, 2, 0}, + {0x70a1f862, 143, 1, 887}, + {0x70a6bfc0, 75, 0, 0}, + {0x70bb52d7, 34, 0, 0}, + {0x70ce3771, 196, 1, 1192}, + {0x70d49617, 66, 0, 0}, + {0x70e0b7d8, 156, 0, 0}, + {0x70f4dadb, 91, 2, 0}, + {0x70f67ab7, 148, 0, 0}, + {0x711896b8, 156, 1, 1288}, + {0x711c2b0e, 117, 0, 0}, + {0x713309ba, 0, 1, 0}, + {0x713cfeb9, 92, 0, 0}, + {0x71547d94, 130, 1, 0}, + {0x7154acb5, 166, 1, 0}, + {0x7156cb4d, 74, 1, 1190}, + {0x716c3fce, 174, 0, 0}, + {0x716daea5, 119, 0, 0}, + {0x7172f3d4, 72, 0, 0}, + {0x717e1169, 100, 0, 0}, + {0x717e1c46, 188, 1, 1290}, + {0x71b107e7, 156, 0, 0}, + {0x71baecec, 129, 2, 0}, + {0x71c9ed1e, 133, 0, 0}, + {0x71d868c4, 0, 1, 0}, + {0x71d8c6e9, 133, 0, 0}, + {0x720fff06, 124, 0, 0}, + {0x72125764, 156, 0, 0}, + {0x721b5217, 180, 0, 0}, + {0x721be58a, 97, 1, 88}, + {0x72342953, 77, 0, 0}, + {0x725b0234, 126, 2, 0}, + {0x7262843c, 92, 1, 1192}, + {0x7265a393, 178, 0, 0}, + {0x7266b1b3, 126, 0, 0}, + {0x726edb66, 89, 0, 0}, + {0x72725a0b, 193, 1, 988}, + {0x72823de0, 100, 0, 0}, + {0x7287d1c0, 153, 1, 292}, + {0x728bfa8d, 156, 0, 0}, + {0x728c3d98, 164, 0, 0}, + {0x72924076, 156, 0, 0}, + {0x72928698, 156, 0, 0}, + {0x72a0d9a7, 6, 1, 1290}, + {0x72c39b9e, 143, 0, 0}, + {0x72d81daa, 100, 0, 0}, + {0x72dd850d, 43, 1, 89}, + {0x72ddbd39, 100, 2, 0}, + {0x72e2395b, 0, 1, 0}, + {0x72e32402, 179, 0, 0}, + {0x72e65d5c, 117, 0, 0}, + {0x72e66392, 40, 1, 0}, + {0x73140eef, 200, 1, 0}, + {0x73272e0a, 77, 0, 0}, + {0x7329118d, 150, 1, 0}, + {0x732b0675, 126, 0, 0}, + {0x73620901, 57, 1, 1188}, + {0x736825f6, 53, 0, 0}, + {0x7376568d, 100, 0, 0}, + {0x737dd1bf, 126, 0, 0}, + {0x737ec182, 38, 0, 0}, + {0x73921674, 96, 0, 0}, + {0x739a1027, 77, 0, 0}, + {0x73ac76db, 24, 0, 0}, + {0x73ad02b7, 165, 0, 0}, + {0x73c7fcf4, 34, 1, 693}, + {0x73cefba7, 0, 1, 0}, + {0x73e41ac7, 150, 0, 0}, + {0x73f7e5d8, 79, 0, 0}, + {0x73ff22a9, 46, 0, 0}, + {0x740c7582, 34, 1, 1191}, + {0x74189e12, 120, 1, 691}, + {0x743387ff, 100, 0, 0}, + {0x7434dc82, 0, 1, 0}, + {0x74386f15, 173, 1, 1089}, + {0x74452e18, 77, 0, 0}, + {0x74663267, 43, 0, 0}, + {0x7474ac92, 68, 1, 191}, + {0x749dc135, 123, 1, 989}, + {0x74b8498e, 0, 1, 0}, + {0x74c08f86, 65, 1, 91}, + {0x7510180a, 126, 1, 1189}, + {0x751fedb7, 77, 0, 0}, + {0x75255f88, 143, 1, 989}, + {0x752743ec, 34, 0, 0}, + {0x753768a6, 12, 1, 1191}, + {0x75547d3e, 165, 0, 0}, + {0x758afbbd, 128, 0, 0}, + {0x75901b18, 150, 0, 0}, + {0x7595c7fc, 46, 1, 1090}, + {0x759c40a1, 89, 0, 0}, + {0x75a7e399, 174, 0, 0}, + {0x75a8943b, 123, 1, 790}, + {0x75b3eb37, 92, 0, 0}, + {0x75b9c0db, 46, 2, 0}, + {0x75ce31c0, 127, 1, 693}, + {0x75f6a9f3, 126, 1, 686}, + {0x75f96142, 92, 0, 0}, + {0x761ccfb5, 117, 0, 0}, + {0x762c72ae, 31, 1, 0}, + {0x764bea6b, 89, 0, 0}, + {0x766727b2, 163, 1, 189}, + {0x766c2cac, 126, 0, 0}, + {0x7671bc51, 34, 0, 0}, + {0x7677a6a6, 38, 0, 0}, + {0x7678f1d5, 160, 0, 0}, + {0x767a6a66, 92, 0, 0}, + {0x768a1b6a, 192, 0, 0}, + {0x76a654b9, 21, 0, 0}, + {0x76b56d0a, 89, 0, 0}, + {0x76c161e3, 77, 2, 0}, + {0x76d5175e, 117, 0, 0}, + {0x76e85ada, 69, 0, 0}, + {0x76fff9d5, 156, 0, 0}, + {0x7709a696, 35, 0, 0}, + {0x771c8855, 0, 1, 0}, + {0x772513f4, 161, 0, 0}, + {0x77415ad3, 178, 0, 0}, + {0x77512388, 69, 0, 0}, + {0x7751588d, 126, 2, 0}, // Metroid (E) [!] + {0x77540bb5, 143, 1, 788}, + {0x775b8ed9, 89, 0, 0}, + {0x77833016, 24, 1, 1086}, + {0x778aaf25, 92, 1, 788}, + {0x77bf8b23, 57, 1, 689}, + {0x77c51d28, 120, 0, 0}, + {0x77dafd89, 46, 1, 1086}, + {0x77dcbba3, 69, 0, 0}, + {0x7826fd3e, 163, 1, 390}, + {0x7831b2ff, 72, 0, 0}, + {0x7840b18d, 150, 0, 0}, + {0x7848dad8, 173, 0, 0}, + {0x786148b6, 100, 0, 0}, + {0x7866fb2c, 160, 0, 0}, + {0x7876e439, 72, 0, 0}, + {0x78784cbf, 14, 1, 190}, + {0x787b41cc, 46, 1, 987}, + {0x7884b56e, 109, 1, 192}, + {0x7889f720, 166, 1, 0}, + {0x788bed9a, 89, 0, 0}, + {0x78a48b23, 126, 0, 0}, + {0x78b09986, 100, 0, 0}, + {0x78b657ac, 81, 0, 0}, + {0x78bb266a, 126, 1, 1088}, + {0x78bd3c9a, 34, 0, 0}, + {0x78bdf588, 100, 0, 0}, + {0x78bff6b0, 133, 0, 0}, + {0x78c4460d, 162, 0, 0}, + {0x78dfb5d3, 143, 0, 0}, + {0x78fcf4fa, 157, 0, 0}, + {0x790d2916, 165, 2, 0}, + {0x791138d9, 126, 1, 686}, + {0x79123682, 0, 1, 0}, + {0x79149ebf, 182, 1, 688}, + {0x7918f29c, 24, 0, 0}, + {0x792070a9, 33, 1, 0}, + {0x7936bc91, 66, 0, 0}, + {0x7950b715, 89, 1, 987}, + {0x795d23ec, 5, 1, 592}, + {0x79698b98, 143, 0, 0}, + {0x7980c4f7, 24, 0, 0}, + {0x798ac9dc, 46, 0, 0}, + {0x798b491d, 66, 0, 0}, + {0x798eeb98, 34, 1, 1291}, + {0x798fbc68, 165, 0, 0}, + {0x799ad3c6, 115, 1, 993}, + {0x79bfe095, 0, 1, 0}, + {0x79c91906, 124, 0, 0}, + {0x79cac545, 24, 0, 0}, + {0x79d8c39d, 126, 1, 1188}, + {0x79f688bc, 115, 2, 0}, + {0x79fba5a2, 21, 0, 0}, + {0x79ff3562, 66, 0, 0}, + {0x7a11d2c9, 72, 0, 0}, + {0x7a365bf8, 0, 1, 0}, + {0x7a373833, 163, 1, 191}, + {0x7a424c07, 104, 1, 989}, + {0x7a497ae3, 160, 0, 0}, + {0x7a4d4eaf, 163, 1, 291}, + {0x7a508dbb, 100, 0, 0}, + {0x7a6e0454, 0, 1, 0}, + {0x7a748058, 9, 0, 0}, + {0x7a76e057, 39, 0, 0}, + {0x7a8ae523, 117, 0, 0}, + {0x7ae0bf3c, 126, 0, 0}, + {0x7ae5c002, 77, 2, 91}, // Jackie Chan's Action Kung Fu (E) + {0x7aee2161, 179, 0, 0}, + {0x7af3afb7, 156, 0, 0}, + {0x7af8204a, 77, 0, 0}, + {0x7b0a41b9, 92, 0, 0}, + {0x7b1b4dd0, 89, 1, 790}, + {0x7b35964e, 100, 1, 192}, + {0x7b44fb2a, 34, 0, 0}, + {0x7b4ed0bb, 104, 1, 1193}, + {0x7b5206af, 24, 0, 0}, + {0x7b5bd2de, 126, 1, 686}, + {0x7b6dc772, 117, 0, 0}, + {0x7b72fba4, 128, 0, 0}, + {0x7b82400f, 174, 0, 0}, + {0x7bb5664f, 117, 0, 0}, + {0x7bc98e11, 68, 1, 788}, + {0x7bd7b849, 164, 0, 0}, + {0x7bd8f902, 77, 0, 0}, + {0x7be93173, 4, 1, 190}, + {0x7bec1745, 160, 0, 0}, + {0x7bf8a890, 165, 0, 0}, + {0x7c27ab86, 128, 0, 0}, + {0x7c393da4, 100, 0, 0}, + {0x7c3d2ea3, 178, 0, 0}, + {0x7c40d6c6, 0, 1, 0}, + {0x7c46998b, 2, 0, 0}, + {0x7c4a72d8, 165, 1, 0}, + {0x7c596e45, 53, 0, 0}, + {0x7c6a3d51, 34, 1, 1088}, + {0x7c6f615f, 158, 1, 192}, + {0x7c7ab58e, 117, 0, 0}, + {0x7c854039, 117, 0, 0}, + {0x7c864796, 160, 0, 0}, + {0x7ca47b40, 0, 1, 0}, + {0x7ca52798, 22, 0, 0}, + {0x7cba563f, 92, 0, 0}, + {0x7cc4778c, 100, 0, 0}, + {0x7cc9c669, 0, 1, 0}, + {0x7ce3dab2, 166, 1, 0}, + {0x7cff0f84, 126, 0, 0}, + {0x7d139211, 195, 0, 0}, + {0x7d223c3d, 117, 0, 0}, + {0x7d280346, 126, 1, 1085}, + {0x7d32acb9, 133, 0, 0}, + {0x7d38027a, 50, 1, 1092}, + {0x7d44b948, 156, 0, 0}, + {0x7d4caf6c, 0, 2, 0}, + {0x7d55cf29, 133, 0, 0}, + {0x7d56840a, 126, 0, 0}, + {0x7d5ca373, 155, 0, 0}, + {0x7d9ad28f, 77, 0, 0}, + {0x7d9d214b, 24, 1, 1086}, + {0x7da80b1b, 117, 0, 0}, + {0x7da8aead, 134, 0, 0}, + {0x7dab215c, 0, 1, 0}, + {0x7dc49898, 0, 1, 0}, + {0x7dcb4c18, 13, 1, 492}, + {0x7dcbea73, 166, 1, 0}, + {0x7dd82754, 143, 0, 0}, + {0x7e036525, 56, 2, 0}, + {0x7e053e64, 117, 0, 0}, + {0x7e26c7d0, 100, 1, 0}, + {0x7e4966e4, 117, 0, 0}, + {0x7e4ba78f, 0, 2, 0}, + {0x7e57fbec, 104, 1, 392}, + {0x7e5d2f1a, 2, 0, 0}, + {0x7e704a14, 24, 0, 0}, + {0x7e990ac3, 166, 1, 0}, + {0x7ec6f75b, 193, 0, 0}, + {0x7ed91f80, 10, 1, 0}, + {0x7ee02ca2, 133, 0, 0}, + {0x7ee625eb, 100, 0, 0}, + {0x7f0ad375, 117, 0, 0}, + {0x7f17c89f, 160, 0, 0}, + {0x7f1d087f, 69, 0, 0}, + {0x7f24efc0, 200, 1, 0}, + {0x7f2a04bf, 156, 0, 0}, + {0x7f397886, 180, 0, 0}, + {0x7f45cff5, 92, 0, 0}, + {0x7f531249, 6, 1, 291}, + {0x7f5f4e4a, 117, 0, 0}, + {0x7f68c629, 66, 0, 0}, + {0x7f7156a7, 68, 1, 489}, + {0x7f7f2821, 157, 0, 0}, + {0x7f7fef6e, 13, 1, 692}, + {0x7f93fb41, 99, 1, 1089}, + {0x7fb72a2c, 57, 1, 191}, + {0x7fb74a43, 160, 1, 1291}, + {0x7fb799fd, 24, 0, 0}, + {0x7fcc340a, 100, 0, 0}, + {0x7fedc0d7, 69, 0, 0}, + {0x7ff76219, 165, 1, 490}, + {0x7ffabb4c, 192, 0, 0}, + {0x801931af, 0, 1, 0}, + {0x801a6d23, 100, 0, 0}, + {0x80250d64, 129, 2, 0}, + {0x803b9979, 81, 0, 0}, + {0x804a0570, 104, 1, 1091}, + {0x804ef7f9, 0, 1, 0}, + {0x804f898a, 21, 0, 0}, + {0x805f81bc, 100, 0, 0}, + {0x80638505, 127, 2, 0}, + {0x808606f0, 117, 0, 0}, + {0x808b89c0, 158, 1, 1193}, + {0x809c3878, 90, 0, 0}, + {0x80c15e96, 95, 0, 0}, + {0x80c227ad, 182, 0, 0}, + {0x80c41616, 0, 1, 0}, + {0x80cd1919, 126, 2, 0}, + {0x80f0d0c0, 0, 1, 0}, + {0x80fb117e, 126, 1, 394}, + {0x80fb7e6b, 126, 0, 0}, + {0x81069812, 126, 1, 1188}, + {0x8106e694, 156, 2, 0}, + {0x810b7ab9, 92, 1, 1288}, + {0x810f3cf0, 34, 0, 0}, + {0x8111ba08, 182, 1, 990}, + {0x811f06d9, 24, 1, 388}, + {0x81293104, 8, 0, 0}, + {0x8161d619, 97, 1, 1288}, + {0x8168080f, 95, 0, 0}, + {0x8172fc55, 25, 0, 0}, + {0x817cfa97, 16, 0, 0}, + {0x818ce8aa, 72, 0, 0}, + {0x8192c804, 126, 0, 0}, + {0x8192d2e7, 46, 1, 1187}, + {0x81a15eb8, 24, 0, 0}, + {0x81a5eb65, 100, 1, 493}, + {0x81a743f5, 96, 0, 0}, + {0x81af4af9, 129, 2, 0}, + {0x81b2a3cd, 100, 2, 0}, + {0x81b7f1a8, 117, 0, 0}, + {0x81bb48c2, 100, 0, 0}, + {0x81d9f17c, 165, 1, 690}, + {0x81df3d70, 24, 0, 0}, + {0x81ecda0d, 15, 1, 0}, + {0x81f31409, 34, 0, 0}, + {0x8207a96c, 156, 1, 987}, + {0x82132616, 117, 0, 0}, + {0x8218c637, 97, 0, 0}, + {0x821f2f9f, 143, 0, 0}, + {0x821feb7a, 156, 0, 0}, + {0x822f17eb, 176, 0, 0}, + {0x8236c523, 34, 0, 0}, + {0x827e5df5, 126, 1, 0}, + {0x8288323f, 74, 1, 1291}, + {0x828d779b, 0, 1, 0}, + {0x828f8f1f, 97, 0, 0}, + {0x82ab60b9, 34, 0, 0}, + {0x82ad545e, 66, 0, 0}, + {0x82afa828, 193, 1, 190}, + {0x82be4724, 34, 1, 1186}, + {0x82cfde25, 126, 1, 288}, + {0x82dff13d, 126, 1, 388}, + {0x82f48389, 193, 0, 0}, + {0x82fcadde, 104, 1, 790}, + {0x8308fed7, 143, 0, 0}, + {0x831f8294, 80, 0, 0}, + {0x831f9c1a, 15, 1, 0}, + {0x83213ca0, 0, 1, 0}, + {0x832c3b9f, 126, 0, 0}, + {0x834273da, 195, 0, 0}, + {0x83431081, 54, 0, 0}, + {0x8348c788, 6, 1, 492}, + {0x834d1924, 166, 1, 0}, + {0x8351283e, 0, 1, 0}, + {0x8360fa88, 100, 0, 0}, + {0x836c4fa7, 89, 0, 0}, + {0x836cc1ab, 100, 0, 0}, + {0x836fe2c2, 100, 2, 0}, + {0x837c1342, 160, 0, 0}, + {0x838bf76f, 195, 0, 0}, + {0x839f2f71, 188, 0, 0}, + {0x83be000c, 34, 1, 88}, + {0x83c28d94, 128, 0, 0}, + {0x83cb743f, 93, 0, 0}, + {0x83ea7b04, 25, 0, 0}, + {0x83fc38f8, 163, 1, 489}, + {0x84148f73, 92, 1, 0}, + {0x841b69b6, 30, 1, 492}, + {0x8437e478, 156, 0, 0}, + {0x84382231, 126, 0, 0}, + {0x8441a9db, 126, 1, 1085}, + {0x8442b86c, 92, 0, 0}, + {0x847d672d, 100, 1, 491}, + {0x84b930a9, 74, 1, 1090}, + {0x84be00e9, 126, 0, 0}, + {0x84d51076, 158, 1, 191}, + {0x84efd927, 53, 0, 0}, + {0x84f7fc31, 34, 2, 0}, + {0x850090bc, 97, 0, 0}, + {0x8531c166, 174, 0, 0}, + {0x85323fd6, 15, 1, 0}, + {0x85498d45, 54, 0, 0}, + {0x854f4661, 92, 0, 0}, + {0x85534474, 77, 0, 0}, + {0x856114c8, 100, 1, 87}, + {0x856e7600, 97, 0, 0}, + {0x8575a0cb, 180, 0, 0}, + {0x857dbc36, 129, 2, 0}, + {0x858863af, 42, 0, 0}, + {0x8593e5ad, 142, 1, 491}, + {0x859c65e1, 143, 1, 790}, + {0x85a922fd, 66, 0, 0}, + {0x85bc0777, 72, 0, 0}, + {0x85c5953f, 97, 0, 0}, + {0x85c5b6b4, 164, 0, 0}, + {0x85cf16e0, 0, 1, 0}, + {0x85d02cd4, 97, 1, 0}, // "Proto" + {0x85d75d58, 24, 1, 1289}, + {0x85e0090b, 161, 0, 0}, + {0x85e1a22a, 92, 0, 0}, + {0x85f12d37, 20, 0, 0}, + {0x85f58367, 15, 1, 0}, + {0x86167220, 126, 0, 0}, + {0x86277361, 104, 1, 894}, + {0x86291f44, 160, 0, 0}, + {0x862ab1e5, 67, 1, 89}, + {0x862c4e8d, 160, 0, 0}, + {0x8635fed1, 97, 0, 0}, + {0x863cea81, 23, 0, 0}, + {0x865ac145, 117, 0, 0}, + {0x86670c93, 126, 1, 887}, + {0x86759c0f, 175, 0, 0}, + {0x8685f366, 136, 0, 0}, + {0x86964edd, 100, 1, 294}, + {0x869652fe, 97, 1, 1088}, + {0x86974ccc, 200, 1, 0}, + {0x86a39645, 117, 0, 0}, + {0x86a637b0, 0, 1, 0}, + {0x86b0d1cf, 196, 1, 1191}, + {0x86b2355f, 156, 0, 0}, + {0x86c495c6, 34, 2, 0}, + {0x86ca126d, 117, 0, 0}, + {0x86cbc595, 4, 1, 290}, + {0x86d67a97, 68, 1, 390}, + {0x86f07508, 177, 0, 0}, + {0x87052ac0, 66, 0, 0}, + {0x87133e90, 53, 0, 0}, + {0x8751abe5, 95, 0, 0}, + {0x8752dccb, 81, 0, 0}, + {0x87645f89, 142, 1, 1289}, + {0x876b80bd, 66, 0, 0}, + {0x8776c0c2, 24, 1, 791}, + {0x877dba77, 25, 0, 0}, + {0x87a3f91e, 179, 0, 0}, + {0x87b2a02c, 77, 0, 0}, + {0x87bad69b, 164, 0, 0}, + {0x87c6a8da, 0, 1, 0}, + {0x87ce3f34, 97, 0, 0}, + {0x87ed54aa, 0, 1, 0}, + {0x87f699dc, 97, 1, 990}, + {0x88053d25, 53, 0, 0}, + {0x88062d9a, 164, 0, 0}, + {0x882e1901, 15, 1, 0}, + {0x88338ed5, 92, 1, 392}, + {0x883875b1, 53, 0, 0}, + {0x885acc2b, 126, 1, 1085}, + {0x8871b5c4, 100, 1, 692}, + {0x88739adf, 201, 0, 0}, + {0x8889c564, 51, 1, 1190}, + {0x889129cb, 126, 1, 1290}, + {0x8897a8f1, 100, 2, 0}, + {0x88a6b192, 15, 1, 0}, + {0x88aa8cd8, 46, 1, 1091}, + {0x88be42e7, 173, 0, 0}, + {0x88c15cf3, 0, 1, 0}, + {0x88c30fda, 84, 2, 0}, + {0x88c83a1d, 100, 0, 0}, + {0x88d3b565, 0, 1, 0}, + {0x88e1a5f4, 143, 1, 988}, + {0x8904149e, 182, 2, 0}, + {0x890d226e, 0, 1, 0}, + {0x892434dd, 33, 1, 0}, + {0x8927fd4c, 22, 1, 991}, + {0x89550500, 72, 0, 0}, + {0x89567668, 46, 0, 0}, + {0x895cbaf8, 34, 2, 0}, + {0x895faef2, 117, 0, 0}, + {0x8963ae6e, 126, 1, 1085}, + {0x8965c590, 34, 1, 1191}, + {0x89688cd5, 143, 0, 0}, + {0x898e4232, 126, 2, 0}, + {0x89984244, 196, 2, 0}, + {0x899a0067, 117, 0, 0}, + {0x89a45446, 153, 2, 0}, + {0x89c80b65, 99, 0, 0}, + {0x89e085fe, 180, 0, 0}, + {0x89f8ebda, 90, 0, 0}, + {0x8a043cd6, 193, 1, 690}, + {0x8a093f62, 169, 0, 0}, + {0x8a0c7337, 126, 2, 0}, + {0x8a20be5f, 143, 1, 788}, + {0x8a2824bb, 117, 0, 0}, + {0x8a368744, 126, 0, 0}, + {0x8a5b72c0, 24, 0, 0}, + {0x8a5bc0d3, 165, 0, 0}, + {0x8a640aef, 4, 1, 190}, + {0x8a65baff, 126, 2, 0}, + {0x8a7d0abe, 160, 0, 0}, + {0x8a82f9c2, 122, 0, 0}, + {0x8a9586f5, 143, 1, 1087}, + {0x8a96e00d, 100, 0, 0}, + {0x8aa4ace0, 0, 1, 0}, + {0x8aa7cce1, 179, 0, 0}, + {0x8ab52a24, 100, 1, 1288}, + {0x8acafe51, 100, 2, 0}, + {0x8ada3497, 115, 1, 190}, + {0x8af25130, 117, 0, 0}, + {0x8afec38f, 77, 0, 0}, + {0x8b03f74d, 100, 0, 0}, + {0x8b0e1235, 34, 0, 0}, + {0x8b1abbe2, 0, 1, 0}, + {0x8b2e3e81, 126, 1, 1085}, + {0x8b4a2866, 179, 0, 0}, + {0x8b56faeb, 193, 1, 1288}, + {0x8b59bac3, 165, 0, 0}, + {0x8b5a9d69, 126, 1, 0}, + {0x8b5c74db, 40, 1, 0}, + {0x8b60cc58, 126, 0, 0}, + {0x8b764fe5, 100, 0, 0}, + {0x8b781d39, 15, 1, 0}, + {0x8b957b50, 43, 1, 389}, + {0x8b9d3e9c, 126, 1, 1087}, + {0x8ba75848, 151, 0, 0}, + {0x8baa5ff2, 100, 0, 0}, + {0x8baedc0e, 112, 0, 0}, + {0x8bbc37a2, 69, 0, 0}, + {0x8bc2ec0c, 117, 0, 0}, + {0x8bca5146, 160, 1, 391}, + {0x8bcb0993, 39, 0, 0}, + {0x8be59ea9, 74, 1, 0}, + {0x8bf29cb6, 34, 1, 690}, + {0x8c0c2df5, 100, 0, 0}, + {0x8c1e1336, 24, 1, 1086}, + {0x8c37a7d5, 126, 0, 0}, + {0x8c399acd, 117, 0, 0}, + {0x8c3d54e8, 93, 0, 0}, + {0x8c46a487, 171, 0, 0}, + {0x8c4d59d6, 2, 0, 0}, + {0x8c5a784e, 53, 1, 990}, + {0x8c6237fd, 89, 0, 0}, + {0x8c6631c8, 156, 0, 0}, + {0x8c71f706, 40, 1, 0}, + {0x8c75a0b6, 66, 0, 0}, + {0x8c8dedb6, 34, 1, 392}, + {0x8c8fa83b, 24, 0, 0}, + {0x8c940984, 17, 1, 1190}, + {0x8cb9eff8, 24, 1, 1086}, + {0x8ce478db, 99, 1, 491}, + {0x8ce4875e, 92, 1, 0}, + {0x8ce8e264, 200, 1, 0}, + {0x8ce99fe4, 24, 0, 0}, + {0x8ce9c87b, 150, 0, 0}, + {0x8cea87c5, 120, 0, 0}, + {0x8cf1aae2, 156, 0, 0}, + {0x8d2454b7, 92, 0, 0}, + {0x8d2488d6, 146, 1, 1190}, + {0x8d26fdea, 25, 0, 0}, + {0x8d3c33b3, 46, 0, 0}, + {0x8d5b77c0, 97, 0, 0}, + {0x8d605a06, 38, 0, 0}, + {0x8d77e5e6, 72, 0, 0}, + {0x8d94797f, 97, 1, 1092}, + {0x8d9af7af, 188, 1, 990}, + {0x8da4e539, 181, 0, 0}, + {0x8da651d4, 34, 1, 990}, + {0x8da6667d, 126, 2, 0}, + {0x8db6d11f, 126, 1, 0}, + {0x8dcc9949, 15, 1, 0}, + {0x8dcd9486, 69, 0, 0}, + {0x8dd0a0c4, 95, 0, 0}, + {0x8dd92725, 68, 1, 991}, + {0x8de76f1d, 114, 1, 389}, + {0x8dedea07, 0, 1, 0}, + {0x8e0d9179, 160, 0, 0}, + {0x8e0ea20f, 24, 0, 0}, + {0x8e1e1181, 117, 0, 0}, + {0x8e2bd25c, 126, 1, 1085}, + {0x8e373118, 201, 0, 0}, + {0x8e504a98, 77, 1, 490}, + {0x8e5c2818, 165, 1, 1291}, + {0x8e7abdfc, 180, 0, 0}, + {0x8e86d6c0, 6, 1, 1289}, + {0x8e9a5e2f, 99, 1, 1191}, + {0x8eab381c, 15, 1, 0}, + {0x8eb11e94, 171, 0, 0}, + {0x8ecbc577, 3, 2, 0}, + {0x8ed0547e, 128, 0, 0}, + {0x8edeb257, 24, 0, 0}, + {0x8ee25f78, 160, 0, 0}, + {0x8ee6463a, 202, 0, 0}, + {0x8ee7c43e, 97, 1, 493}, + {0x8ee98523, 126, 0, 0}, + {0x8eef8b76, 24, 0, 0}, + {0x8f011713, 124, 0, 0}, + {0x8f3f8b1f, 89, 0, 0}, + {0x8f4497ee, 145, 0, 0}, + {0x8f506d75, 178, 0, 0}, + {0x8f628d51, 43, 0, 0}, + {0x8f726dbc, 156, 0, 0}, + {0x8f7719f3, 100, 0, 0}, + {0x8f81d008, 38, 0, 0}, + {0x8fa95456, 99, 0, 0}, + {0x8fb0cef1, 103, 1, 894}, + {0x8fdb8f62, 179, 0, 0}, + {0x900a55db, 117, 0, 0}, + {0x900c7442, 100, 0, 0}, + {0x900cf570, 126, 0, 0}, + {0x900e3a23, 147, 0, 0}, + {0x90226e40, 13, 1, 692}, + {0x902e3168, 165, 1, 891}, + {0x90388d1b, 99, 1, 1089}, + {0x90491713, 165, 1, 787}, + {0x90597545, 133, 0, 0}, + {0x9077a623, 126, 1, 593}, + {0x908505ee, 156, 0, 0}, + {0x90a33263, 34, 1, 1289}, + {0x90c3f886, 24, 0, 0}, + {0x90c773c1, 92, 1, 1192}, + {0x90ca616d, 126, 1, 1085}, + {0x90d3210a, 33, 1, 0}, + {0x90d68a43, 3, 1, 990}, + {0x90ecdade, 165, 0, 0}, + {0x90f12ac8, 0, 1, 0}, + {0x90f6fa33, 100, 0, 0}, + {0x90faa618, 117, 0, 0}, + {0x910cc30f, 117, 0, 0}, + {0x91209041, 92, 1, 291}, + {0x912b790e, 175, 0, 0}, + {0x912d82c5, 117, 0, 0}, + {0x91328c1d, 100, 0, 0}, + {0x9152ce50, 100, 0, 0}, + {0x91568f01, 92, 0, 0}, + {0x91585c4c, 117, 0, 0}, + {0x915a53a7, 100, 0, 0}, + {0x9160a87a, 174, 0, 0}, + {0x916913d2, 153, 1, 292}, + {0x917770d8, 100, 1, 1091}, + {0x917d9262, 133, 0, 0}, + {0x9183054e, 193, 0, 0}, + {0x918c1b71, 20, 0, 0}, + {0x9198279e, 114, 2, 0}, + {0x919ac0fe, 100, 0, 0}, + {0x91a140c9, 120, 1, 691}, + {0x91aa57f1, 74, 1, 393}, + {0x91ac514e, 117, 0, 0}, + {0x91b4b1d7, 126, 2, 0}, + {0x91c6ee43, 156, 1, 290}, + {0x91d1e85b, 97, 1, 1290}, + {0x91d24d7b, 50, 1, 690}, + {0x91d33e3c, 126, 1, 1085}, + {0x91d52e9a, 92, 0, 0}, + {0x91e2e863, 163, 1, 291}, + {0x91e4a289, 0, 1, 0}, + {0x9204a65d, 100, 0, 0}, + {0x92197173, 43, 1, 1289}, + {0x9227180c, 117, 0, 0}, + {0x923f915b, 202, 0, 0}, + {0x9247c38d, 126, 2, 0}, + {0x92547f1c, 195, 0, 0}, + {0x926f07af, 157, 0, 0}, + {0x9273f18e, 72, 0, 0}, + {0x927aba9d, 126, 0, 0}, + {0x927c7a3a, 69, 0, 0}, + {0x928361d4, 92, 1, 1088}, + {0x92861600, 104, 1, 289}, + {0x92873f0e, 165, 0, 0}, + {0x929c7b2f, 133, 0, 0}, + {0x92a2185c, 126, 1, 0}, + {0x92a2a702, 34, 0, 0}, + {0x92a3d007, 15, 1, 0}, + {0x92b07fd9, 202, 0, 0}, + {0x92bc8c2e, 24, 0, 0}, + {0x92c138e4, 152, 1, 0}, + {0x92d0a3e8, 124, 0, 0}, + {0x92d5e3dc, 193, 0, 0}, + {0x92edac5e, 92, 0, 0}, + {0x92f04530, 100, 0, 0}, + {0x93146bb0, 117, 0, 0}, + {0x931472d0, 92, 0, 0}, + {0x9316bf4b, 0, 1, 0}, + {0x932a077a, 100, 0, 0}, + {0x932c11c1, 24, 0, 0}, + {0x932ff06e, 65, 1, 990}, + {0x93368b81, 140, 0, 0}, + {0x934db14a, 193, 1, 1289}, + {0x9357a157, 40, 1, 89}, + {0x9369a2f8, 34, 2, 0}, + {0x936e229d, 46, 0, 0}, + {0x93794634, 100, 0, 0}, + {0x937aba88, 68, 1, 390}, + {0x9381a81d, 156, 1, 1288}, + {0x93991433, 163, 1, 990}, + {0x93a7d26c, 124, 0, 0}, + {0x93b49582, 160, 1, 390}, + {0x93b9b15c, 92, 0, 0}, + {0x93dc3c82, 97, 0, 0}, + {0x93f3a490, 166, 1, 0}, + {0x941ba912, 156, 0, 0}, + {0x942b1210, 12, 1, 690}, + {0x942f6bb0, 44, 0, 0}, + {0x943dfbbe, 126, 1, 1189}, + {0x9445a4ea, 0, 1, 0}, + {0x9474c09c, 126, 1, 692}, + {0x947f0728, 104, 1, 288}, + {0x948ad7f6, 52, 2, 0}, + {0x948e0bd6, 89, 0, 0}, + {0x94ccbbbc, 126, 1, 789}, + {0x9509f703, 46, 0, 0}, + {0x950aa028, 34, 1, 692}, + {0x95192148, 89, 0, 0}, + {0x951c12ae, 59, 0, 0}, + {0x9525fa38, 39, 0, 0}, + {0x952a9e77, 16, 0, 0}, + {0x953a3eaf, 156, 1, 989}, + {0x953ca1b6, 100, 0, 0}, + {0x953ffe4c, 48, 0, 0}, + {0x9552e8df, 24, 0, 0}, + {0x9561798d, 24, 0, 0}, + {0x95649977, 97, 1, 493}, + {0x957f3d28, 100, 0, 0}, + {0x958e4bae, 74, 1, 1090}, + {0x95aaed34, 160, 0, 0}, + {0x95af291f, 92, 1, 392}, + {0x95ba5733, 99, 0, 0}, + {0x95c04df9, 38, 0, 0}, + {0x95ca9ec7, 100, 1, 990}, + {0x95d6d531, 57, 1, 1088}, + {0x95dbb274, 160, 0, 0}, + {0x95e4e594, 160, 1, 191}, + {0x95e71ea9, 140, 0, 0}, + {0x96087988, 127, 1, 892}, + {0x9615209f, 34, 0, 0}, + {0x9622fbd9, 133, 0, 0}, + {0x96277a43, 93, 0, 0}, + {0x96533999, 117, 0, 0}, + {0x96546242, 34, 0, 0}, + {0x965b9a2f, 0, 1, 0}, + {0x9668488e, 158, 1, 1092}, + {0x96773f32, 117, 0, 0}, + {0x96788826, 158, 1, 1091}, + {0x967a605f, 126, 2, 0}, + {0x9684657f, 77, 0, 0}, + {0x968dcf09, 117, 0, 0}, + {0x9694bfb8, 115, 1, 993}, + {0x969ef9e4, 4, 1, 987}, + {0x96ba90b0, 173, 0, 0}, + {0x96c3e953, 156, 0, 0}, + {0x96c4ce38, 126, 1, 1289}, + {0x96cfb4d8, 114, 2, 0}, + {0x96d3f955, 188, 1, 990}, + {0x96dfc776, 166, 1, 0}, + {0x96f6051a, 160, 1, 1092}, + {0x96fcdc5f, 182, 1, 390}, + {0x97217057, 115, 1, 1290}, + {0x972d08c5, 144, 0, 0}, + {0x9731a9a3, 160, 1, 390}, + {0x9735d267, 126, 2, 0}, + {0x9747ac09, 131, 1, 591}, + {0x974d0745, 178, 0, 0}, + {0x974e8840, 2, 0, 0}, + {0x975b25ee, 43, 0, 0}, + {0x975ccfeb, 34, 1, 1186}, + {0x9768e920, 93, 0, 0}, + {0x977ee848, 166, 1, 0}, + {0x9786d132, 127, 1, 192}, + {0x979c5314, 6, 1, 1187}, + {0x97bc4585, 120, 0, 0}, + {0x97cad370, 126, 0, 0}, + {0x97d9f9b4, 62, 0, 0}, + {0x98011231, 0, 1, 0}, + {0x9806cb84, 125, 0, 0}, + {0x98087e4d, 117, 0, 0}, + {0x9808abb8, 166, 1, 0}, + {0x980be936, 100, 0, 0}, + {0x982189a2, 24, 1, 988}, + {0x982dfb38, 74, 1, 393}, + {0x9832d15a, 160, 0, 0}, + {0x983948a5, 104, 1, 1187}, + {0x985b1d05, 100, 0, 0}, + {0x986bb2d4, 46, 1, 188}, + {0x987dcda3, 24, 1, 689}, + {0x9881d1b4, 0, 1, 0}, + {0x988798a8, 34, 1, 394}, + {0x988b446d, 57, 1, 1087}, + {0x9894f766, 46, 0, 0}, + {0x98977591, 120, 0, 0}, + {0x98a8f5be, 104, 1, 591}, + {0x98a97a59, 39, 0, 0}, + {0x98aa7cab, 54, 0, 0}, + {0x98bb90d9, 4, 1, 790}, + {0x98c8e090, 99, 0, 0}, + {0x98ccc9ab, 156, 0, 0}, + {0x98ccd385, 163, 2, 0}, + {0x98dc1099, 179, 0, 0}, + {0x98e3c75a, 126, 0, 0}, + {0x98fca2b9, 24, 0, 0}, + {0x99004b45, 175, 0, 0}, + {0x99083b3a, 200, 1, 0}, + {0x9928ee82, 104, 1, 1190}, + {0x99344acd, 46, 1, 1288}, + {0x99580334, 100, 0, 0}, + {0x99686dad, 34, 0, 0}, + {0x9992f445, 21, 0, 0}, + {0x999577b6, 100, 1, 1187}, + {0x99a62e47, 76, 0, 0}, + {0x99a9f57e, 126, 1, 1188}, + {0x99b73746, 77, 0, 0}, + {0x99c395f9, 160, 0, 0}, + {0x99c88648, 26, 2, 0}, // "Proto" + {0x99cc199b, 174, 0, 0}, + {0x99d27118, 0, 1, 0}, + {0x99d38676, 160, 0, 0}, + {0x99dddb04, 100, 1, 1291}, + {0x9a172152, 77, 0, 0}, + {0x9a273fc0, 0, 1, 0}, + {0x9a2b0641, 117, 0, 0}, + {0x9a2db086, 126, 2, 0}, + {0x9a35edfe, 37, 0, 0}, + {0x9a60bf47, 48, 0, 0}, + {0x9a808c3b, 99, 0, 0}, + {0x9a851990, 117, 0, 0}, + {0x9aac6754, 185, 0, 0}, + {0x9aacd75d, 179, 0, 0}, + {0x9ab274ae, 5, 1, 0}, + {0x9acded0e, 57, 1, 492}, + {0x9ad945eb, 126, 0, 0}, + {0x9adb2af7, 34, 0, 0}, + {0x9add521e, 164, 0, 0}, + {0x9adfc8f0, 16, 0, 0}, + {0x9ae5ae08, 117, 0, 0}, + {0x9b1bdbbe, 99, 1, 1290}, + {0x9b208ab1, 23, 0, 0}, + {0x9b323e20, 126, 0, 0}, + {0x9b37d149, 167, 1, 89}, + {0x9b38d9e8, 122, 0, 0}, + {0x9b3c5124, 39, 0, 0}, + {0x9b49657d, 0, 1, 0}, + {0x9b4f0405, 156, 0, 0}, + {0x9b506a48, 126, 1, 1085}, + {0x9b565541, 93, 0, 0}, + {0x9b6d2cb5, 77, 0, 0}, + {0x9b74e080, 34, 0, 0}, + {0x9b821a83, 57, 1, 1191}, + {0x9b88a185, 195, 0, 0}, + {0x9b8e02c0, 200, 1, 0}, + {0x9ba20dad, 57, 1, 1087}, + {0x9ba777e1, 51, 1, 1190}, + {0x9bac73ef, 97, 1, 691}, + {0x9bdcd892, 63, 0, 0}, + {0x9bde3267, 77, 1, 989}, + {0x9beed7a8, 66, 0, 0}, + {0x9bf99c3a, 50, 1, 1191}, + {0x9c015583, 155, 0, 0}, + {0x9c053f24, 72, 0, 0}, + {0x9c18762b, 99, 1, 1191}, + {0x9c521240, 165, 0, 0}, + {0x9c537919, 126, 1, 1093}, + {0x9c58f4a6, 77, 0, 0}, + {0x9c5e2b65, 171, 0, 0}, + {0x9c7e6421, 126, 1, 1085}, + {0x9c9f3571, 100, 1, 487}, + {0x9ca42697, 179, 0, 0}, + {0x9caaba9e, 199, 0, 0}, + {0x9cb55b96, 100, 1, 1291}, + {0x9cbadc25, 53, 0, 0}, + {0x9cbb0291, 9, 0, 0}, + {0x9cbc8253, 117, 0, 0}, + {0x9cf17fab, 195, 0, 0}, + {0x9d1deab8, 0, 1, 0}, + {0x9d21fe96, 117, 0, 0}, + {0x9d38f8f9, 166, 1, 0}, + {0x9d45d8ec, 72, 0, 0}, + {0x9da252b4, 100, 0, 85}, + {0x9db51b1f, 95, 0, 0}, + {0x9db6a3ed, 26, 2, 0}, // "Proto" + {0x9dc96ec7, 92, 0, 0}, + {0x9def17bb, 160, 0, 0}, + {0x9df89be5, 160, 1, 190}, + {0x9e0bf355, 100, 0, 0}, + {0x9e0e1dc8, 160, 0, 0}, + {0x9e1ce13d, 21, 0, 0}, + {0x9e356267, 147, 0, 0}, + {0x9e379698, 33, 1, 0}, + {0x9e382ebf, 126, 1, 389}, + {0x9e4e9cc2, 117, 1, 1193}, + {0x9e6092a4, 100, 1, 1292}, + {0x9e777ea5, 102, 0, 0}, + {0x9ea1dc76, 160, 1, 691}, + {0x9eab6b1f, 100, 0, 0}, + {0x9ebdc94e, 89, 0, 0}, + {0x9eca0941, 156, 1, 1288}, + {0x9ecb9dcd, 176, 0, 0}, + {0x9ed99198, 126, 1, 1088}, + {0x9edd2159, 182, 1, 1292}, + {0x9ee83916, 92, 0, 0}, + {0x9eef47aa, 143, 1, 690}, + {0x9eff96d2, 156, 0, 0}, + {0x9f2eef20, 172, 1, 992}, + {0x9f3da143, 117, 0, 0}, + {0x9f432594, 77, 1, 690}, + {0x9f50a100, 92, 0, 0}, + {0x9f5138cb, 159, 0, 0}, + {0x9f6c119c, 74, 1, 590}, + {0x9f6ce171, 12, 1, 990}, + {0x9f75b83b, 126, 1, 490}, + {0x9f8336db, 98, 0, 0}, + {0x9f85fa70, 161, 0, 0}, + {0x9f8bd88d, 38, 0, 0}, + {0x9fa1c11f, 2, 0, 0}, + {0x9fae4d46, 34, 0, 0}, + {0x9fb32923, 173, 1, 292}, + {0x9fd0f213, 24, 1, 289}, + {0x9fd718fd, 201, 0, 0}, + {0x9fe0aef5, 126, 1, 0}, + {0x9ffe2f55, 160, 1, 989}, + {0xa01ef87e, 115, 1, 93}, + {0xa025344d, 92, 1, 493}, + {0xa029dfe9, 38, 0, 0}, + {0xa02b3a39, 102, 0, 0}, + {0xa03a422b, 104, 1, 488}, + {0xa03e89d9, 0, 1, 0}, + {0xa045fe1d, 33, 2, 0}, + {0xa0568e1d, 160, 1, 188}, + {0xa06c820c, 65, 1, 1091}, + {0xa085a697, 0, 1, 0}, + {0xa098ebd2, 177, 0, 0}, + {0xa0a095c4, 193, 1, 1288}, + {0xa0a5a0b9, 56, 2, 0}, + {0xa0a6e860, 100, 0, 0}, + {0xa0b0b742, 126, 1, 290}, + {0xa0b8d310, 176, 0, 0}, + {0xa0c31a57, 115, 1, 1288}, + {0xa0c9bf7e, 160, 1, 1292}, + {0xa0cad20f, 160, 0, 0}, + {0xa0dabe0a, 126, 1, 84}, + {0xa0dad6e4, 130, 1, 0}, + {0xa0ddf884, 77, 0, 0}, + {0xa0df4b8f, 4, 1, 992}, + {0xa0eca0f9, 0, 1, 0}, + {0xa0ed7d20, 126, 1, 290}, + {0xa0f56a29, 117, 0, 0}, + {0xa0f99bb8, 100, 0, 0}, + {0xa11fac93, 92, 0, 0}, + {0xa139009c, 13, 1, 492}, + {0xa1431bbd, 93, 0, 0}, + {0xa166548f, 150, 1, 192}, + {0xa16ab939, 195, 0, 0}, + {0xa186f064, 116, 1, 190}, + {0xa1a0c13f, 182, 2, 0}, + {0xa1a33b85, 2, 0, 0}, + {0xa1b67ac5, 34, 0, 0}, + {0xa1ff4e1d, 158, 1, 1292}, + {0xa2194cad, 34, 1, 788}, + {0xa21e675c, 89, 0, 0}, + {0xa22657fa, 126, 1, 1290}, + {0xa232e8be, 143, 1, 0}, // "Proto" + {0xa25a750f, 57, 1, 1290}, + {0xa25ed91d, 66, 0, 0}, + {0xa262a81f, 24, 0, 0}, + {0xa2a6ee58, 165, 1, 0}, + {0xa2af25d0, 68, 1, 788}, + {0xa2bac574, 153, 1, 492}, + {0xa2c0cab7, 175, 0, 0}, + {0xa2c79ec5, 124, 0, 0}, + {0xa2c89cb9, 0, 1, 0}, + {0xa2c94c04, 178, 0, 0}, + {0xa2d6ee34, 24, 0, 0}, + {0xa2d79603, 117, 0, 0}, + {0xa2da133b, 6, 1, 990}, + {0xa2e4534f, 115, 1, 190}, + {0xa2e68da8, 100, 0, 0}, + {0xa2ee1350, 95, 0, 0}, + {0xa2f826f1, 182, 1, 390}, + {0xa3047263, 24, 0, 0}, + {0xa308a153, 100, 0, 0}, + {0xa30d8baf, 54, 0, 0}, + {0xa31142ff, 6, 2, 0}, + {0xa3414faa, 117, 0, 0}, + {0xa342a5fd, 4, 1, 588}, + {0xa37b0ee3, 46, 1, 188}, + {0xa38857eb, 156, 0, 0}, + {0xa38fe9ce, 168, 0, 0}, + {0xa397028a, 15, 1, 0}, + {0xa3a6184c, 21, 0, 0}, + {0xa3ad445d, 34, 1, 1288}, + {0xa3d59f62, 43, 0, 0}, + {0xa3da8777, 95, 0, 0}, + {0xa3e082a6, 117, 0, 0}, + {0xa3e37134, 126, 0, 0}, + {0xa3ea8dba, 126, 1, 1088}, + {0xa4062017, 57, 1, 289}, + {0xa45bcda0, 121, 0, 0}, + {0xa461436a, 126, 0, 0}, + {0xa4625dbc, 57, 1, 1088}, + {0xa46aa597, 150, 0, 0}, + {0xa479b08c, 97, 1, 1290}, + {0xa485abed, 39, 0, 0}, + {0xa49253c6, 117, 0, 0}, + {0xa49b48b8, 53, 0, 0}, + {0xa4a4f4bf, 46, 0, 0}, + {0xa4b947ca, 126, 1, 0}, + {0xa4bdcc1d, 0, 2, 0}, + {0xa4cb97df, 126, 1, 0}, + {0xa4dcdf28, 133, 0, 0}, + {0xa4e935df, 156, 0, 0}, + {0xa4ee65ac, 24, 0, 0}, + {0xa505d342, 117, 0, 0}, + {0xa50c51fb, 117, 0, 0}, + {0xa520f44c, 166, 1, 0}, + {0xa524ae9b, 106, 0, 0}, + {0xa52a8119, 24, 0, 0}, + {0xa52b28ed, 105, 0, 87}, + {0xa5313248, 24, 1, 689}, + {0xa547a6ec, 77, 0, 0}, + {0xa54d9086, 92, 0, 0}, + {0xa55701dd, 200, 1, 0}, + {0xa558fb52, 34, 1, 990}, + {0xa55fa397, 104, 1, 989}, + {0xa5636767, 77, 1, 194}, + {0xa56a1bd0, 72, 0, 0}, + {0xa5781280, 133, 0, 0}, + {0xa58a8da1, 202, 0, 0}, + {0xa59ca2ef, 24, 0, 0}, + {0xa5a19af5, 122, 0, 0}, + {0xa5b62a4c, 164, 0, 0}, + {0xa5d05d45, 90, 0, 0}, + {0xa5dfa8ce, 68, 1, 1088}, + {0xa5e6baf9, 117, 0, 0}, + {0xa5e89675, 40, 1, 0}, + {0xa5e8d2cd, 46, 1, 1187}, + {0xa60ca3d6, 188, 1, 192}, + {0xa60fba51, 108, 0, 0}, + {0xa65d3207, 79, 0, 0}, + {0xa6648353, 193, 0, 0}, + {0xa66596d9, 164, 0, 0}, + {0xa67ea466, 104, 1, 393}, + {0xa6819195, 133, 0, 0}, + {0xa695b076, 40, 1, 0}, + {0xa69f29fa, 156, 1, 1192}, + {0xa6a2ec56, 42, 0, 0}, + {0xa6a725b8, 50, 1, 1292}, + {0xa6b3f7b3, 156, 0, 0}, + {0xa6de7024, 160, 0, 0}, + {0xa6e16202, 124, 0, 0}, + {0xa6f5cb3c, 68, 1, 290}, + {0xa70b2813, 92, 0, 0}, + {0xa71c3452, 160, 0, 0}, + {0xa725b2d3, 91, 1, 792}, + {0xa72fde03, 92, 0, 0}, + {0xa7481c4b, 22, 0, 0}, + {0xa7b0536c, 160, 0, 0}, + {0xa7b0bc87, 143, 1, 989}, + {0xa7d3635e, 164, 0, 0}, + {0xa7de65e4, 6, 1, 489}, + {0xa7e784ed, 72, 0, 0}, + {0xa80a0f01, 104, 1, 894}, + {0xa8104fb2, 24, 2, 0}, + {0xa8138860, 65, 1, 989}, + {0xa817d175, 93, 0, 0}, + {0xa81c51ea, 92, 0, 0}, + {0xa848a2b1, 43, 1, 1289}, + {0xa86a5318, 53, 1, 392}, + {0xa86af976, 4, 1, 1292}, + {0xa86e3704, 100, 0, 0}, + {0xa875c5e9, 93, 0, 0}, + {0xa8784932, 34, 1, 288}, + {0xa8923256, 97, 1, 490}, + {0xa895d40a, 77, 0, 0}, + {0xa8a4a48b, 200, 1, 0}, + {0xa8a9b982, 180, 0, 0}, + {0xa8aa7994, 12, 1, 690}, + {0xa8b0cfce, 89, 1, 990}, + {0xa8b0da56, 3, 1, 592}, + {0xa8efac13, 0, 1, 0}, + {0xa8f5c2ab, 166, 1, 0}, + {0xa9004a88, 142, 1, 1290}, + {0xa905cc12, 0, 1, 0}, + {0xa9065101, 124, 0, 0}, + {0xa913a222, 77, 1, 189}, + {0xa91460b8, 54, 0, 0}, + {0xa9217ea2, 188, 1, 1290}, + {0xa922075e, 66, 0, 0}, + {0xa93527e2, 100, 2, 0}, + {0xa941eefa, 117, 0, 0}, + {0xa94591b0, 100, 1, 992}, + {0xa94686cb, 200, 1, 0}, + {0xa9541452, 24, 0, 0}, + {0xa97567a4, 84, 2, 0}, + {0xa98046b8, 126, 0, 0}, + {0xa9a4ea4c, 143, 0, 0}, + {0xa9bbf44f, 166, 1, 0}, + {0xa9c2c503, 100, 1, 88}, + {0xa9e2bf31, 0, 1, 0}, + {0xa9e30826, 100, 0, 0}, + {0xa9e70766, 41, 0, 0}, + {0xa9f0b6f3, 46, 0, 0}, + {0xaa14431a, 0, 1, 0}, + {0xaa174bc6, 4, 1, 689}, + {0xaa20f73d, 92, 1, 1291}, + {0xaa4318ae, 39, 0, 0}, + {0xaa6bb985, 193, 1, 390}, + {0xaa6e1a35, 92, 0, 0}, + {0xaa755715, 165, 0, 0}, + {0xaa96889c, 100, 0, 0}, + {0xaa97d0a0, 117, 0, 0}, + {0xaa9ca482, 0, 1, 0}, + {0xaaa67035, 92, 0, 0}, + {0xaaaa17bd, 68, 1, 290}, + {0xaad7bdc1, 156, 0, 0}, + {0xaaed295c, 126, 1, 288}, + {0xaaf2ce64, 24, 0, 0}, + {0xaafe699c, 165, 0, 0}, + {0xaafed9b4, 164, 0, 0}, + {0xab07c7f3, 155, 0, 0}, + {0xab12ece6, 0, 1, 0}, + {0xab1f0f44, 0, 1, 0}, + {0xab3062cf, 24, 0, 0}, + {0xab41445e, 156, 1, 292}, + {0xab459d2f, 97, 1, 1290}, + {0xab4ac985, 20, 0, 0}, + {0xab547071, 92, 2, 0}, + {0xab6b4794, 142, 1, 491}, + {0xab8371f6, 188, 1, 190}, + {0xab90e397, 155, 1, 690}, + {0xababffdb, 74, 1, 1090}, + {0xabe1a0c2, 126, 0, 0}, + {0xabef6b83, 100, 0, 0}, + {0xac05ebb7, 34, 0, 0}, + {0xac0d2d09, 46, 1, 290}, + {0xac136f2d, 155, 0, 0}, + {0xac273c14, 0, 1, 0}, + {0xac4bf9dc, 100, 0, 0}, + {0xac4f4e9f, 24, 0, 0}, + {0xac559fbd, 40, 1, 0}, + {0xac609320, 160, 2, 0}, + {0xac652b47, 100, 0, 0}, + {0xac75f8cd, 117, 0, 0}, + {0xac7a54cc, 34, 2, 0}, + {0xac8dcdea, 4, 1, 1289}, + {0xac9817e3, 4, 1, 291}, + {0xac9895cc, 100, 0, 0}, + {0xac98cd70, 100, 0, 0}, + {0xaca15643, 99, 1, 1191}, + {0xaca687c2, 165, 0, 0}, + {0xacd3e768, 20, 0, 0}, + {0xacde60c0, 127, 1, 192}, + {0xace56f39, 117, 0, 0}, + {0xacf912df, 132, 0, 0}, + {0xad12a34f, 165, 1, 189}, + {0xad16f6c7, 0, 1, 0}, + {0xad28aef6, 156, 0, 0}, + {0xad3df455, 24, 0, 0}, + {0xad50e497, 3, 1, 190}, + {0xad5f0653, 175, 0, 0}, + {0xad6cdf29, 126, 1, 1085}, + {0xad6e96f1, 185, 0, 0}, + {0xad7b97bc, 143, 0, 0}, + {0xad7f9480, 156, 1, 987}, + {0xada1b12f, 130, 1, 0}, + {0xada80d95, 140, 0, 0}, + {0xadb47286, 89, 0, 0}, + {0xadb5d0b3, 77, 0, 0}, + {0xadba7064, 156, 0, 0}, + {0xadbd4e48, 34, 1, 9}, + {0xadd6374f, 156, 0, 0}, + {0xade11141, 34, 0, 0}, + {0xadf606f6, 160, 0, 0}, + {0xadffd64f, 117, 0, 0}, + {0xae128fac, 133, 0, 0}, + {0xae19d06e, 165, 1, 787}, + {0xae280e20, 72, 0, 0}, + {0xae286904, 100, 1, 192}, + {0xae321339, 117, 0, 0}, + {0xae52dece, 68, 1, 1088}, + {0xae56518e, 160, 0, 0}, + {0xae64ca77, 126, 1, 388}, + {0xae6d99c7, 126, 1, 387}, + {0xae8666b4, 92, 1, 588}, + {0xae97627c, 97, 1, 0}, // "Proto" + {0xaeb7fce9, 160, 0, 0}, + {0xaebd6549, 160, 0, 0}, + {0xaed1e6a4, 126, 0, 0}, + {0xaef78148, 194, 0, 86}, + {0xaf05f37e, 4, 1, 1292}, + {0xaf15338f, 117, 0, 0}, + {0xaf16ee39, 202, 0, 0}, + {0xaf2bb895, 77, 2, 0}, + {0xaf2bbcbc, 126, 1, 1085}, + {0xaf31a310, 171, 0, 0}, + {0xaf3ec4b1, 160, 0, 0}, + {0xaf4010ea, 126, 1, 888}, + {0xaf432446, 0, 1, 0}, + {0xaf4b5c8a, 127, 1, 892}, + {0xaf5676de, 29, 1, 987}, + {0xaf5ad5af, 147, 0, 0}, + {0xaf65aa84, 163, 2, 0}, + {0xaf6e8571, 117, 0, 0}, + {0xaf754426, 202, 0, 0}, + {0xaf85b53e, 169, 0, 0}, + {0xafb40372, 100, 0, 0}, + {0xafb46dd6, 12, 1, 789}, + {0xafc65de3, 190, 0, 0}, + {0xafdcbd24, 126, 1, 1085}, + {0xafeafeaa, 0, 1, 0}, + {0xb00abe1c, 77, 0, 0}, + {0xb00b4eb8, 91, 1, 1191}, + {0xb00b84d3, 143, 0, 0}, + {0xb0480ae9, 100, 1, 691}, + {0xb049a8c4, 24, 0, 0}, + {0xb04ba659, 76, 0, 0}, + {0xb051c0e1, 4, 1, 692}, + {0xb06c0674, 100, 0, 0}, + {0xb092dd8e, 126, 1, 1085}, + {0xb092fa3e, 92, 0, 0}, + {0xb097f651, 117, 0, 0}, + {0xb09e38f9, 126, 1, 790}, + {0xb0cd000f, 158, 1, 1193}, + {0xb0ebf3db, 196, 1, 292}, + {0xb1031788, 0, 1, 0}, + {0xb10429aa, 97, 1, 889}, + {0xb1250d0c, 100, 0, 0}, + {0xb134d713, 114, 1, 390}, + {0xb1378c99, 160, 1, 1188}, + {0xb13f00d4, 100, 2, 0}, + {0xb1494b9e, 191, 0, 0}, + {0xb14e668e, 37, 0, 0}, + {0xb14ea4d2, 143, 1, 788}, + {0xb1612fe6, 77, 1, 490}, + {0xb174b680, 117, 0, 0}, // Dig Dug (J) + {0xb17574f3, 0, 1, 0}, + {0xb184060a, 47, 0, 0}, + {0xb1849d4e, 69, 0, 0}, + {0xb19a55dd, 166, 1, 0}, + {0xb19c48a5, 166, 1, 0}, + {0xb1a94b82, 24, 0, 0}, + {0xb1b16b8a, 21, 0, 0}, + {0xb1b9e187, 117, 0, 0}, + {0xb1c4c508, 126, 0, 0}, + {0xb1e84e5b, 38, 0, 0}, + {0xb1f7e3e9, 0, 1, 0}, + {0xb20001f4, 100, 0, 0}, + {0xb201b522, 100, 0, 0}, + {0xb20f87de, 160, 1, 1092}, + {0xb22c41cd, 100, 0, 0}, + {0xb24abc55, 100, 0, 0}, + {0xb252a5b9, 117, 0, 0}, + {0xb258d6ca, 97, 1, 691}, + {0xb27b8cf4, 100, 0, 0}, + {0xb297b5e7, 92, 0, 0}, + {0xb2ab361e, 192, 0, 0}, + {0xb2c0d62c, 115, 1, 1288}, + {0xb2fa62fd, 156, 1, 989}, + {0xb301b0dc, 34, 0, 0}, + {0xb30f6865, 117, 0, 0}, + {0xb330ed11, 188, 1, 291}, + {0xb33bc971, 65, 1, 391}, + {0xb3440357, 143, 1, 790}, + {0xb34ed396, 0, 1, 0}, + {0xb3598cd0, 179, 0, 0}, + {0xb36457c7, 97, 0, 0}, + {0xb3769a51, 156, 1, 1288}, + {0xb3783f2a, 165, 1, 787}, + {0xb37f48cd, 23, 0, 0}, + {0xb382aea4, 77, 0, 0}, + {0xb399bee4, 128, 0, 0}, + {0xb39bab34, 133, 0, 0}, + {0xb3c30bea, 117, 0, 0}, + {0xb3ca8bc5, 117, 0, 0}, + {0xb3cb3da9, 174, 0, 0}, + {0xb3d1178e, 166, 1, 0}, + {0xb3d74c0d, 126, 1, 686}, + {0xb3dbc08f, 117, 0, 0}, + {0xb3e2f2ef, 15, 1, 0}, + {0xb3fe1017, 32, 0, 0}, + {0xb40457d5, 34, 0, 0}, + {0xb40870a2, 156, 2, 0}, + {0xb41bbadf, 122, 0, 0}, + {0xb422a67a, 166, 1, 0}, + {0xb42a57c7, 124, 0, 0}, + {0xb4481476, 160, 0, 0}, + {0xb462718e, 33, 1, 0}, + {0xb4735fac, 69, 0, 0}, + {0xb47569e2, 97, 0, 0}, + {0xb4844ca5, 173, 1, 292}, + {0xb4adede3, 117, 0, 0}, + {0xb4d554d6, 46, 1, 390}, + {0xb4e4879e, 34, 1, 1186}, + {0xb4ff91e7, 181, 0, 0}, + {0xb515e7d4, 24, 0, 0}, + {0xb548fd2e, 161, 0, 0}, + {0xb54db5ef, 191, 0, 0}, + {0xb5576820, 77, 0, 0}, + {0xb55d5747, 88, 1, 90}, + {0xb55da544, 164, 0, 0}, + {0xb56958d1, 99, 1, 491}, + {0xb5897e94, 179, 0, 0}, + {0xb58d962b, 195, 0, 0}, + {0xb5902c20, 0, 1, 0}, + {0xb5976b7c, 179, 0, 0}, + {0xb59a7a29, 89, 0, 0}, + {0xb5af14ca, 176, 0, 0}, + {0xb5be4935, 104, 1, 0}, // "Proto" + {0xb5d28ea2, 163, 1, 489}, + {0xb5e24324, 144, 0, 0}, + {0xb5e38091, 100, 1, 992}, + {0xb5e392e2, 160, 1, 1192}, + {0xb5f7e661, 46, 0, 0}, + {0xb5ff71ab, 117, 0, 0}, + {0xb627254b, 160, 1, 490}, + {0xb629d555, 92, 1, 391}, // Totally Rad (U) + {0xb62a7b71, 117, 0, 0}, + {0xb64ab5e2, 92, 0, 0}, + {0xb66471cb, 0, 1, 0}, + {0xb6661bda, 150, 1, 690}, + {0xb668c7fc, 100, 1, 587}, + {0xb6758ee6, 74, 1, 292}, + {0xb69831ca, 45, 0, 0}, + {0xb6bf5137, 156, 1, 989}, + {0xb6ca8a0e, 95, 0, 0}, + {0xb6d2d300, 117, 1, 0}, + {0xb6dc9aa7, 55, 0, 0}, + {0xb6f94005, 160, 0, 0}, + {0xb70129f4, 16, 0, 0}, + {0xb775c9c1, 168, 0, 0}, + {0xb7773a07, 144, 0, 0}, + {0xb780521c, 165, 1, 590}, + {0xb786ab95, 180, 0, 0}, + {0xb788ebf8, 40, 1, 0}, + {0xb78970d2, 117, 0, 0}, + {0xb7925da2, 34, 1, 1186}, + {0xb79c320d, 34, 2, 0}, + {0xb7a338ca, 100, 0, 0}, + {0xb7b6800c, 117, 0, 0}, + {0xb7c0dae7, 100, 0, 0}, + {0xb7f28915, 24, 0, 0}, + {0xb7f485d8, 126, 1, 887}, + {0xb80192b7, 186, 2, 0}, + {0xb807446e, 30, 0, 0}, + {0xb811c054, 2, 0, 0}, + {0xb843eb84, 188, 1, 990}, + {0xb859ab5f, 24, 1, 492}, + {0xb8747abf, 2, 0, 0}, + {0xb89764a2, 92, 0, 0}, + {0xb89888c9, 33, 1, 0}, + {0xb8af9340, 37, 0, 0}, + {0xb8b2e479, 97, 0, 0}, + {0xb8bb48d3, 160, 1, 887}, + {0xb8d93cf2, 100, 0, 0}, + {0xb8f3781d, 161, 0, 0}, + {0xb90497aa, 126, 0, 0}, + {0xb90865cf, 165, 1, 690}, + {0xb918580c, 13, 1, 1291}, + {0xb938b7e9, 166, 1, 0}, + {0xb9661f04, 92, 0, 0}, + {0xb96ac600, 95, 0, 0}, + {0xb9762da8, 0, 1, 0}, + {0xb979cad5, 89, 0, 0}, + {0xb97afb85, 126, 0, 0}, + {0xb99085ce, 100, 2, 0}, + {0xb99394c3, 0, 1, 0}, + {0xb9ab06aa, 150, 0, 0}, + {0xb9b4d9e0, 126, 1, 990}, + {0xb9c20634, 117, 0, 0}, + {0xb9cf171f, 92, 1, 190}, + {0xba23e079, 174, 0, 0}, + {0xba322865, 126, 1, 0}, + {0xba428f36, 134, 0, 0}, + {0xba51ac6f, 89, 0, 0}, + {0xba6bdd6a, 100, 0, 0}, + {0xba766ec6, 46, 0, 0}, + {0xba898162, 0, 1, 0}, + {0xba9164e7, 0, 1, 0}, + {0xba93cb95, 40, 1, 0}, + {0xbab3ddb9, 100, 0, 0}, + {0xbaca10a9, 160, 0, 0}, + {0xbad36c17, 195, 0, 0}, + {0xbadafc2b, 90, 0, 0}, + {0xbb1b591b, 117, 0, 0}, + {0xbb375ed2, 24, 0, 0}, + {0xbb435255, 89, 0, 0}, + {0xbb4b6df7, 165, 0, 0}, + {0xbb4ee993, 160, 0, 0}, + {0xbb673749, 29, 1, 987}, + {0xbb6d7949, 100, 1, 292}, + {0xbb7bca36, 126, 0, 0}, + {0xbb7c5f7a, 156, 0, 0}, + {0xbb7f829a, 69, 0, 0}, + {0xbbc44e39, 126, 0, 0}, + {0xbbd8c622, 98, 0, 0}, + {0xbbe2d09d, 117, 0, 0}, + {0xbbe40dc4, 40, 1, 0}, + {0xbc065fc3, 30, 1, 990}, + {0xbc1197a4, 89, 0, 0}, + {0xbc11e61a, 117, 0, 0}, + {0xbc19f17e, 18, 0, 0}, + {0xbc1dce96, 10, 1, 0}, + {0xbc1f50b5, 0, 1, 0}, + {0xbc1fbd7b, 178, 0, 0}, + {0xbc5f6c94, 24, 1, 787}, + {0xbc7485b5, 83, 0, 0}, + {0xbc7b1d0f, 160, 0, 0}, + {0xbc7fedb9, 43, 1, 1290}, + {0xbc80fb52, 99, 0, 0}, + {0xbc8e8bc7, 160, 0, 0}, + {0xbc9bffcb, 100, 2, 0}, + {0xbcacbbf4, 89, 1, 291}, + {0xbccfef1c, 71, 2, 0}, + {0xbcd7e5ea, 66, 0, 0}, + {0xbce1da2c, 0, 1, 0}, + {0xbcf68611, 72, 0, 0}, + {0xbd0e29b3, 0, 1, 0}, + {0xbd154c3e, 33, 1, 0}, + {0xbd2269ad, 43, 0, 0}, + {0xbd29178a, 15, 1, 0}, + {0xbd2fe6a6, 143, 1, 788}, + {0xbd40d4be, 121, 0, 0}, + {0xbd523011, 117, 0, 0}, + {0xbd8a0d7d, 89, 0, 0}, + {0xbd9d0e85, 77, 0, 0}, + {0xbda8f8e4, 24, 0, 0}, + {0xbdabac0b, 42, 0, 0}, + {0xbdabc4a6, 117, 0, 0}, + {0xbdad07e0, 24, 0, 0}, + {0xbdad5548, 117, 0, 0}, + {0xbdbae3e0, 117, 0, 0}, + {0xbdc124e5, 25, 0, 0}, + {0xbdc65c91, 16, 0, 0}, + {0xbdd176af, 0, 1, 0}, + {0xbdd55ea1, 127, 1, 693}, + {0xbde10416, 180, 0, 0}, + {0xbde3ae9b, 77, 0, 0}, + {0xbde7a7b5, 15, 1, 0}, + {0xbde93999, 24, 1, 490}, + {0xbdf046ef, 114, 1, 690}, + {0xbdf1307c, 122, 0, 0}, + {0xbe00966b, 72, 0, 0}, + {0xbe06853f, 24, 0, 0}, + {0xbe1bf10c, 10, 1, 0}, + {0xbe250388, 34, 1, 892}, + {0xbe2e7055, 0, 1, 0}, + {0xbe3bf3b3, 76, 1, 690}, + {0xbe6dbb5d, 34, 1, 0}, + {0xbe7c4202, 43, 1, 889}, + {0xbe86ee6d, 15, 1, 0}, + {0xbe898565, 165, 0, 0}, + {0xbe8a744d, 92, 0, 0}, + {0xbe939fce, 126, 1, 1092}, + {0xbe95b219, 34, 0, 0}, + {0xbe9c42c9, 126, 0, 0}, + {0xbe9e3799, 117, 0, 0}, + {0xbea682e5, 191, 0, 0}, + {0xbeb15855, 104, 1, 289}, + {0xbeb30478, 92, 0, 0}, + {0xbeb88304, 126, 1, 1290}, + {0xbeb8ab01, 126, 1, 686}, + {0xbee1c0d9, 17, 1, 1190}, + {0xbee54426, 15, 1, 0}, + {0xbf250af2, 46, 1, 1086}, + {0xbf3635cf, 34, 0, 0}, + {0xbf3bb6d5, 69, 0, 0}, + {0xbf471693, 43, 0, 0}, + {0xbf4f4ba6, 77, 0, 0}, + {0xbf5e2513, 75, 0, 0}, + {0xbf7f54b4, 191, 0, 0}, + {0xbf865533, 100, 0, 0}, + {0xbf888b75, 127, 2, 0}, + {0xbf8c1116, 97, 0, 0}, + {0xbf8d7db6, 24, 0, 0}, + {0xbf93112a, 92, 0, 0}, + {0xbf968b1e, 165, 0, 0}, + {0xbf9adf43, 69, 0, 0}, + {0xbfbfd25d, 0, 1, 0}, + {0xbfc7a2e9, 24, 0, 0}, + {0xbfe0e37f, 68, 1, 191}, + {0xbffcf247, 117, 0, 0}, + {0xc0103592, 92, 2, 0}, + {0xc032e5b0, 126, 1, 1092}, + {0xc042515a, 95, 0, 0}, + {0xc05a365b, 174, 0, 0}, + {0xc05bbdfc, 148, 0, 0}, + {0xc05d2034, 188, 1, 490}, + {0xc0605945, 92, 0, 0}, + {0xc060ed0a, 192, 0, 0}, + {0xc09bf032, 160, 0, 0}, + {0xc0b23520, 188, 1, 289}, + {0xc0b2aa1f, 0, 1, 0}, + {0xc0bd6275, 92, 1, 788}, + {0xc0dd00e0, 97, 1, 1088}, + {0xc0e35dae, 156, 0, 0}, + {0xc0ededd0, 100, 2, 0}, + {0xc0ef49ac, 95, 0, 0}, + {0xc0f173c4, 161, 0, 0}, + {0xc0f73d85, 0, 1, 0}, + {0xc0fb91ac, 156, 0, 0}, + {0xc0fed437, 89, 0, 0}, + {0xc11c7da2, 115, 1, 1290}, + {0xc138cfea, 46, 0, 0}, + {0xc15a50fc, 126, 2, 0}, + {0xc16897d2, 166, 1, 0}, + {0xc17ae2dc, 143, 0, 0}, + {0xc1a9e6c0, 0, 1, 0}, + {0xc1b43207, 182, 1, 892}, + {0xc1b6b2a6, 117, 0, 0}, + {0xc1ba8bb9, 72, 0, 0}, + {0xc1c3636b, 91, 1, 1191}, + {0xc1cd15dd, 129, 2, 0}, + {0xc1cf486a, 180, 0, 0}, + {0xc1cf4948, 145, 0, 0}, + {0xc1d7ab1d, 24, 0, 0}, + {0xc1d9abbf, 24, 0, 0}, + {0xc1dc5b12, 124, 0, 0}, + {0xc1e91d3f, 97, 2, 90}, // Spy Vs Spy (E) [!] + {0xc1fbf659, 100, 0, 0}, + {0xc2222bb1, 92, 0, 0}, + {0xc22c23ab, 39, 0, 0}, + {0xc22ff1d8, 46, 0, 0}, + {0xc237ccb0, 77, 0, 0}, + {0xc23a56da, 115, 1, 991}, + {0xc23cc4ee, 95, 0, 0}, + {0xc247a23d, 100, 1, 193}, + {0xc247cc80, 117, 0, 0}, + {0xc2730c30, 29, 1, 987}, + {0xc273751e, 58, 1, 92}, + {0xc2757acd, 126, 0, 0}, + {0xc283e72d, 126, 0, 0}, + {0xc2840372, 24, 0, 0}, + {0xc2c6827e, 57, 1, 1088}, + {0xc2cfd9d9, 77, 0, 0}, + {0xc2db7551, 24, 1, 689}, + {0xc2df0a00, 92, 0, 0}, + {0xc2ef3422, 2, 0, 0}, + {0xc30848d3, 126, 2, 0}, + {0xc30c9ec9, 60, 0, 0}, + {0xc313ef54, 100, 1, 390}, + {0xc31abe30, 146, 1, 889}, + {0xc31f5585, 126, 0, 0}, + {0xc3244352, 0, 1, 0}, + {0xc3463a3d, 158, 1, 991}, + {0xc346b2cc, 39, 0, 0}, + {0xc36abbec, 173, 0, 0}, + {0xc372399b, 117, 0, 0}, + {0xc382120c, 46, 0, 0}, + {0xc38b1aae, 126, 1, 0}, + {0xc38b62cb, 126, 1, 85}, + {0xc3a0a3e0, 57, 2, 0}, + {0xc3a1b9a8, 42, 0, 0}, + {0xc3aec9fa, 191, 2, 0}, + {0xc3ba98ee, 100, 0, 0}, + {0xc3c7a568, 4, 1, 987}, + {0xc3d9b673, 100, 1, 1288}, + {0xc3df85c3, 100, 0, 0}, + {0xc3f85d1e, 0, 1, 0}, + {0xc420552f, 20, 0, 0}, + {0xc43da8e2, 165, 1, 590}, + {0xc4482b58, 89, 0, 0}, + {0xc471e42d, 100, 1, 1288}, + {0xc47efc0e, 15, 1, 0}, + {0xc4835a34, 77, 0, 0}, + {0xc48363b4, 25, 0, 0}, + {0xc48ddb52, 54, 0, 0}, + {0xc4966242, 77, 0, 0}, + {0xc4b6ed3c, 104, 1, 891}, + {0xc4bc85a2, 100, 1, 888}, + {0xc4c3949a, 126, 1, 686}, + {0xc4cd7431, 104, 0, 0}, + {0xc4d97c46, 158, 1, 1193}, + {0xc4dcbb18, 169, 0, 0}, + {0xc4e1886f, 133, 0, 0}, + {0xc4e5abb8, 39, 0, 0}, + {0xc4e8c04a, 34, 1, 1186}, + {0xc50149db, 160, 0, 0}, + {0xc50a8304, 0, 1, 0}, + {0xc527c297, 104, 1, 393}, + {0xc529c604, 24, 0, 0}, + {0xc53cf1d0, 100, 2, 0}, + {0xc55a3393, 188, 1, 688}, + {0xc577fe32, 183, 0, 0}, + {0xc57fce87, 40, 1, 0}, + {0xc59641b6, 31, 1, 0}, + {0xc5b0b1ab, 100, 1, 987}, + {0xc5b80dce, 183, 0, 0}, + {0xc5b8efea, 77, 0, 0}, + {0xc5cfe54e, 97, 0, 0}, + {0xc5fea9f2, 25, 0, 0}, + {0xc6000085, 115, 1, 291}, + {0xc6182024, 99, 1, 1089}, + {0xc6224026, 77, 0, 0}, + {0xc62793cf, 100, 1, 1288}, + {0xc62d9630, 66, 0, 0}, + {0xc6475c2a, 46, 0, 0}, + {0xc651e21a, 0, 1, 0}, + {0xc6557e02, 117, 0, 0}, + {0xc655c077, 164, 0, 0}, + {0xc66fb098, 24, 0, 0}, + {0xc6790794, 0, 1, 0}, + {0xc68363f6, 124, 0, 0}, + {0xc6a4bf7b, 164, 0, 0}, + {0xc6a66b68, 24, 0, 0}, + {0xc6acceea, 2, 0, 0}, + {0xc6add8c5, 117, 0, 0}, + {0xc6b5d7e0, 76, 0, 0}, + {0xc6c226bf, 74, 1, 590}, + {0xc6cc7417, 54, 0, 0}, + {0xc6d62814, 100, 0, 0}, + {0xc6dd7e69, 6, 1, 1089}, + {0xc701a262, 1, 0, 0}, + {0xc708fbed, 166, 1, 0}, + {0xc70bd0f8, 165, 1, 690}, + {0xc7198f2d, 4, 2, 0}, + {0xc71c7619, 156, 1, 1188}, + {0xc71d4ce7, 100, 0, 0}, + {0xc71e77ca, 165, 0, 0}, + {0xc7347bc0, 46, 1, 188}, + {0xc73b82fc, 10, 1, 0}, + {0xc740eb46, 4, 1, 1190}, + {0xc747ac41, 156, 0, 0}, + {0xc74dd019, 156, 0, 0}, + {0xc7642467, 100, 0, 0}, + {0xc769bb34, 55, 0, 0}, + {0xc76aadf4, 174, 0, 0}, + {0xc76fc363, 92, 2, 0}, + {0xc78d17d3, 126, 1, 290}, + {0xc7908669, 0, 1, 0}, + {0xc79d050e, 100, 0, 0}, + {0xc7a79be2, 117, 0, 0}, + {0xc7bce866, 0, 1, 0}, + {0xc7c09049, 126, 0, 0}, + {0xc7c64533, 66, 0, 0}, + {0xc7ce1429, 30, 0, 0}, + {0xc7f0c457, 14, 1, 1092}, + {0xc7f5b3d8, 0, 1, 0}, + {0xc7fbecc3, 100, 1, 490}, + {0xc7fd5388, 55, 0, 0}, + {0xc811dc7a, 117, 0, 0}, + {0xc8133b04, 117, 0, 0}, + {0xc8160857, 92, 0, 0}, + {0xc8272d94, 27, 1, 89}, + {0xc85bf3e3, 0, 1, 0}, + {0xc86a1f7d, 92, 0, 0}, + {0xc8859038, 100, 0, 0}, + {0xc89385df, 153, 1, 1193}, + {0xc898e6c1, 0, 1, 0}, + {0xc8a10b71, 142, 1, 991}, + {0xc8ad4f32, 126, 1, 789}, + {0xc8edc97e, 175, 0, 0}, + {0xc9187b43, 132, 0, 0}, + {0xc92b814b, 160, 2, 0}, + {0xc94ac75f, 0, 1, 0}, + {0xc95321a8, 126, 0, 0}, + {0xc95480d0, 100, 1, 987}, + {0xc9556b36, 155, 0, 0}, + {0xc96971e2, 126, 1, 1085}, + {0xc96c6f04, 192, 0, 0}, + {0xc970ad09, 69, 0, 0}, + {0xc973699d, 68, 1, 390}, + {0xc9762ab4, 24, 0, 0}, + {0xc993258f, 195, 0, 0}, + {0xc99b690a, 160, 2, 0}, + {0xc9aa7110, 100, 0, 0}, + {0xc9acec1d, 77, 0, 0}, + {0xc9c2a1ec, 92, 0, 0}, + {0xc9cce8f2, 126, 0, 0}, + {0xc9edf585, 46, 0, 0}, + {0xc9ffc5fc, 126, 0, 0}, + {0xca026f1f, 104, 1, 392}, + {0xca033b3a, 153, 1, 1290}, + {0xca06cb60, 0, 1, 0}, + {0xca0a869e, 40, 1, 0}, + {0xca114aac, 40, 1, 0}, + {0xca24a1a2, 156, 0, 0}, + {0xca279a8f, 0, 1, 0}, + {0xca37aa92, 200, 1, 0}, + {0xca3e9b1a, 126, 0, 0}, + {0xca594ace, 126, 1, 1088}, + {0xca680e61, 74, 1, 0}, + {0xca69751b, 117, 0, 0}, + {0xca6a7bf1, 117, 0, 0}, + {0xca7afb3f, 66, 0, 0}, + {0xca8efc8a, 117, 0, 0}, + {0xca96ad0e, 100, 0, 0}, + {0xcaaf5c6b, 0, 1, 0}, + {0xcac5c86b, 135, 1, 0}, + {0xcaf9cc4c, 115, 1, 690}, + {0xcb02a930, 126, 1, 1090}, + {0xcb0a3af4, 174, 0, 0}, + {0xcb0a76b1, 155, 0, 0}, + {0xcb15ba34, 46, 1, 0}, + {0xcb1967e9, 160, 1, 188}, + {0xcb25a2ca, 104, 1, 1193}, + {0xcb275051, 100, 1, 689}, + {0xcb32e243, 34, 0, 0}, + {0xcb475567, 163, 1, 190}, + {0xcb5acb49, 34, 0, 0}, + {0xcb5c3b38, 178, 0, 0}, + {0xcb66694a, 66, 0, 0}, + {0xcb7e529d, 24, 0, 0}, + {0xcb9e67a6, 160, 0, 0}, + {0xcbb545c1, 160, 0, 0}, + {0xcbd413a9, 55, 0, 0}, + {0xcbd6cf2c, 158, 1, 1292}, + {0xcbe85490, 126, 0, 0}, + {0xcbe9482f, 24, 0, 0}, + {0xcbf4366f, 166, 1, 0}, + {0xcbf4cdf6, 77, 0, 0}, + {0xcc0034e3, 143, 0, 0}, + {0xcc2c4b5d, 126, 0, 0}, + {0xcc3544b0, 93, 0, 0}, + {0xcc37094c, 57, 1, 490}, + {0xcc65ab83, 160, 0, 0}, + {0xcc6ad67b, 198, 0, 0}, + {0xcc710d52, 126, 0, 0}, + {0xcc7a4dca, 111, 0, 0}, + {0xcc8a8652, 34, 0, 0}, + {0xcc93a320, 92, 0, 0}, + {0xcca983f8, 24, 1, 1086}, + {0xccaf32dc, 68, 1, 788}, + {0xccaf543a, 160, 0, 0}, + {0xcccaf368, 33, 1, 0}, + {0xccd575a1, 0, 1, 0}, + {0xccdcbfc6, 33, 1, 0}, + {0xcce0cc10, 34, 0, 0}, + {0xccf6bdb2, 165, 0, 0}, + {0xcd00249b, 126, 0, 0}, + {0xcd01471d, 116, 1, 190}, + {0xcd05d34e, 97, 1, 0}, // "Proto" + {0xcd10dce2, 89, 1, 990}, + {0xcd2a6427, 158, 1, 1091}, + {0xcd2a73f0, 188, 1, 1091}, + {0xcd2ac92c, 126, 1, 1090}, + {0xcd2b058c, 0, 1, 0}, + {0xcd35e2e9, 99, 1, 1191}, + {0xcd50a092, 166, 1, 0}, + {0xcd6549ea, 100, 0, 0}, + {0xcd7a2fd7, 155, 0, 0}, + {0xcd8233ef, 133, 0, 0}, + {0xcd8b279f, 126, 2, 0}, + {0xcd9acf43, 69, 0, 0}, + {0xcdac270d, 68, 1, 1088}, + {0xcdb8c6a5, 41, 0, 0}, + {0xcdc3350b, 127, 1, 1091}, + {0xcdc641fc, 115, 1, 1093}, + {0xcdca4513, 34, 0, 0}, + {0xcdd62ab2, 45, 0, 0}, + {0xcdef1c0d, 150, 0, 0}, + {0xce00022d, 165, 1, 289}, + {0xce06f2d4, 76, 0, 0}, + {0xce07194f, 24, 0, 0}, + {0xce090b49, 33, 1, 0}, + {0xce228874, 163, 1, 390}, + {0xce27fa04, 126, 1, 1085}, + {0xce3f8820, 0, 1, 0}, + {0xce77b4be, 4, 1, 692}, + {0xce90983a, 160, 0, 0}, + {0xcea35d5a, 160, 0, 0}, + {0xceb65b06, 182, 1, 693}, + {0xcebd2a31, 126, 1, 590}, + {0xcebe3e09, 156, 0, 0}, + {0xcece4cfc, 92, 0, 0}, + {0xceceaa31, 0, 1, 0}, + {0xcee5857b, 2, 0, 0}, + {0xcf0c9d97, 97, 0, 0}, + {0xcf23290f, 117, 0, 0}, + {0xcf26a149, 142, 1, 1089}, + {0xcf26c7a1, 104, 1, 989}, + {0xcf322bb3, 182, 1, 389}, + {0xcf3b904c, 81, 0, 0}, + {0xcf40b1c5, 97, 0, 0}, + {0xcf4487a2, 65, 1, 991}, + {0xcf4dbdbe, 160, 0, 0}, + {0xcf5df8ba, 126, 1, 1093}, + {0xcf5f8af0, 22, 1, 1291}, + {0xcf6c88b6, 126, 1, 686}, + {0xcf7ca9bd, 170, 2, 0}, + {0xcf849f72, 165, 2, 0}, + {0xcf9cf7a2, 179, 0, 0}, + {0xcfae9dfa, 17, 1, 990}, + {0xcfd0cc7b, 193, 0, 0}, + {0xcfd4a281, 150, 0, 0}, + {0xcfd5ac62, 200, 1, 0}, + {0xd029f841, 34, 2, 0}, + {0xd0350e25, 41, 0, 0}, + {0xd054ffb0, 126, 1, 394}, + {0xd05cea69, 66, 0, 0}, + {0xd071a15d, 0, 0, 87}, + {0xd09b74dc, 143, 0, 0}, + {0xd0a9f4e1, 55, 0, 0}, + {0xd0c6e757, 195, 0, 0}, + {0xd0cc5ec8, 77, 0, 0}, + {0xd0df726e, 180, 0, 0}, + {0xd0e53454, 160, 0, 0}, + {0xd0eb749f, 92, 0, 0}, + {0xd0f70e36, 156, 2, 0}, + {0xd131bf15, 34, 1, 8}, + {0xd1397940, 89, 0, 0}, + {0xd152fb02, 173, 1, 790}, + {0xd153caf6, 88, 2, 90}, // Swords and Serpents (E) [!] + {0xd161888b, 84, 2, 0}, + {0xd1691028, 117, 0, 0}, + {0xd175b0cb, 191, 0, 0}, + {0xd188963d, 40, 1, 0}, + {0xd18a3dde, 126, 0, 0}, + {0xd18e6be3, 142, 1, 1289}, + {0xd19addeb, 126, 1, 490}, + {0xd19dcb2b, 123, 1, 991}, + {0xd1bde95c, 11, 1, 0}, + {0xd1d0496f, 186, 1, 1193}, + {0xd1d1b61e, 92, 0, 0}, + {0xd1e50064, 21, 0, 0}, + {0xd1f7df3a, 178, 0, 0}, + {0xd1fb1533, 156, 1, 90}, + {0xd202612b, 89, 0, 0}, + {0xd2038fc5, 89, 0, 0}, + {0xd20bb617, 92, 2, 0}, + {0xd2121f97, 100, 1, 1288}, + {0xd21da4f7, 56, 2, 0}, + {0xd24de7f9, 76, 0, 0}, + {0xd255c7a3, 179, 0, 0}, + {0xd2562072, 4, 1, 392}, + {0xd2574720, 34, 1, 1288}, + {0xd2699893, 117, 0, 0}, + {0xd26efd78, 126, 1, 1188}, + {0xd273b409, 160, 1, 1092}, + {0xd2754add, 0, 1, 0}, + {0xd27b9d50, 104, 0, 0}, + {0xd29db3c7, 155, 0, 0}, + {0xd2bc86f3, 93, 0, 0}, + {0xd2e775d8, 174, 0, 0}, + {0xd308d52c, 29, 1, 987}, + {0xd313adfd, 38, 0, 0}, + {0xd31dc910, 34, 0, 0}, + {0xd31e4238, 126, 1, 686}, + {0xd3428e2e, 65, 1, 991}, + {0xd343c66a, 24, 0, 0}, + {0xd359e02e, 140, 0, 0}, + {0xd377cc36, 31, 1, 0}, + {0xd383dc94, 97, 1, 490}, + {0xd386c2bb, 24, 0, 0}, + {0xd3a265ff, 38, 0, 0}, + {0xd3a91b41, 0, 1, 0}, + {0xd3bff72e, 104, 1, 288}, + {0xd3d248c9, 89, 0, 0}, + {0xd3ee724e, 164, 0, 0}, + {0xd4042702, 97, 0, 0}, + {0xd415571f, 0, 1, 0}, + {0xd4304457, 34, 1, 192}, + {0xd445f698, 126, 1, 0}, + {0xd4611b79, 104, 1, 992}, + {0xd467c0cc, 100, 0, 0}, + {0xd48b14ed, 92, 0, 0}, + {0xd4a443d0, 92, 0, 0}, + {0xd4a70d2b, 74, 1, 1291}, + {0xd4a76b07, 15, 1, 0}, + {0xd4d02ebb, 155, 0, 0}, + {0xd4d9e21a, 126, 1, 1085}, + {0xd4dc528d, 34, 1, 192}, + {0xd4e4afcd, 62, 0, 0}, + {0xd4e8ec0b, 165, 1, 992}, + {0xd4f018f5, 126, 1, 1290}, + {0xd4f5287b, 77, 1, 89}, + {0xd52ebaa6, 100, 2, 0}, + {0xd532e98f, 126, 0, 0}, + {0xd534c98e, 165, 1, 690}, + {0xd548307f, 126, 1, 1085}, + {0xd54f21c7, 38, 0, 0}, + {0xd54f5da9, 144, 0, 0}, + {0xd554c455, 97, 0, 0}, + {0xd5566edf, 113, 1, 1292}, + {0xd568563f, 34, 0, 0}, + {0xd5883d6b, 166, 1, 0}, + {0xd58d14e3, 126, 1, 0}, + {0xd58fec16, 160, 1, 887}, + {0xd5941aa9, 120, 0, 0}, + {0xd5c64257, 126, 1, 1085}, + {0xd5c71458, 65, 1, 988}, + {0xd5d7eac4, 126, 0, 0}, + {0xd5f95da7, 0, 1, 0}, + {0xd60e10aa, 24, 0, 0}, + {0xd63b30f5, 74, 1, 1291}, + {0xd653e27b, 120, 0, 0}, + {0xd65c0697, 104, 1, 891}, + {0xd666560f, 117, 0, 0}, + {0xd679627a, 104, 1, 1092}, + {0xd68a6f33, 136, 0, 0}, + {0xd6ae4ab5, 120, 1, 1290}, + {0xd6cc1f78, 92, 0, 0}, + {0xd6d9de37, 3, 1, 592}, + {0xd6da2a1e, 117, 0, 0}, + {0xd6f7383e, 100, 2, 0}, + {0xd711ea15, 126, 1, 1088}, + {0xd71746d4, 156, 0, 0}, + {0xd7215873, 55, 0, 0}, + {0xd72fb9c8, 4, 1, 1292}, + {0xd738c059, 24, 1, 890}, + {0xd73aa04c, 127, 1, 693}, + {0xd745d7cb, 24, 2, 0}, + {0xd74b2719, 126, 1, 1188}, + {0xd7679a0e, 165, 2, 0}, + {0xd76a57bf, 173, 2, 0}, + {0xd770c1a9, 126, 1, 787}, + {0xd777d47d, 25, 0, 0}, + {0xd7794afc, 126, 1, 593}, + {0xd77b73c8, 34, 1, 690}, + {0xd78729fd, 92, 1, 0}, + {0xd78f47ae, 91, 1, 792}, + {0xd7961e01, 100, 0, 0}, + {0xd7a97b38, 161, 0, 0}, + {0xd7aa0b6d, 117, 0, 0}, + {0xd7ae93df, 0, 1, 0}, + {0xd7b35f7d, 100, 2, 0}, + {0xd7c4816b, 182, 1, 389}, + {0xd7cb398f, 165, 0, 0}, + {0xd7e29c03, 24, 1, 690}, + {0xd7f6320c, 74, 1, 0}, + {0xd7fabac1, 100, 0, 0}, + {0xd7fb558e, 83, 0, 0}, + {0xd7fda595, 117, 0, 0}, + {0xd821a1c6, 165, 0, 0}, + {0xd8230d0e, 153, 1, 492}, + {0xd836a90b, 126, 1, 389}, + {0xd83c671a, 104, 1, 1187}, + {0xd8447fa4, 92, 1, 90}, + {0xd84663b8, 178, 0, 0}, + {0xd84cda4c, 117, 0, 0}, + {0xd852c2f7, 149, 0, 0}, + {0xd8578bfd, 100, 1, 393}, + {0xd858033d, 143, 0, 0}, + {0xd85d26a4, 179, 0, 0}, + {0xd8748e0a, 92, 0, 0}, + {0xd878ebf5, 165, 0, 0}, + {0xd88293fa, 128, 0, 0}, + {0xd88d48d7, 160, 1, 192}, + {0xd89b40c8, 24, 0, 0}, + {0xd8a0c10c, 100, 0, 0}, + {0xd8e641a3, 32, 0, 0}, + {0xd8ee1e43, 32, 0, 0}, + {0xd8ee7669, 6, 1, 1290}, + {0xd8eff0df, 100, 0, 0}, + {0xd8f651e2, 77, 0, 0}, + {0xd907b2e8, 117, 0, 0}, + {0xd9084936, 176, 0, 0}, + {0xd920f9df, 160, 0, 0}, + {0xd923eb5b, 34, 0, 0}, + {0xd9350367, 147, 0, 0}, + {0xd9612836, 25, 0, 0}, + {0xd9736a6d, 2, 1, 492}, + {0xd97595a3, 92, 0, 0}, + {0xd979c8b7, 69, 0, 0}, + {0xd97c31b0, 117, 0, 0}, + {0xd9803a35, 117, 0, 0}, + {0xd98138c4, 34, 0, 0}, + {0xd988bdc0, 156, 0, 0}, + {0xd98e3e31, 0, 1, 0}, + {0xd99a2087, 100, 0, 0}, + {0xd9bb572c, 123, 1, 790}, + {0xd9c093b1, 160, 0, 0}, + {0xd9e8e747, 0, 1, 0}, + {0xd9f1e47c, 34, 0, 0}, + {0xda05d696, 126, 0, 0}, + {0xda122635, 93, 0, 0}, + {0xda23166a, 0, 1, 0}, + {0xda2cb59a, 104, 1, 1090}, + {0xda40e4a2, 120, 0, 0}, + {0xda4de327, 46, 0, 0}, + {0xda690d17, 20, 0, 0}, + {0xda6ccd81, 92, 0, 0}, + {0xda731f14, 133, 0, 0}, + {0xda8b5993, 117, 0, 0}, + {0xdaadebfa, 179, 0, 0}, + {0xdab84a9c, 160, 1, 0}, + {0xdaba9e8e, 142, 1, 1290}, + {0xdad34ee6, 191, 0, 0}, + {0xdaeb93ed, 100, 0, 0}, + {0xdaee19f2, 92, 1, 788}, + {0xdaf9d7e3, 46, 1, 587}, + {0xdb05106e, 24, 0, 0}, + {0xdb0b7db0, 100, 0, 0}, + {0xdb0c3656, 0, 1, 0}, + {0xdb0c7019, 0, 1, 0}, + {0xdb196068, 160, 0, 0}, + {0xdb2d711d, 117, 0, 0}, + {0xdb479677, 55, 0, 0}, + {0xdb511963, 54, 0, 0}, + {0xdb5169dc, 160, 0, 0}, + {0xdb53a88d, 92, 0, 0}, + {0xdb564628, 126, 0, 0}, + {0xdb70a67c, 30, 1, 990}, + {0xdb770ca7, 92, 1, 788}, + {0xdb844a39, 100, 0, 0}, + {0xdb9418e8, 100, 0, 0}, + {0xdb99d0cb, 33, 1, 0}, + {0xdb9dcf89, 77, 1, 189}, + {0xdba3a02e, 89, 0, 0}, + {0xdbb06a25, 24, 1, 1289}, + {0xdbbd1de9, 0, 1, 0}, + {0xdbdefceb, 37, 0, 0}, + {0xdbece74f, 25, 0, 0}, + {0xdbf90772, 143, 1, 1087}, + {0xdc02f095, 104, 1, 790}, + {0xdc1e07d2, 190, 0, 0}, + {0xdc270779, 166, 1, 0}, + {0xdc2a266c, 10, 1, 0}, + {0xdc33aed8, 2, 0, 0}, + {0xdc4da5d4, 46, 1, 687}, + {0xdc52bf0c, 24, 0, 0}, + {0xdc5930ab, 83, 0, 0}, + {0xdc75732f, 20, 0, 0}, + {0xdc87f63d, 114, 1, 89}, + {0xdcb71ca6, 159, 0, 0}, + {0xdcb7c0a1, 160, 0, 0}, + {0xdcb94341, 0, 1, 0}, + {0xdcb972ce, 24, 0, 0}, + {0xdcba4a78, 84, 2, 0}, + {0xdcbcc7a2, 0, 1, 0}, + {0xdcd8d6f4, 39, 0, 0}, + {0xdcdf06de, 117, 0, 0}, + {0xdcea8dc4, 117, 0, 0}, + {0xdcf5fa30, 46, 0, 0}, + {0xdcfd85fc, 0, 1, 0}, + {0xdd29fd59, 117, 0, 0}, + {0xdd454208, 117, 0, 0}, + {0xdd516f89, 57, 1, 490}, + {0xdd53c4ae, 100, 0, 0}, + {0xdd564975, 20, 1, 190}, + {0xdd6a44b5, 156, 0, 0}, + {0xdd8ed0f7, 24, 0, 0}, + {0xdda9f780, 92, 0, 0}, + {0xddad5880, 84, 0, 0}, + {0xddd90c39, 120, 1, 1290}, + {0xdddc649b, 117, 0, 0}, + {0xdde40381, 199, 0, 0}, + {0xdde46aad, 45, 0, 0}, + {0xddf29542, 4, 1, 987}, + {0xddfb8dd5, 159, 0, 0}, + {0xde182c88, 46, 1, 790}, + {0xde1d762f, 0, 1, 0}, + {0xde2070ab, 117, 0, 0}, + {0xde25b90f, 100, 1, 487}, + {0xde395efd, 100, 0, 0}, + {0xde7e6767, 87, 1, 93}, + {0xde8c89ab, 126, 1, 1290}, + {0xde8fd935, 126, 1, 1189}, + {0xde9c9c64, 160, 0, 0}, + {0xdea0d843, 57, 2, 0}, + {0xdea62da3, 24, 0, 0}, + {0xdeb21c9e, 177, 0, 0}, + {0xdebea5a6, 165, 0, 0}, + {0xdeddd5e5, 164, 0, 0}, + {0xdee059e7, 114, 1, 390}, + {0xdefd7665, 115, 1, 93}, + {0xdefe6e8d, 90, 0, 0}, + {0xdf16890a, 107, 0, 0}, + {0xdf189d34, 100, 0, 0}, + {0xdf28a039, 165, 1, 787}, + {0xdf3776c6, 21, 0, 0}, + {0xdf38e21c, 48, 0, 0}, + {0xdf3e45d2, 100, 0, 0}, + {0xdf3eb610, 100, 0, 0}, + {0xdf43e073, 196, 1, 0}, // "Proto" + {0xdf58fc5a, 126, 1, 1085}, + {0xdf64963b, 115, 1, 190}, + {0xdf67daa1, 126, 1, 1085}, + {0xdfa111f1, 153, 1, 993}, + {0xdfc0ce21, 23, 0, 0}, + {0xdfd70e27, 24, 1, 988}, + {0xdfd9a2ee, 126, 0, 0}, + {0xdff6cf19, 46, 1, 1187}, + {0xe02133ac, 147, 0, 0}, + {0xe03d1e02, 66, 0, 0}, + {0xe042a0ef, 0, 1, 0}, + {0xe054a381, 15, 1, 0}, + {0xe06a060b, 67, 1, 91}, + {0xe086e68e, 117, 0, 0}, + {0xe08c8a60, 39, 0, 0}, + {0xe095c3f2, 160, 1, 893}, + {0xe09d2cd0, 92, 1, 91}, + {0xe0ac6242, 100, 2, 0}, + {0xe0bc622d, 160, 1, 1190}, + {0xe0cbc2ba, 120, 0, 0}, + {0xe0fe0a18, 100, 1, 1292}, + {0xe0fffbd2, 34, 2, 0}, + {0xe12bb5fb, 158, 1, 1192}, + {0xe1383deb, 100, 0, 0}, + {0xe1404f15, 24, 0, 0}, + {0xe145b441, 68, 1, 692}, + {0xe14a7971, 0, 1, 0}, + {0xe1526228, 117, 0, 0}, + {0xe15c973d, 94, 0, 0}, + {0xe16bb5fe, 89, 0, 0}, + {0xe170404c, 24, 0, 0}, + {0xe1789032, 38, 0, 0}, + {0xe17e3b54, 196, 1, 894}, + {0xe180297f, 104, 1, 289}, + {0xe18ab050, 24, 0, 0}, + {0xe19293a2, 2, 0, 0}, + {0xe19a2473, 164, 0, 0}, + {0xe19ee99c, 100, 1, 192}, + {0xe1a2e5b5, 117, 0, 0}, + {0xe1a987e7, 46, 0, 0}, + {0xe1acc990, 72, 0, 0}, + {0xe1b260da, 165, 0, 0}, + {0xe1c03eb6, 78, 0, 0}, + {0xe1c41d7c, 142, 1, 990}, + {0xe1cefa12, 126, 2, 0}, + {0xe1dc9b54, 195, 0, 0}, + {0xe1e307f1, 126, 0, 0}, + {0xe20779c6, 77, 0, 0}, + {0xe20fca45, 156, 0, 0}, + {0xe2281986, 34, 0, 0}, + {0xe2313813, 34, 1, 390}, + {0xe23e6fac, 77, 0, 0}, + {0xe2428b3c, 166, 1, 90}, + {0xe24483b1, 165, 0, 0}, + {0xe24704c9, 160, 0, 0}, + {0xe24df353, 193, 0, 0}, + {0xe266f83a, 77, 0, 0}, + {0xe28f2596, 117, 0, 0}, + {0xe292aa10, 57, 1, 1087}, + {0xe294c86f, 159, 0, 0}, + {0xe2a79a57, 69, 0, 0}, + {0xe2b43a68, 12, 1, 489}, + {0xe2c0a2be, 156, 0, 0}, + {0xe2c4edce, 156, 1, 990}, + {0xe2e92524, 150, 0, 0}, + {0xe2eca5ce, 24, 0, 0}, + {0xe305202e, 149, 0, 0}, + {0xe30552db, 32, 0, 0}, + {0xe30736d7, 29, 1, 987}, + {0xe30b2bcf, 180, 0, 0}, + {0xe30b7f64, 89, 0, 0}, + {0xe333ffa1, 72, 0, 0}, + {0xe349af38, 100, 0, 0}, + {0xe35321bc, 166, 1, 0}, + {0xe353969f, 92, 1, 291}, + {0xe3626d51, 92, 0, 0}, + {0xe362ecdc, 192, 0, 0}, + {0xe3664231, 77, 0, 0}, + {0xe36d5991, 0, 1, 0}, + {0xe3765667, 100, 1, 190}, + {0xe37f82cc, 126, 1, 0}, + {0xe386da54, 126, 0, 0}, + {0xe387c77f, 6, 1, 492}, + {0xe3a6d7f6, 0, 1, 0}, + {0xe3b98143, 100, 0, 0}, + {0xe3c5bb3d, 188, 1, 990}, + {0xe3ef6c9a, 126, 0, 0}, + {0xe3f3f6ae, 115, 1, 1288}, + {0xe402b134, 56, 2, 0}, + {0xe40b4973, 117, 0, 0}, + {0xe40b593b, 0, 1, 0}, + {0xe41ae491, 46, 1, 390}, + {0xe429f0d3, 176, 0, 0}, + {0xe43075eb, 117, 0, 0}, + {0xe431136d, 0, 1, 0}, + {0xe44001d8, 201, 0, 0}, + {0xe4541f6d, 0, 1, 0}, + {0xe45485a5, 117, 0, 0}, + {0xe46c156d, 133, 0, 0}, + {0xe476313e, 0, 1, 0}, + {0xe47e9fa7, 160, 0, 0}, + {0xe488a2f0, 166, 1, 0}, + {0xe492d45a, 89, 0, 0}, + {0xe49fc53e, 24, 0, 0}, + {0xe4a1777e, 100, 0, 0}, + {0xe4c04eea, 100, 0, 0}, + {0xe4c1a245, 6, 2, 0}, + {0xe4ceead1, 100, 0, 0}, + {0xe4cf03d3, 24, 0, 0}, + {0xe4cf4e13, 46, 0, 0}, + {0xe4e3d2ed, 117, 0, 0}, + {0xe50a9130, 97, 1, 889}, + {0xe52687d0, 156, 1, 290}, + {0xe53f7a55, 124, 0, 0}, + {0xe54138a9, 126, 2, 0}, + {0xe542e3cf, 100, 1, 192}, + {0xe5703fc4, 77, 1, 1090}, + {0xe575687c, 3, 1, 993}, + {0xe583ec5e, 151, 0, 0}, + {0xe5a8401b, 6, 2, 0}, + {0xe5a972be, 126, 2, 0}, + {0xe5d11921, 117, 0, 0}, + {0xe5d49424, 193, 0, 0}, + {0xe5d93c8f, 156, 1, 292}, + {0xe5edcde3, 126, 1, 1085}, + {0xe5f49166, 4, 1, 1289}, + {0xe60c5258, 20, 0, 0}, + {0xe61e89ee, 156, 0, 0}, + {0xe62a531b, 156, 0, 0}, + {0xe62e3382, 33, 1, 0}, + {0xe63d9193, 133, 0, 0}, + {0xe63f7d0b, 92, 0, 0}, + {0xe641bd98, 0, 1, 0}, + {0xe64b8975, 117, 0, 0}, + {0xe6517826, 0, 1, 0}, + {0xe66bddcf, 97, 0, 0}, + {0xe66e59a0, 77, 0, 0}, + {0xe675ba2a, 120, 0, 0}, + {0xe6779074, 0, 1, 0}, + {0xe699a97c, 24, 0, 0}, + {0xe6a477b2, 4, 1, 987}, + {0xe6b30bb3, 34, 0, 0}, + {0xe6c28c5f, 99, 0, 0}, + {0xe6c9029e, 100, 0, 0}, + {0xe6d73266, 9, 0, 0}, + {0xe6df6616, 100, 0, 0}, + {0xe6f08e93, 126, 1, 387}, + {0xe6f5fdd2, 92, 0, 0}, + {0xe713f464, 156, 0, 0}, + {0xe71d034e, 100, 2, 91}, // Snake's Revenge (E) [!] + {0xe737a11f, 0, 1, 0}, + {0xe73e7260, 166, 1, 0}, + {0xe74a91bb, 12, 1, 690}, + {0xe74aa15a, 150, 0, 0}, + {0xe77268ec, 156, 0, 0}, + {0xe78a394c, 25, 0, 0}, + {0xe792de94, 2, 0, 0}, + {0xe7a3867b, 53, 0, 0}, + {0xe7b03b03, 24, 0, 0}, + {0xe7ba371d, 34, 1, 789}, + {0xe7baa5f6, 192, 0, 0}, + {0xe7c72dbb, 99, 1, 392}, + {0xe7c981a2, 22, 1, 1192}, + {0xe7da8a04, 153, 1, 292}, + {0xe7ff1a3d, 104, 1, 488}, + {0xe801f662, 160, 0, 0}, + {0xe80b9027, 164, 0, 0}, + {0xe840fd21, 126, 1, 290}, + {0xe84274c5, 92, 0, 0}, + {0xe8456051, 126, 0, 0}, + {0xe84f40e5, 142, 1, 792}, + {0xe86e4df1, 72, 0, 0}, + {0xe87d33a0, 133, 0, 0}, + {0xe880d426, 58, 1, 90}, + {0xe8941598, 166, 1, 0}, + {0xe8973b45, 101, 0, 0}, + {0xe8af6ff5, 178, 0, 0}, + {0xe8b20197, 126, 0, 0}, + {0xe8b4834c, 100, 0, 0}, + {0xe8b7c767, 117, 0, 0}, + {0xe8baa782, 192, 0, 0}, + {0xe8bbe2be, 195, 0, 0}, + {0xe8d26cb0, 126, 1, 0}, + {0xe8f8f7a5, 126, 2, 0}, + {0xe9023072, 92, 0, 0}, + {0xe90507fa, 100, 0, 0}, + {0xe909d14a, 77, 0, 0}, + {0xe90d4a9f, 92, 0, 0}, + {0xe911bcc4, 117, 0, 0}, + {0xe91548d8, 126, 0, 0}, + {0xe9176129, 160, 0, 0}, + {0xe91f17c2, 117, 0, 0}, + {0xe922037c, 117, 0, 0}, + {0xe92aed05, 97, 1, 190}, + {0xe93d07d7, 117, 0, 0}, + {0xe940d56f, 155, 0, 0}, + {0xe943ec4d, 24, 1, 791}, + {0xe949ef8a, 1, 0, 0}, + {0xe94d5181, 128, 0, 0}, + {0xe94e883d, 126, 2, 88}, // Super Mario Bros 2 (E) [!] + {0xe95454fc, 102, 0, 0}, + {0xe95752bd, 0, 1, 0}, + {0xe98ab943, 158, 1, 1193}, + {0xe9a6c211, 188, 1, 291}, + {0xe9aa0400, 90, 0, 0}, + {0xe9bc16ff, 128, 0, 0}, + {0xe9c387ec, 14, 1, 190}, + {0xe9cf747f, 34, 1, 85}, + {0xe9d1ec63, 92, 0, 0}, + {0xe9d352eb, 4, 2, 0}, + {0xe9e27b7b, 0, 1, 0}, + {0xe9f16673, 130, 1, 0}, + {0xe9f5be99, 0, 1, 0}, + {0xea08700b, 77, 0, 0}, + {0xea113128, 40, 1, 0}, + {0xea2b9d3f, 60, 0, 0}, + {0xea3e78dd, 92, 0, 0}, + {0xea4eb69e, 143, 1, 291}, + {0xea62f88f, 76, 0, 0}, + {0xea74c587, 100, 0, 0}, + {0xea79cc19, 150, 0, 0}, + {0xea812597, 92, 0, 0}, + {0xea89963f, 161, 0, 0}, + {0xea9f7a09, 200, 1, 0}, + {0xeab93cfb, 69, 0, 0}, + {0xead17d60, 166, 1, 0}, + {0xeadf0b97, 134, 0, 0}, + {0xeaf7ed72, 126, 1, 0}, + {0xeafc4944, 100, 0, 0}, + {0xeb0bda7e, 166, 1, 0}, + {0xeb0f903a, 142, 1, 92}, + {0xeb15169e, 131, 1, 690}, + {0xeb297321, 117, 0, 0}, + {0xeb2c8f4d, 34, 0, 0}, + {0xeb465156, 43, 0, 0}, + {0xeb61133b, 160, 1, 1190}, + {0xeb6daea5, 126, 1, 1088}, + {0xeb71a6c5, 34, 1, 392}, + {0xeb75ca30, 117, 0, 0}, + {0xeb764567, 117, 0, 0}, + {0xeb803610, 20, 1, 1191}, + {0xeb807320, 89, 0, 0}, + {0xeb84c54c, 104, 1, 591}, + {0xeb9960ee, 4, 1, 290}, + {0xebac24e9, 124, 0, 0}, + {0xebb1c045, 66, 0, 0}, + {0xebbc6ba8, 77, 0, 0}, + {0xebcf8419, 195, 0, 0}, + {0xebcfe7c5, 4, 1, 1289}, + {0xec04f0da, 155, 0, 0}, + {0xec0fc2de, 74, 1, 989}, + {0xec1c5ad5, 20, 1, 1190}, + {0xec3001c7, 29, 1, 987}, + {0xec461db9, 126, 0, 0}, + {0xec47296d, 147, 0, 0}, + {0xec69adf2, 65, 1, 988}, + {0xec8a884f, 100, 0, 0}, + {0xec968c51, 166, 1, 0}, + {0xeca43006, 126, 1, 1088}, + {0xecbf33ce, 124, 0, 0}, + {0xeccd4089, 92, 1, 1088}, + {0xecdbafa4, 160, 0, 0}, + {0xece525dd, 24, 1, 388}, + {0xece951ea, 138, 0, 0}, + {0xecec80b4, 92, 0, 0}, + {0xecf3964d, 133, 0, 0}, + {0xecfd3c69, 160, 0, 0}, + {0xed0aa77b, 77, 0, 0}, + {0xed2465be, 100, 1, 990}, + {0xed36649f, 89, 0, 0}, + {0xed4fb487, 92, 0, 0}, + {0xed55073f, 142, 1, 1289}, + {0xed588f00, 126, 0, 0}, + {0xed58dddd, 40, 1, 89}, + {0xed620003, 195, 0, 0}, + {0xed67811e, 126, 0, 0}, + {0xed77b453, 87, 2, 0}, + {0xed9a3ac6, 24, 0, 0}, + {0xedc3662b, 160, 1, 589}, + {0xedcf1b71, 153, 1, 690}, + {0xedd6d29b, 68, 1, 692}, + {0xedd7479a, 117, 0, 0}, + {0xeddcc468, 34, 0, 0}, + {0xeddd580b, 156, 1, 1090}, + {0xede55d1c, 178, 0, 0}, + {0xedea9ac6, 193, 1, 390}, + {0xee1b0d52, 92, 0, 0}, + {0xee309168, 0, 1, 0}, + {0xee43153e, 34, 0, 0}, + {0xee60b5d6, 117, 0, 0}, + {0xee64327b, 126, 0, 0}, + {0xee6892eb, 4, 1, 1091}, + {0xee693008, 127, 1, 493}, + {0xee6ef957, 161, 0, 0}, + {0xee711afb, 160, 0, 0}, + {0xee875b41, 133, 0, 0}, + {0xee8c9971, 126, 1, 0}, + {0xee921d8e, 188, 1, 689}, + {0xee993635, 69, 0, 0}, + {0xeeb16683, 117, 0, 0}, + {0xeeb7a62b, 126, 1, 1085}, + {0xeeb9252b, 156, 0, 0}, + {0xeee0c7f8, 57, 2, 0}, + {0xeee3f31c, 126, 0, 0}, + {0xeee6314e, 165, 0, 0}, + {0xeee9a682, 99, 0, 0}, + {0xef0c9672, 116, 1, 291}, + {0xef1c8906, 0, 1, 0}, + {0xef40790c, 160, 1, 1291}, + {0xef4649f6, 191, 0, 0}, + {0xef4f5903, 0, 1, 0}, + {0xef4f9b96, 130, 1, 0}, + {0xef5f1d5f, 93, 0, 0}, + {0xef6278f9, 163, 1, 89}, + {0xef7996bf, 117, 0, 0}, + {0xef7af338, 126, 0, 0}, + {0xefb09075, 34, 1, 989}, + {0xefb1df9e, 92, 0, 0}, + {0xefcc2da9, 100, 0, 0}, + {0xefcf375d, 110, 1, 1090}, + {0xefd4837f, 92, 0, 0}, + {0xefd51f04, 193, 1, 0}, + {0xefeb0c34, 100, 0, 0}, + {0xeffd40c3, 188, 1, 691}, + {0xeffe438f, 117, 0, 0}, + {0xeffeea40, 77, 0, 0}, + {0xf00355a0, 147, 0, 0}, + {0xf00584b6, 188, 1, 691}, + {0xf009ddd2, 24, 1, 492}, + {0xf011e490, 99, 1, 991}, + {0xf0302211, 165, 0, 0}, + {0xf04bc5ef, 160, 0, 0}, + {0xf053ac5f, 97, 0, 0}, + {0xf0552f02, 156, 0, 0}, + {0xf05870d5, 15, 1, 0}, + {0xf0632142, 100, 0, 0}, + {0xf08e8ef0, 192, 0, 0}, + {0xf08ed289, 34, 1, 1291}, + {0xf09d0316, 74, 1, 1090}, + {0xf0a4d2f4, 199, 0, 0}, + {0xf0a5eb24, 0, 1, 0}, + {0xf0ae68c0, 24, 0, 0}, + {0xf0c198ff, 0, 2, 0}, + {0xf0c77dcb, 165, 1, 891}, + {0xf0d02f6a, 42, 0, 0}, + {0xf0dab3d3, 115, 1, 1290}, + {0xf0df311e, 4, 1, 291}, + {0xf0e9971b, 34, 1, 1092}, + {0xf1081b1b, 46, 0, 0}, + {0xf159b106, 37, 0, 0}, + {0xf161a5d8, 34, 0, 0}, + {0xf17486df, 43, 0, 0}, + {0xf18180cb, 81, 0, 0}, + {0xf181c021, 29, 1, 489}, + {0xf18eef69, 188, 1, 289}, + {0xf19a11af, 93, 0, 0}, + {0xf19a4249, 0, 1, 0}, + {0xf1a03778, 24, 0, 0}, + {0xf1bdcf18, 160, 0, 0}, + {0xf1c76aed, 39, 0, 0}, + {0xf1d07e3e, 0, 1, 0}, + {0xf1d861ef, 126, 1, 0}, + {0xf1db1ca5, 129, 2, 0}, + {0xf1e2b9e8, 173, 0, 0}, + {0xf1e6b576, 92, 0, 0}, + {0xf1fed9b8, 43, 1, 889}, + {0xf1ffd9f8, 126, 1, 686}, + {0xf200080d, 0, 1, 0}, + {0xf2099df0, 46, 1, 188}, + {0xf210e68f, 126, 1, 1188}, + {0xf21af99c, 126, 0, 0}, + {0xf225e3f5, 161, 0, 0}, + {0xf23c035c, 38, 0, 0}, + {0xf2594374, 136, 0, 0}, + {0xf2641ad0, 126, 1, 890}, + {0xf280184b, 178, 0, 0}, + {0xf28a5b8d, 153, 2, 0}, + {0xf2a6ace1, 92, 1, 190}, + {0xf2a99db1, 100, 0, 0}, + {0xf2a9f64d, 92, 0, 0}, + {0xf2c4836f, 0, 1, 0}, + {0xf2d03ada, 2, 0, 0}, + {0xf2df30b2, 0, 1, 0}, + {0xf2ec0901, 156, 0, 0}, + {0xf2fc8212, 77, 0, 0}, + {0xf304f1b9, 6, 1, 1289}, + {0xf308e97a, 46, 0, 0}, + {0xf31d36a3, 158, 1, 1091}, + {0xf31dcc15, 121, 0, 0}, + {0xf32748a1, 59, 0, 0}, + {0xf34175b3, 92, 0, 0}, + {0xf34190b4, 34, 0, 0}, + {0xf3473009, 117, 0, 0}, + {0xf35e4442, 122, 0, 0}, + {0xf35f14d3, 43, 1, 390}, + {0xf3623561, 175, 0, 0}, + {0xf37befd5, 76, 1, 792}, + {0xf3808245, 76, 0, 0}, + {0xf3826759, 74, 1, 1190}, + {0xf39fd253, 34, 1, 1088}, + {0xf3a36c5f, 38, 0, 0}, + {0xf3c13f19, 156, 0, 0}, + {0xf3c743aa, 34, 1, 1293}, + {0xf3e394d1, 139, 0, 0}, + {0xf3f1269d, 165, 0, 0}, + {0xf3f3a491, 126, 2, 0}, + {0xf3feb3ab, 164, 0, 0}, + {0xf4024666, 99, 1, 1290}, + {0xf4120e58, 99, 0, 0}, + {0xf4174da2, 156, 0, 0}, + {0xf419b383, 174, 0, 0}, + {0xf41add60, 117, 0, 0}, + {0xf42b0dbd, 46, 1, 1186}, + {0xf435dc0c, 171, 0, 0}, + {0xf4615036, 92, 1, 689}, + {0xf470d4e3, 4, 1, 1291}, + {0xf4c83de3, 24, 0, 0}, + {0xf4cd4998, 99, 0, 0}, + {0xf4d46622, 0, 1, 0}, + {0xf4d70c17, 92, 1, 588}, + {0xf4dd5ba5, 144, 0, 0}, + {0xf4dfdb14, 150, 1, 791}, + {0xf4e5df0e, 128, 0, 0}, + {0xf4fb65dd, 69, 0, 0}, + {0xf511566c, 117, 0, 0}, + {0xf518dd58, 114, 1, 690}, + {0xf51e2ffe, 153, 1, 689}, + {0xf532f09a, 193, 1, 988}, + {0xf5399c3c, 126, 0, 0}, + {0xf540677b, 99, 0, 0}, + {0xf54b34bd, 188, 1, 190}, + {0xf54e1dcc, 92, 1, 788}, + {0xf56a5b10, 126, 1, 686}, + {0xf585407a, 133, 0, 0}, + {0xf59cfc3d, 92, 2, 0}, + {0xf59da73c, 160, 0, 0}, + {0xf5a1b8fb, 87, 2, 0}, + {0xf5bfd030, 100, 2, 0}, + {0xf5d061ca, 156, 1, 987}, + {0xf5dfa4a2, 6, 2, 0}, + {0xf5f435b1, 77, 0, 0}, + {0xf5faae4f, 117, 0, 0}, + {0xf5fe896f, 30, 0, 0}, + {0xf6035030, 100, 1, 288}, + {0xf60f6667, 100, 0, 0}, + {0xf6139ee9, 174, 0, 0}, + {0xf613a8f9, 114, 1, 389}, + {0xf6271a51, 100, 0, 0}, + {0xf633813d, 34, 0, 0}, + {0xf635c594, 156, 0, 0}, + {0xf6419d79, 100, 0, 0}, + {0xf64cb545, 92, 0, 0}, + {0xf651398d, 160, 1, 490}, + {0xf66ec512, 126, 0, 0}, + {0xf6751d3d, 54, 0, 0}, + {0xf6898a59, 158, 1, 1192}, + {0xf699ee7e, 166, 1, 0}, + {0xf6aea515, 0, 1, 0}, + {0xf6b24806, 0, 1, 0}, + {0xf6b9799c, 126, 1, 991}, + {0xf6de2aa2, 100, 0, 0}, + {0xf6fa4453, 4, 1, 790}, + {0xf714fae3, 99, 0, 0}, + {0xf71a9931, 4, 2, 0}, + {0xf732c8fd, 33, 1, 0}, + {0xf735d926, 100, 0, 0}, + {0xf74dfc91, 74, 1, 390}, + {0xf751f337, 46, 0, 0}, + {0xf760f1cb, 97, 0, 0}, + {0xf76c815e, 160, 1, 989}, + {0xf786d602, 4, 1, 588}, + {0xf79a75d7, 126, 1, 1294}, + {0xf79d684a, 2, 0, 0}, + {0xf7b852e4, 37, 0, 0}, + {0xf7bc7104, 43, 1, 389}, + {0xf7d20181, 93, 0, 0}, + {0xf7d51d87, 2, 0, 0}, + {0xf7de03e0, 181, 0, 0}, + {0xf7e07b83, 63, 0, 0}, + {0xf7e84558, 77, 1, 1092}, + {0xf7f9785a, 82, 1, 991}, + {0xf808af60, 156, 0, 0}, + {0xf80bdc50, 160, 0, 0}, + {0xf81e0fed, 68, 1, 489}, + {0xf83c4c49, 97, 1, 1290}, + {0xf85084a3, 25, 0, 0}, + {0xf85bbf3e, 126, 1, 0}, // "Proto" + {0xf85e264d, 160, 0, 0}, + {0xf865ec8d, 53, 0, 0}, + {0xf86d8d8a, 126, 0, 0}, + {0xf885d931, 75, 0, 0}, + {0xf89170c5, 144, 0, 0}, + {0xf89300fb, 178, 0, 0}, + {0xf8a713be, 0, 1, 0}, + {0xf8b6cb9c, 95, 0, 0}, + {0xf8d53171, 147, 0, 0}, + {0xf8da2506, 24, 0, 0}, + {0xf8f2b56b, 12, 1, 489}, + {0xf90ae80e, 0, 1, 0}, + {0xf919795d, 52, 2, 0}, + {0xf91bac83, 65, 1, 1093}, + {0xf927fa43, 76, 0, 0}, + {0xf92be3ec, 166, 1, 0}, + {0xf92be7f2, 115, 1, 591}, + {0xf95d26a9, 134, 0, 0}, + {0xf9622bfa, 126, 0, 0}, + {0xf96d07c8, 173, 0, 0}, + {0xf982cdf5, 34, 0, 0}, + {0xf989296c, 190, 0, 0}, + {0xf98ee444, 43, 0, 0}, + {0xf99e37eb, 20, 1, 190}, + {0xf9b4c729, 117, 0, 0}, + {0xf9c22e00, 117, 0, 0}, + {0xf9c7e1b2, 0, 1, 0}, + {0xf9d5ae4a, 83, 0, 0}, + {0xf9fd48cd, 77, 1, 194}, + {0xfa014ba1, 100, 2, 0}, + {0xfa258f2f, 48, 0, 0}, + {0xfa2a8a8b, 124, 0, 0}, + {0xfa322d23, 29, 1, 987}, + {0xfa3bfc11, 115, 1, 1090}, + {0xfa43146b, 29, 1, 489}, + {0xfa46c411, 126, 0, 0}, + {0xfa4b1d72, 148, 0, 0}, + {0xfa6d4281, 32, 0, 0}, + {0xfa704c86, 160, 0, 0}, + {0xfa7ee642, 34, 2, 0}, + {0xfa8339a5, 100, 1, 192}, + {0xfa839dc9, 22, 1, 1192}, + {0xfa918f22, 102, 0, 0}, + {0xfaa957b1, 160, 1, 391}, + {0xfaeb390b, 133, 0, 0}, + {0xfaf04375, 95, 0, 0}, + {0xfaf48d27, 0, 1, 0}, + {0xfaf802d1, 39, 0, 0}, + {0xfaff4177, 39, 0, 0}, + {0xfb03a704, 34, 0, 0}, + {0xfb1c0551, 150, 0, 0}, + {0xfb26ff02, 69, 0, 0}, + {0xfb4092fa, 46, 1, 1292}, + {0xfb40d76c, 127, 2, 0}, + {0xfb5f213e, 41, 0, 0}, + {0xfb69c131, 40, 1, 0}, + {0xfb72e586, 0, 1, 0}, + {0xfb7f90fa, 100, 0, 0}, + {0xfb847d1a, 139, 0, 0}, + {0xfb8672a2, 171, 0, 0}, + {0xfb8a9b80, 102, 0, 0}, + {0xfb98d46e, 126, 1, 1085}, + {0xfba98643, 117, 0, 0}, + {0xfbaab554, 68, 1, 989}, + {0xfbd87b3e, 191, 0, 0}, + {0xfbf8a785, 65, 1, 988}, + {0xfbfc6a6c, 100, 2, 0}, + {0xfc00a282, 92, 0, 0}, + {0xfc201c46, 97, 0, 0}, + {0xfc2b7cfa, 97, 1, 1290}, + {0xfc3e5c86, 34, 1, 287}, + {0xfc5783a7, 34, 1, 194}, + {0xfc6201e7, 164, 0, 0}, + {0xfc8f6a1c, 183, 0, 0}, + {0xfcb13110, 46, 0, 0}, + {0xfcb5cb1e, 174, 0, 0}, + {0xfcbf28b1, 100, 0, 0}, + {0xfcd772eb, 91, 2, 0}, + {0xfcdaca80, 160, 0, 0}, + {0xfcde8825, 97, 0, 0}, + {0xfce408a4, 165, 1, 1187}, + {0xfcebcc5f, 126, 0, 0}, + {0xfcfa4c1e, 44, 0, 0}, + {0xfd0299c3, 46, 0, 0}, + {0xfd17e704, 57, 1, 1191}, + {0xfd214c58, 117, 0, 0}, + {0xfd3c3147, 100, 0, 0}, + {0xfd3fc292, 89, 0, 0}, + {0xfd45e9c1, 23, 0, 0}, + {0xfd529896, 173, 0, 0}, + {0xfd5d5c55, 0, 1, 0}, + {0xfd65afff, 68, 1, 290}, + {0xfd719491, 0, 1, 0}, + {0xfd7dd2b0, 77, 0, 0}, + {0xfd7e9a7e, 127, 2, 0}, + {0xfd8d6c75, 115, 1, 1090}, + {0xfd9ad6a2, 34, 1, 1287}, + {0xfda76f70, 120, 0, 0}, + {0xfdb1917e, 166, 0, 0}, + {0xfdb8aa9a, 89, 0, 0}, + {0xfdbca5d1, 117, 0, 0}, + {0xfddf2135, 34, 0, 0}, + {0xfde14cce, 44, 0, 0}, + {0xfde1c7ed, 74, 1, 1090}, + {0xfde61002, 92, 0, 0}, + {0xfde79681, 156, 0, 0}, + {0xfe18e6b6, 2, 0, 0}, + {0xfe2dab28, 156, 1, 987}, + {0xfe3488d1, 99, 0, 0}, + {0xfe35d144, 126, 0, 0}, + {0xfe364be5, 20, 0, 0}, + {0xfe37b30d, 128, 0, 0}, + {0xfe387fe5, 89, 0, 0}, + {0xfe4e5b11, 202, 0, 0}, + {0xfe4ed42b, 191, 0, 0}, + {0xfe58928a, 68, 1, 489}, + {0xfe5f17f0, 126, 1, 692}, + {0xfe94066d, 147, 0, 0}, + {0xfe99bbed, 16, 0, 0}, + {0xfeac6916, 156, 0, 0}, + {0xfecf92c8, 0, 1, 0}, + {0xfed27aca, 160, 0, 0}, + {0xfefe9064, 0, 1, 0}, + {0xff0bd357, 34, 0, 0}, + {0xff1412ea, 100, 0, 0}, + {0xff24d794, 126, 1, 1085}, + {0xff2c8ee4, 156, 0, 0}, + {0xff5135a3, 126, 0, 0}, + {0xff5bc685, 100, 0, 0}, + {0xff6621ce, 100, 0, 0}, + {0xff743e38, 0, 1, 0}, + {0xffbef374, 100, 0, 0}, + {0xffd9db04, 147, 0, 0}, + {0xffecf645, 46, 1, 790}, + {0xffef86c8, 126, 0, 0} +}; + +static nes_file_t type; + +static const st_getopt2_t ines_usage[] = + { + {NULL, 0, 0, 0, NULL, "iNES header", NULL}, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + +static const st_getopt2_t unif_usage[] = + { + {NULL, 0, 0, 0, NULL, "UNIF header", NULL}, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + +static const st_getopt2_t pasofami_usage[] = + { + {NULL, 0, 0, 0, NULL, "Pasofami file", NULL}, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + +static const st_getopt2_t fds_usage[] = + { + {NULL, 0, 0, 0, NULL, "Famicom Disk System file (diskimage)", NULL}, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + +static st_ines_header_t ines_header; +static st_unif_header_t unif_header; +static st_smc_header_t ffe_header; + +#if UNIF_REVISION > 7 +static const char unif_ucon64_sig[] = + "uCON64" WRTR_MARKER_S UCON64_VERSION_S WRTR_MARKER_S CURRENT_OS_S; +#else +static const char unif_ucon64_sig[] = STD_COMMENT UCON64_VERSION_S " " CURRENT_OS_S; +#endif +static const int unif_prg_ids[] = {PRG0_ID, PRG1_ID, PRG2_ID, PRG3_ID, + PRG4_ID, PRG5_ID, PRG6_ID, PRG7_ID, + PRG8_ID, PRG9_ID, PRGA_ID, PRGB_ID, + PRGC_ID, PRGD_ID, PRGE_ID, PRGF_ID}; +static const int unif_pck_ids[] = {PCK0_ID, PCK1_ID, PCK2_ID, PCK3_ID, + PCK4_ID, PCK5_ID, PCK6_ID, PCK7_ID, + PCK8_ID, PCK9_ID, PCKA_ID, PCKB_ID, + PCKC_ID, PCKD_ID, PCKE_ID, PCKF_ID}; +static const int unif_chr_ids[] = {CHR0_ID, CHR1_ID, CHR2_ID, CHR3_ID, + CHR4_ID, CHR5_ID, CHR6_ID, CHR7_ID, + CHR8_ID, CHR9_ID, CHRA_ID, CHRB_ID, + CHRC_ID, CHRD_ID, CHRE_ID, CHRF_ID}; +static const int unif_cck_ids[] = {CCK0_ID, CCK1_ID, CCK2_ID, CCK3_ID, + CCK4_ID, CCK5_ID, CCK6_ID, CCK7_ID, + CCK8_ID, CCK9_ID, CCKA_ID, CCKB_ID, + CCKC_ID, CCKD_ID, CCKE_ID, CCKF_ID}; + +static const char *nes_destfname = NULL, *internal_name; +static int rom_size; +static FILE *nes_destfile; + + +static int +nes_compare (const void *key, const void *found) +{ + /* + The return statement looks overly complicated, but is really necessary. + This construct: + return ((st_nes_data_t *) key)->crc32 - ((st_nes_data_t *) found)->crc32; + does *not* work correctly for all cases. + */ + return (int) (((int64_t) ((st_nes_data_t *) key)->crc32 - + (int64_t) ((st_nes_data_t *) found)->crc32) / 2); +} + + +static int +toprint (int c) +{ + return isprint (c) ? c : '-'; +} + + +nes_file_t +nes_get_file_type (void) +{ + return type; +} + + +static void +remove_destfile (void) +{ + if (nes_destfname) + { + printf ("Removing: %s\n", nes_destfname); + fclose (nes_destfile); // necessary under DOS/Win9x for DJGPP port + remove (nes_destfname); + nes_destfname = NULL; + } +} + + +static unsigned int +read_block (unsigned char **data, unsigned int size, FILE *file, const char *format, ...) +{ + va_list argptr; + unsigned int real_size; + + va_start (argptr, format); + if ((*data = (unsigned char *) malloc (size)) == NULL) + { + vfprintf (stderr, format, argptr); + exit (1); + } + real_size = fread (*data, 1, size, file); + if (real_size != size) + printf ("WARNING: Couldn't read %d bytes, only %d bytes were available\n", + size, real_size); + + va_end (argptr); + return real_size; +} + + +static st_unif_chunk_t * +read_chunk (unsigned long id, unsigned char *rom_buffer, int cont) +/* + The caller is responsible for freeing the memory for the allocated + st_unif_chunk_t. It should do that by calling free() with the pointer to + the st_unif_chunk_t. It should do NOTHING for the struct member `data'. +*/ +{ +// the DEBUG_READ_CHUNK blocks are left here on purpose, don't remove! +//#define DEBUG_READ_CHUNK +#ifdef DEBUG_READ_CHUNK + char id_str[5] = " "; +#endif + struct + { + unsigned int id; // chunk identification string + unsigned int length; // data length, in little endian format + } chunk_header; + st_unif_chunk_t *unif_chunk; + static int pos = 0; + + if (!cont) + pos = 0; // fseek (file, UNIF_HEADER_LEN, SEEK_SET); + +#ifdef WORDS_BIGENDIAN + id = bswap_32 (id); // swap id once instead of chunk_header.id often +#endif + do + { + // fread (&chunk_header, 1, sizeof (chunk_header), file); + memcpy (&chunk_header, rom_buffer + pos, sizeof (chunk_header)); + pos += sizeof (chunk_header); +#ifdef WORDS_BIGENDIAN + chunk_header.length = bswap_32 (chunk_header.length); +#endif +// if (feof (file)) +// break; +#ifdef DEBUG_READ_CHUNK + memcpy (id_str, &chunk_header.id, 4); +#ifdef WORDS_BIGENDIAN + *((int *) id_str) = bswap_32 (*((int *) id_str)); +#endif + printf ("chunk header: id=%s, length=%d\n", id_str, chunk_header.length); +#endif + if (chunk_header.id != id) + { + // (fseek (file, chunk_header.length, SEEK_CUR) != 0) // fseek() clears EOF indicator + if ((int) (pos + chunk_header.length) >= rom_size) + break; + else + pos += chunk_header.length; + } + } + while (chunk_header.id != id); + + if (chunk_header.id != id || pos >= rom_size) // || feof (file)) + { +#ifdef DEBUG_READ_CHUNK + printf ("exit1\n"); +#endif + return (st_unif_chunk_t *) NULL; + } + + if ((unif_chunk = (st_unif_chunk_t *) + malloc (sizeof (st_unif_chunk_t) + chunk_header.length)) == NULL) + { + fprintf (stderr, "ERROR: Not enough memory for chunk (%d bytes)\n", + (int) sizeof (st_unif_chunk_t) + chunk_header.length); + exit (1); + } + unif_chunk->id = me2le_32 (chunk_header.id); + unif_chunk->length = chunk_header.length; + unif_chunk->data = &((unsigned char *) unif_chunk)[sizeof (st_unif_chunk_t)]; + + // fread (unif_chunk->data, 1, chunk_header.length, file); + memcpy (unif_chunk->data, rom_buffer + pos, chunk_header.length); + pos += chunk_header.length; +#ifdef DEBUG_READ_CHUNK + printf ("exit2\n"); +#endif + return unif_chunk; +#undef DEBUG_READ_CHUNK +} + + +static void +write_chunk (st_unif_chunk_t *chunk, FILE *file) +{ +#ifdef WORDS_BIGENDIAN + int x; + x = bswap_32 (chunk->id); + fwrite (&x, 1, sizeof (chunk->id), file); + x = bswap_32 (chunk->length); + fwrite (&x, 1, sizeof (chunk->length), file); +#else + fwrite (&chunk->id, 1, sizeof (chunk->id), file); + fwrite (&chunk->length, 1, sizeof (chunk->length), file); +#endif + fwrite (chunk->data, 1, chunk->length, file); +} + + +static int +parse_info_file (st_dumper_info_t *info, const char *fname) +/* + Format of info file: + + + []{-,/}[]{-,/}[][]{-,/} + [] + + can be \n (Unix), \r\n (DOS) or \r (Macintosh) + + examples of valid date lines: + 22-11-1975 + 1/1/01 +*/ +{ +#define SIZE_INFO (99 + 10 + 99 + 3 * 2) // possibly 3 lines in DOS text format + int n, prev_n; + char buf[SIZE_INFO] = { 0 }, number[80]; // 4 should be enough, but don't + // be too sensitive to errors + memset (info, 0, sizeof (st_dumper_info_t)); + if (ucon64_fread (buf, 0, SIZE_INFO, fname) <= 0) + return -1; + + for (n = 0; n < 99; n++) + if (buf[n] == '\n' || buf[n] == '\r') + break; + strncpy (info->dumper_name, buf, n); + info->dumper_name[99] = 0; + + // handle newline, possibly in DOS format + prev_n = n; + for (; n < prev_n + 2; n++) + if (buf[n] != '\n' && buf[n] != '\r') + break; + + prev_n = n; + for (; n < prev_n + 2; n++) + if (buf[n] == '-' || buf[n] == '/') + break; + strncpy (number, &buf[prev_n], n - prev_n); + number[n - prev_n] = 0; + info->day = (unsigned char) strtol (number, NULL, 10); + + n++; + prev_n = n; + for (; n < prev_n + 2; n++) + if (buf[n] == '-' || buf[n] == '/') + break; + strncpy (number, &buf[prev_n], n - prev_n); + number[n - prev_n] = 0; + info->month = (unsigned char) strtol (number, NULL, 10); + + n++; + prev_n = n; + for (; n < prev_n + 4; n++) + if (buf[n] == '\n' || buf[n] == '\r') + break; + strncpy (number, &buf[prev_n], n - prev_n); + number[n - prev_n] = 0; + info->year = (unsigned short) strtol (number, NULL, 10); + + // handle newline, possibly in DOS format + prev_n = n; + for (; n < prev_n + 2; n++) + if (buf[n] != '\n' && buf[n] != '\r') + break; + + prev_n = n; + for (; n < prev_n + 99; n++) + if (buf[n] == '\n' || buf[n] == '\r') + break; + strncpy (info->dumper_agent, &buf[prev_n], n - prev_n); + info->dumper_agent[99] = 0; + + return 0; +#undef SIZE_INFO +} + + +static int +nes_ines_unif (FILE *srcfile, FILE *destfile) +{ + int prg_size, chr_size, x; + unsigned char *prg_data = NULL, *chr_data = NULL, b; + st_unif_chunk_t unif_chunk; + + // read iNES file + fread (&ines_header, 1, INES_HEADER_LEN, srcfile); + if (ines_header.ctrl1 & INES_TRAINER) + fseek (srcfile, 512, SEEK_CUR); // discard trainer data (lib_unif does the same) + + prg_size = ines_header.prg_size << 14; + prg_size = read_block (&prg_data, prg_size, srcfile, + "ERROR: Not enough memory for PRG buffer (%d bytes)\n", prg_size); + chr_size = ines_header.chr_size << 13; + if (chr_size > 0) + chr_size = read_block (&chr_data, chr_size, srcfile, + "ERROR: Not enough memory for CHR buffer (%d bytes)\n", chr_size); + + // write UNIF file + memset (&unif_header, 0, UNIF_HEADER_LEN); + memcpy (&unif_header.signature, UNIF_SIG_S, 4); + unif_header.revision = me2le_32 (UNIF_REVISION); + fwrite (&unif_header, 1, UNIF_HEADER_LEN, destfile); + + unif_chunk.id = MAPR_ID; + if (ucon64.mapr == NULL || strlen (ucon64.mapr) == 0) + { + fprintf (stderr, "ERROR: No board name specified\n"); + return -1; + } + unif_chunk.length = strlen (ucon64.mapr) + 1; // +1 to include ASCII-z + unif_chunk.data = (void *) ucon64.mapr; + if (unif_chunk.length > BOARDNAME_MAXLEN) + { // Should we give a warning? + unif_chunk.length = BOARDNAME_MAXLEN; + ((char *) unif_chunk.data)[BOARDNAME_MAXLEN - 1] = 0; + } // make it an ASCII-z string + write_chunk (&unif_chunk, destfile); + +#if UNIF_REVISION > 7 + if (ucon64.comment != NULL && strlen (ucon64.comment) > 0) + { + unif_chunk.id = READ_ID; + unif_chunk.length = strlen (ucon64.comment) + 1; // +1 to include ASCII-z + unif_chunk.data = (void *) ucon64.comment; + write_chunk (&unif_chunk, destfile); + } + + // WRTR chunk can be helpful when debugging + unif_chunk.id = WRTR_ID; + unif_chunk.length = strlen (unif_ucon64_sig) + 1; + unif_chunk.data = (char *) unif_ucon64_sig; + write_chunk (&unif_chunk, destfile); +#else + // READ chunk can be helpful when debugging + unif_chunk.id = READ_ID; + unif_chunk.length = strlen (unif_ucon64_sig) + 1; + unif_chunk.data = (char *) unif_ucon64_sig; // assume ASCII-z (spec is not clear) + write_chunk (&unif_chunk, destfile); +#endif + + if (UCON64_ISSET (ucon64.tv_standard)) + { + unif_chunk.id = TVCI_ID; + unif_chunk.length = 1; + b = ucon64.tv_standard; // necessary for big endian machines + unif_chunk.data = &b; + write_chunk (&unif_chunk, destfile); + } + + if (UCON64_ISSET (ucon64.use_dump_info)) + { + st_dumper_info_t info; + if (ucon64.dump_info != NULL && strlen (ucon64.dump_info) > 0) + { + if (access (ucon64.dump_info, F_OK) == 0) + { + parse_info_file (&info, ucon64.dump_info); +#ifdef WORDS_BIGENDIAN + info.year = bswap_16 (info.year); +#endif + unif_chunk.id = DINF_ID; + unif_chunk.length = 204; + unif_chunk.data = &info; + write_chunk (&unif_chunk, destfile); + } + else + printf ("WARNING: Dumper info file %s does not exist, chunk won't be written\n", + ucon64.dump_info); + } + else // Is this a warning or an error? + printf ("WARNING: No dumper info file was specified, chunk won't be written\n"); + } + + if (UCON64_ISSET (ucon64.controller)) + { + unif_chunk.id = CTRL_ID; + unif_chunk.length = 1; + b = ucon64.controller; // necessary for big endian machines + unif_chunk.data = &b; + write_chunk (&unif_chunk, destfile); + } + + x = crc32 (0, prg_data, prg_size); + unif_chunk.id = PCK0_ID; + unif_chunk.length = 4; +#ifdef WORDS_BIGENDIAN + x = bswap_32 (x); +#endif + unif_chunk.data = &x; + write_chunk (&unif_chunk, destfile); + + unif_chunk.id = PRG0_ID; // How to detect that x > 0 for PRGx? + unif_chunk.length = prg_size; + unif_chunk.data = prg_data; + write_chunk (&unif_chunk, destfile); + + if (chr_size > 0) + { + x = crc32 (0, chr_data, chr_size); + unif_chunk.id = CCK0_ID; + unif_chunk.length = 4; +#ifdef WORDS_BIGENDIAN + x = bswap_32 (x); +#endif + unif_chunk.data = &x; + write_chunk (&unif_chunk, destfile); + + unif_chunk.id = CHR0_ID; // How to detect that x > 0 for CHRx? + unif_chunk.length = chr_size; + unif_chunk.data = chr_data; + write_chunk (&unif_chunk, destfile); + } + + b = 0; // this is a dummy + unif_chunk.id = BATR_ID; + unif_chunk.length = 1; + unif_chunk.data = &b; + if (UCON64_ISSET (ucon64.battery)) + { + if (ucon64.battery) + write_chunk (&unif_chunk, destfile); + } + else if (ines_header.ctrl1 & INES_SRAM) + write_chunk (&unif_chunk, destfile); + + if (UCON64_ISSET (ucon64.vram)) + { + if (ucon64.vram) + { + b = 0; // this is a dummy + unif_chunk.id = VROR_ID; + unif_chunk.length = 1; + unif_chunk.data = &b; + write_chunk (&unif_chunk, destfile); + } + } + + unif_chunk.id = MIRR_ID; + unif_chunk.length = 1; + if (UCON64_ISSET (ucon64.mirror)) + { + if (ucon64.mirror > 5) + { + ucon64.mirror = 5; + printf ("WARNING: Invalid mirroring type specified, using \"%d\"\n", + ucon64.mirror); + } + b = ucon64.mirror; // necessary for big endian machines + unif_chunk.data = &b; + write_chunk (&unif_chunk, destfile); + } + else if (ines_header.ctrl1 & (INES_MIRROR | INES_4SCREEN)) + { + if (ines_header.ctrl1 & INES_MIRROR) + b = 1; + else // it must be INES_4SCREEN + b = 4; + unif_chunk.data = &b; + write_chunk (&unif_chunk, destfile); + } + + free (prg_data); + free (chr_data); // free() accepts case "chr_data == NULL" + return 0; +} + + +static int +nes_unif_unif (unsigned char *rom_buffer, FILE *destfile) +{ + int x, n; + st_unif_chunk_t *unif_chunk1, unif_chunk2, *unif_chunk3; + unsigned char b; + + x = me2le_32 (unif_header.revision); + if (x > UNIF_REVISION) + printf ("WARNING: The UNIF file is of a revision later than %d (%d), but uCON64\n" + " doesn't support that revision yet. Some chunks may be discarded.\n", + UNIF_REVISION, x); + unif_header.revision = me2le_32 (UNIF_REVISION); + memcpy (&unif_header.signature, UNIF_SIG_S, 4); + fwrite (&unif_header, 1, UNIF_HEADER_LEN, destfile); + + if ((unif_chunk1 = read_chunk (MAPR_ID, rom_buffer, 0)) == NULL || // no MAPR chunk + (ucon64.mapr != NULL && strlen (ucon64.mapr) > 0)) // MAPR, but has to change + { + unif_chunk2.id = MAPR_ID; + if (ucon64.mapr == NULL || strlen (ucon64.mapr) == 0) // unif_chunk1 == NULL + { + fprintf (stderr, "ERROR: File has no MAPR chunk, but no board name was specified\n"); + return -1; + } + + // ucon64.mapr != NULL && strlen (ucon64.mapr) > 0 + unif_chunk2.length = strlen (ucon64.mapr) + 1; // +1 to include ASCII-z + unif_chunk2.data = (void *) ucon64.mapr; + if (unif_chunk2.length > BOARDNAME_MAXLEN) + { + unif_chunk2.length = BOARDNAME_MAXLEN; + ((char *) unif_chunk2.data)[BOARDNAME_MAXLEN - 1] = 0; + } // make it an ASCII-z string + write_chunk (&unif_chunk2, destfile); + } + else // MAPR chunk, but no board name specified + { + printf ("WARNING: No board name specified, using old value\n"); + write_chunk (unif_chunk1, destfile); + } + free (unif_chunk1); // case NULL is valid + +#if UNIF_REVISION > 7 + if (ucon64.comment != NULL && strlen (ucon64.comment) > 0) + { + unif_chunk2.id = READ_ID; + unif_chunk2.length = strlen (ucon64.comment) + 1; // +1 to include ASCII-z + unif_chunk2.data = (void *) ucon64.comment; + write_chunk (&unif_chunk2, destfile); + } + else + { + if ((unif_chunk1 = read_chunk (READ_ID, rom_buffer, 0)) != NULL) + write_chunk (unif_chunk1, destfile); + free (unif_chunk1); + } + + if ((unif_chunk1 = read_chunk (WRTR_ID, rom_buffer, 0)) == NULL) + { + unif_chunk2.id = WRTR_ID; + unif_chunk2.length = strlen (unif_ucon64_sig) + 1; + unif_chunk2.data = (char *) unif_ucon64_sig; + write_chunk (&unif_chunk2, destfile); + } + else + { + char ucon64_name[] = "uCON64"; + int sig_added = 0; + // find uCON64 WRTR chunk and modify it if it is present + do + { + if (!strncmp ((const char *) unif_chunk1->data, ucon64_name, strlen (ucon64_name))) + { + unif_chunk1->length = strlen (unif_ucon64_sig) + 1; + unif_chunk1->data = (char *) unif_ucon64_sig; + sig_added = 1; // don't break, because we want to preserve other WRTR chunks + } + write_chunk (unif_chunk1, destfile); + free (unif_chunk1); + } + while ((unif_chunk1 = read_chunk (WRTR_ID, rom_buffer, 1)) != NULL); + + if (!sig_added) + { + unif_chunk2.id = WRTR_ID; + unif_chunk2.length = strlen (unif_ucon64_sig) + 1; + unif_chunk2.data = (char *) unif_ucon64_sig; + write_chunk (&unif_chunk2, destfile); + } + } +#else + if ((unif_chunk1 = read_chunk (READ_ID, rom_buffer, 0)) == NULL) + { + unif_chunk2.id = READ_ID; + unif_chunk2.length = strlen (unif_ucon64_sig) + 1; + unif_chunk2.data = (char *) unif_ucon64_sig; + write_chunk (&unif_chunk2, destfile); // assume ASCII-z (spec is not clear) + } + else + { + if (!strncmp (unif_chunk1->data, STD_COMMENT, strlen (STD_COMMENT))) + { // overwrite uCON64 comment -> OS and version match with the used exe + unif_chunk1->length = strlen (unif_ucon64_sig) + 1; + unif_chunk1->data = (char *) unif_ucon64_sig; + } + write_chunk (unif_chunk1, destfile); + } + free (unif_chunk1); +#endif + + if (internal_name != NULL) + { + unif_chunk2.id = NAME_ID; + unif_chunk2.length = strlen (internal_name) + 1; + unif_chunk2.data = (char *) internal_name; + write_chunk (&unif_chunk2, destfile); // assume ASCII-z (spec is not clear) + } + else + { + if ((unif_chunk1 = read_chunk (NAME_ID, rom_buffer, 0)) != NULL) + write_chunk (unif_chunk1, destfile); + free (unif_chunk1); + } + + if (UCON64_ISSET (ucon64.tv_standard)) + { + unif_chunk2.id = TVCI_ID; + unif_chunk2.length = 1; + b = ucon64.tv_standard; // necessary for big endian machines + unif_chunk2.data = &b; + write_chunk (&unif_chunk2, destfile); + } + else + { + if ((unif_chunk1 = read_chunk (TVCI_ID, rom_buffer, 0)) != NULL) + write_chunk (unif_chunk1, destfile); + free (unif_chunk1); + } + + if (UCON64_ISSET (ucon64.use_dump_info)) + { + st_dumper_info_t info; + if (ucon64.dump_info != NULL && strlen (ucon64.dump_info) > 0) + { + if (access (ucon64.dump_info, F_OK) == 0) + { + parse_info_file (&info, ucon64.dump_info); +/* + printf ("Dump info:\n" + " Dumper: %s\n" + " Date: %d-%d-%02d\n" + " Agent: %s\n", + info.dumper_name, + info.day, info.month, info.year, + info.dumper_agent); +*/ +#ifdef WORDS_BIGENDIAN + info.year = bswap_16 (info.year); +#endif + unif_chunk2.id = DINF_ID; + unif_chunk2.length = 204; + unif_chunk2.data = &info; + write_chunk (&unif_chunk2, destfile); + } + else + printf ("WARNING: Dumper info file %s does not exist, chunk won't be written\n", + ucon64.dump_info); + } + else // Is this a warning or an error? + printf ("WARNING: No dumper info file was specified, chunk won't be written\n"); + } + else + { + if ((unif_chunk1 = read_chunk (DINF_ID, rom_buffer, 0)) != NULL) + write_chunk (unif_chunk1, destfile); + free (unif_chunk1); + } + + if (UCON64_ISSET (ucon64.controller)) + { + unif_chunk2.id = CTRL_ID; + unif_chunk2.length = 1; + b = ucon64.controller; // necessary for big endian machines + unif_chunk2.data = &b; + write_chunk (&unif_chunk2, destfile); + } + else + { + if ((unif_chunk1 = read_chunk (CTRL_ID, rom_buffer, 0)) != NULL) + write_chunk (unif_chunk1, destfile); + free (unif_chunk1); + } + + // copy PRG chunks + for (n = 0; n < 16; n++) + { + if ((unif_chunk1 = read_chunk (unif_prg_ids[n], rom_buffer, 0)) != NULL) + { + if ((unif_chunk3 = read_chunk (unif_pck_ids[n], rom_buffer, 0)) == NULL) + { + x = crc32 (0, (unsigned char *) unif_chunk1->data, unif_chunk1->length); + unif_chunk2.id = unif_pck_ids[n]; + unif_chunk2.length = 4; +#ifdef WORDS_BIGENDIAN + x = bswap_32 (x); +#endif + unif_chunk2.data = &x; + write_chunk (&unif_chunk2, destfile); + } + else + { + x = crc32 (0, (unsigned char *) unif_chunk1->data, unif_chunk1->length); +#ifdef WORDS_BIGENDIAN + x = bswap_32 (x); +#endif + if (x != *((int *) unif_chunk3->data)) + printf ("WARNING: PRG chunk %d has a bad checksum, writing new checksum\n", n); + unif_chunk3->length = 4; + unif_chunk3->data = &x; + write_chunk (unif_chunk3, destfile); + } + free (unif_chunk3); + write_chunk (unif_chunk1, destfile); + } + free (unif_chunk1); + } + + // copy CHR chunks + for (n = 0; n < 16; n++) + { + if ((unif_chunk1 = read_chunk (unif_chr_ids[n], rom_buffer, 0)) != NULL) + { + if ((unif_chunk3 = read_chunk (unif_cck_ids[n], rom_buffer, 0)) == NULL) + { + x = crc32 (0, (unsigned char *) unif_chunk1->data, unif_chunk1->length); + unif_chunk2.id = unif_cck_ids[n]; + unif_chunk2.length = 4; +#ifdef WORDS_BIGENDIAN + x = bswap_32 (x); +#endif + unif_chunk2.data = &x; + write_chunk (&unif_chunk2, destfile); + } + else + { + x = crc32 (0, (unsigned char *) unif_chunk1->data, unif_chunk1->length); +#ifdef WORDS_BIGENDIAN + x = bswap_32 (x); +#endif + if (x != *((int *) unif_chunk3->data)) + printf ("WARNING: CHR chunk %d has a bad checksum, writing new checksum\n", n); + unif_chunk3->length = 4; + unif_chunk3->data = &x; + write_chunk (unif_chunk3, destfile); + } + free (unif_chunk3); + write_chunk (unif_chunk1, destfile); + } + free (unif_chunk1); + } + + if (UCON64_ISSET (ucon64.battery)) + { + if (ucon64.battery) + { + b = 0; // this is a dummy + unif_chunk2.id = BATR_ID; + unif_chunk2.length = 1; + unif_chunk2.data = &b; + write_chunk (&unif_chunk2, destfile); + } + } + else + { + if ((unif_chunk1 = read_chunk (BATR_ID, rom_buffer, 0)) != NULL) + write_chunk (unif_chunk1, destfile); + free (unif_chunk1); + } + + if (UCON64_ISSET (ucon64.vram)) + { + if (ucon64.vram) + { + b = 0; // this is a dummy + unif_chunk2.id = VROR_ID; + unif_chunk2.length = 1; + unif_chunk2.data = &b; + write_chunk (&unif_chunk2, destfile); + } + } + else + { + if ((unif_chunk1 = read_chunk (VROR_ID, rom_buffer, 0)) != NULL) + write_chunk (unif_chunk1, destfile); + free (unif_chunk1); + } + + if (UCON64_ISSET (ucon64.mirror)) + { + if (ucon64.mirror > 5) + { + ucon64.mirror = 5; + printf ("WARNING: Invalid mirroring type specified, using \"%d\"\n", + ucon64.mirror); + } + unif_chunk2.id = MIRR_ID; + unif_chunk2.length = 1; + b = ucon64.mirror; // necessary for big endian machines + unif_chunk2.data = &b; + write_chunk (&unif_chunk2, destfile); + } + else + { + if ((unif_chunk1 = read_chunk (MIRR_ID, rom_buffer, 0)) != NULL) + write_chunk (unif_chunk1, destfile); + free (unif_chunk1); + } + + return 0; +} + + +int +nes_unif (void) +{ + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + unsigned char *rom_buffer; + FILE *srcfile, *destfile; + + if (type != INES && type != UNIF) + { + if (type == PASOFAMI) + fprintf (stderr, "ERROR: Pasofami -> UNIF is currently not supported\n"); + else if (type == FFE) + fprintf (stderr, "ERROR: FFE -> UNIF is currently not supported\n"); + else if (type == FDS || type == FAM) + fprintf (stderr, "ERROR: FDS/FAM -> UNIF is currently not supported\n"); + return -1; + } + + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".unf"); + strcpy (src_name, ucon64.rom); + ucon64_file_handler (dest_name, src_name, 0); + if ((destfile = fopen (dest_name, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], dest_name); + return -1; + } + + /* + remove possible temp file created by ucon64_file_handler () + nes_ines_unif() and nes_unif_unif() might exit() so we use register_func() + */ + register_func (remove_temp_file); + nes_destfname = dest_name; + nes_destfile = destfile; + register_func (remove_destfile); + /* + Converting from UNIF to UNIF should be allowed, because the user might want + to change some paramaters. + */ + if (type == INES) + { + if ((srcfile = fopen (src_name, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], src_name); + return -1; + } + + if (nes_ines_unif (srcfile, destfile) == -1) // -1 == error + exit (1); + + fclose (srcfile); + } + else if (type == UNIF) + { + if ((rom_buffer = (unsigned char *) malloc (rom_size)) == NULL) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], rom_size); + exit (1); + } + ucon64_fread (rom_buffer, UNIF_HEADER_LEN, rom_size, src_name); + + if (nes_unif_unif (rom_buffer, destfile) == -1) // -1 == error + exit (1); + + free (rom_buffer); + } + + unregister_func (remove_destfile); + fclose (destfile); + unregister_func (remove_temp_file); + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return 0; +} + + +static void +set_mapper (st_ines_header_t *header, unsigned int mapper) +{ + header->ctrl1 &= 0x0f; // clear mapper bits + header->ctrl2 &= 0x03; // clear reserved and mapper bits + + header->ctrl1 |= mapper << 4; + header->ctrl2 |= mapper & 0xf0; + if (mapper > 0xff) // we support mapper numbers > 255 + { + if (mapper > 0xfff) + { + fprintf (stderr, "ERROR: Mapper numbers greater than 4095 can't be stored\n"); + exit (1); + } + // We can't just clear bits 0 & 1 of ctrl2, because they have their own + // meaning. So, a warning is in place here. + printf ("WARNING: Mapper number is greater than 255\n"); + header->ctrl2 |= (mapper >> 8) & 0xf; + } +} + + +static int +nes_ines_ines (FILE *srcfile, FILE *destfile, int deinterleave) +{ + int prg_size, chr_size; + unsigned char *prg_data = NULL, *chr_data = NULL; + + // read iNES file + fread (&ines_header, 1, INES_HEADER_LEN, srcfile); + if (ines_header.ctrl1 & INES_TRAINER) + { + fseek (srcfile, 512, SEEK_CUR); // discard trainer data + ines_header.ctrl1 &= ~INES_TRAINER; // clear trainer bit + } + + prg_size = ines_header.prg_size << 14; + prg_size = read_block (&prg_data, prg_size, srcfile, + "ERROR: Not enough memory for PRG buffer (%d bytes)\n", prg_size); + chr_size = ines_header.chr_size << 13; + if (chr_size > 0) + chr_size = read_block (&chr_data, chr_size, srcfile, + "ERROR: Not enough memory for CHR buffer (%d bytes)\n", chr_size); + + if (deinterleave) + /* + This is a bit of a hack, i.e., putting the following code in this + function. AFAIK (dbjh) interleaved images contain one big block of data: + ROM data at even addresses (offsets in the file) and VROM data at odd + addresses. Currently uCON64 supports deinterleaving of iNES files only, + so we have to handle the improbable case that the source ROM file + contains two blocks of interleaved ROM/VROM data. + Due to the way the data is stored the ROM data will have the same size + as the VROM data. + */ + { + unsigned char *data; + int size, n = 0, prg = 0, chr = 0; + + size = prg_size + chr_size; + if ((data = (unsigned char *) malloc (size)) == NULL) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], size); + exit (1); + } + memcpy (data, prg_data, prg_size); + memcpy (data + prg_size, chr_data, chr_size); + + if (prg_size != chr_size) + { + free (prg_data); + free (chr_data); + prg_size = chr_size = size / 2; + prg_data = (unsigned char *) malloc (prg_size); + chr_data = (unsigned char *) malloc (chr_size); + if (prg_data == NULL || chr_data == NULL) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], size); + exit (1); + } + } + + while (n < size) + { // deinterleave + prg_data[prg++] = data[n++]; + chr_data[chr++] = data[n++]; + } + free (data); + } + + // write iNES file + if (ucon64.mapr == NULL || strlen (ucon64.mapr) == 0) + printf ("WARNING: No mapper number specified, using old value\n"); + else // mapper specified + set_mapper (&ines_header, strtol (ucon64.mapr, NULL, 10)); + memcpy (&ines_header.signature, INES_SIG_S, 4); + ines_header.prg_size = prg_size >> 14; + ines_header.chr_size = chr_size >> 13; + + if (UCON64_ISSET (ucon64.battery)) + { + if (ucon64.battery) + ines_header.ctrl1 |= INES_SRAM; + else + ines_header.ctrl1 &= ~INES_SRAM; + } + + if (UCON64_ISSET (ucon64.mirror)) + { + ines_header.ctrl1 &= ~(INES_MIRROR | INES_4SCREEN); // clear bits + if (ucon64.mirror == 0) + ; // default value in ctrl1 (0) is ok + else if (ucon64.mirror == 1) + ines_header.ctrl1 |= INES_MIRROR; + else if (ucon64.mirror == 4) + ines_header.ctrl1 |= INES_4SCREEN; + else + printf ("WARNING: Invalid mirroring type specified, using \"0\"\n"); + } + + ines_header.reserved1 = 0; + ines_header.reserved2 = 0; + fwrite (&ines_header, 1, INES_HEADER_LEN, destfile); + fwrite (prg_data, 1, prg_size, destfile); + fwrite (chr_data, 1, chr_size, destfile); + + free (prg_data); + free (chr_data); + return 0; +} + + +static int +nes_mapper_number (const char *board_name) +{ + typedef struct + { + const char *string; + int value; + } st_string_value_t; + + int n; + st_string_value_t name_to_mapr[] = // TODO: expand this list + { + { "NROM", 0 }, + { "NES-RROM", 0 }, + { "SNROM", 1 }, + { "SOROM", 1 }, + { "SVROM", 1 }, + { "SUROM", 1 }, + { "SAROM", 1 }, + { "SBROM", 1 }, + { "UNROM", 2 }, + { "CNROM", 3 }, + { "TEROM", 4 }, + { "TFROM", 4 }, + { "TGROM", 4 }, + { "TVROM", 4 }, + { "TSROM", 4 }, + { "TQROM", 4 }, + { "TKROM", 4 }, + { "TLSROM", 4 }, + { "DRROM", 4 }, + { "TLROM", 4 }, + { "SL1ROM", 4 }, + { "SL2ROM", 4 }, + { "SL3ROM", 4 }, + { "ELROM", 5 }, + { "ETROM", 5 }, + { "EWROM", 5 }, + { "AOROM", 7 }, + { "PNROM", 9 }, + { NULL, 0 } + }; + + n = 0; + while (name_to_mapr[n].string != NULL) + { + if (!strncmp (board_name, name_to_mapr[n].string, BOARDNAME_MAXLEN - 1)) + return name_to_mapr[n].value; + n++; + } + + return -1; +} + + +static int +nes_unif_ines (unsigned char *rom_buffer, FILE *destfile) +{ + int n, x, prg_size = 0, chr_size = 0; + st_unif_chunk_t *unif_chunk; + + x = me2le_32 (unif_header.revision); + if (x > UNIF_REVISION) + printf ("WARNING: The UNIF file is of a revision later than %d (%d), but uCON64\n" + " doesn't support that revision yet. Some chunks may be discarded.\n", + UNIF_REVISION, x); + + // build iNES header + memset (&ines_header, 0, INES_HEADER_LEN); + memcpy (&ines_header.signature, INES_SIG_S, 4); + + if (ucon64.mapr == NULL || strlen (ucon64.mapr) == 0) + { // no mapper specified, try autodetection + if ((unif_chunk = read_chunk (MAPR_ID, rom_buffer, 0)) != NULL) + { + if ((x = nes_mapper_number ((const char *) unif_chunk->data)) == -1) + { + printf ("WARNING: Couldn't determine mapper number, writing \"0\"\n"); + x = 0; + } + set_mapper (&ines_header, x); + free (unif_chunk); + } + else // no MAPR chunk + { + fprintf (stderr, "ERROR: File has no MAPR chunk, but no mapper number was specified\n"); + return -1; + } + } + else // mapper specified + set_mapper (&ines_header, strtol (ucon64.mapr, NULL, 10)); + + if (UCON64_ISSET (ucon64.battery)) + { + if (ucon64.battery) + ines_header.ctrl1 |= INES_SRAM; + else + ines_header.ctrl1 &= ~INES_SRAM; + } + else + { + if ((unif_chunk = read_chunk (BATR_ID, rom_buffer, 0)) != NULL) + ines_header.ctrl1 |= INES_SRAM; + free (unif_chunk); + } + + if (UCON64_ISSET (ucon64.mirror)) + { + if (ucon64.mirror == 0) + ; // default value in ctrl1 (0) is ok + else if (ucon64.mirror == 1) + ines_header.ctrl1 |= INES_MIRROR; + else if (ucon64.mirror == 4) + ines_header.ctrl1 |= INES_4SCREEN; + else + printf ("WARNING: Invalid mirroring type specified, using \"0\"\n"); + } + else if ((unif_chunk = read_chunk (MIRR_ID, rom_buffer, 0)) != NULL) + { + switch (*((unsigned char *) unif_chunk->data)) + { + case 0: // default value in ctrl1 (0) is ok + case 2: // can't express in iNES terms + case 3: // idem + case 5: // idem + break; + case 1: + ines_header.ctrl1 |= INES_MIRROR; + break; + case 4: + ines_header.ctrl1 |= INES_4SCREEN; + break; + default: + printf ("WARNING: Unsupported value in MIRR chunk\n"); + break; + } + free (unif_chunk); + } + + /* + Determining the PRG & CHR sizes could be done in the copy loops. Doing it + here is quite inefficient, but now we can write to a zlib stream. zlib + doesn't support backward seeks in a compressed output stream. A backward + seek would be necessary if the size calculation would be done in the copy + loops. + */ + for (n = 0; n < 16; n++) + { + if ((unif_chunk = read_chunk (unif_prg_ids[n], rom_buffer, 0)) != NULL) + prg_size += unif_chunk->length; + free (unif_chunk); + if ((unif_chunk = read_chunk (unif_chr_ids[n], rom_buffer, 0)) != NULL) + chr_size += unif_chunk->length; + free (unif_chunk); + } + + // write header + ines_header.prg_size = prg_size >> 14; // # 16 kB banks + ines_header.chr_size = chr_size >> 13; // # 8 kB banks + fwrite (&ines_header, 1, INES_HEADER_LEN, destfile); + + // copy PRG data + for (n = 0; n < 16; n++) + { + if ((unif_chunk = read_chunk (unif_prg_ids[n], rom_buffer, 0)) != NULL) + fwrite (unif_chunk->data, 1, unif_chunk->length, destfile); + free (unif_chunk); + } + + // copy CHR data + for (n = 0; n < 16; n++) + { + if ((unif_chunk = read_chunk (unif_chr_ids[n], rom_buffer, 0)) != NULL) + fwrite (unif_chunk->data, 1, unif_chunk->length, destfile); + free (unif_chunk); + } + + return 0; +} + + +int +nes_ines (void) +{ + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + unsigned char *rom_buffer; + FILE *srcfile, *destfile; + + if (type == FFE) + { + fprintf (stderr, "ERROR: FFE -> iNES is currently not supported\n"); + return -1; + } + else if (type == FDS || type == FAM) + { + fprintf (stderr, "ERROR: FDS/FAM -> iNES is not possible\n"); + return -1; + } + + // Pasofami doesn't fit well in the source -> destination "paradigm" + if (type == PASOFAMI) + return nes_j (NULL); + + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".nes"); + strcpy (src_name, ucon64.rom); + ucon64_file_handler (dest_name, src_name, 0); + if ((destfile = fopen (dest_name, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], dest_name); + return -1; + } + + register_func (remove_temp_file); + nes_destfname = dest_name; + nes_destfile = destfile; + register_func (remove_destfile); + if (type == INES) + { + if ((srcfile = fopen (src_name, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], src_name); + return -1; + } + + if (nes_ines_ines (srcfile, destfile, 0) == -1) // -1 == error + exit (1); // calls remove_temp_file() & remove_destfile() + + fclose (srcfile); + } + else if (type == UNIF) + { + if ((rom_buffer = (unsigned char *) malloc (rom_size)) == NULL) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], rom_size); + exit (1); + } + ucon64_fread (rom_buffer, UNIF_HEADER_LEN, rom_size, src_name); + + if (nes_unif_ines (rom_buffer, destfile) == -1) // -1 == error + exit (1); // calls remove_temp_file() & remove_destfile() + + free (rom_buffer); + } + + unregister_func (remove_destfile); + fclose (destfile); + unregister_func (remove_temp_file); + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return 0; +} + + +int +nes_pasofami (void) +{ + // nes_s() does iNES -> Pasofami. nes_s() checks for type + return nes_s (); +} + + +int +nes_ffe (st_rominfo_t *rominfo) +{ + st_smc_header_t smc_header; + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + int size = ucon64.file_size - rominfo->buheader_len, mapper, + prg_size, chr_size, new_prg_size = -1; + + if (type != INES) + { + fprintf (stderr, "ERROR: Currently only iNES -> FFE is supported\n"); + return -1; + } + + ucon64_fread (&ines_header, 0, INES_HEADER_LEN, ucon64.rom); + + mapper = ines_header.ctrl1 >> 4 | (ines_header.ctrl2 & 0xf0); + prg_size = ines_header.prg_size << 14; + if (prg_size > size) + prg_size = size; + chr_size = ines_header.chr_size << 13; + + memset (&smc_header, 0, SMC_HEADER_LEN); + + switch (mapper) + { + case 0: + if ((prg_size == 32 * 1024 || prg_size == 16 * 1024) && chr_size == 8 * 1024) + { + if (prg_size == 16 * 1024) + new_prg_size = 32 * 1024; + smc_header.emulation1 = 0x80; + smc_header.emulation2 = 0xea; + } + else if (prg_size == 32 * 1024 && chr_size == 16 * 1024) + { + smc_header.emulation1 = 0x80; + smc_header.emulation2 = 0xce; + } + break; + case 2: + case 71: + /* + Some mapper 71 games need to be patched before they work: + [0x0e] <= 0x40 + [0x12] <= 0x50 + */ + if (prg_size == 128 * 1024 && chr_size == 0) + { + smc_header.emulation1 = 0x80; + smc_header.emulation2 = 0; + } + else if (prg_size == 256 * 1024 && chr_size == 0) + { + smc_header.emulation1 = 0x80; + smc_header.emulation2 = 0x40; + } + break; + case 3: + if (prg_size == 32 * 1024 && chr_size == 32 * 1024) + { + smc_header.emulation1 = 0x80; + smc_header.emulation2 = 0xb7; + } + break; +#if 0 + case ?: + if (prg_size == 128 * 1024 && chr_size == 32 * 1024) + { + smc_header.emulation1 = 0x80; + smc_header.emulation2 = 0x80; + } + break; +#endif + case 6: // falling through + case 8: // falling through + case 12: + /* + According to kyuusaku the following headers only apply to "trained" + dumps. "Trainers tell the copier how to play the game and how many rom + banks there are and where they are on a game by game basis." + However, I (dbjh) subtracted 0x40 from emulation1 and moved the code + that sets bit SMC_TRAINER (0x40) to general code. + Note that we discard trainer data when converting to iNES and UNIF. + */ + if (size == 1 * MBIT) + { + smc_header.emulation1 = 0x80; + smc_header.emulation2 = 0; + } + else if (size == 2 * MBIT) + { + smc_header.emulation1 = 0x80; + smc_header.emulation2 = 0x20; + } + else if (size == 3 * MBIT) + { + smc_header.emulation1 = 0xa0; + smc_header.emulation2 = 0x20; + } + else if (size == 4 * MBIT) + { + smc_header.emulation1 = 0xb0; + smc_header.emulation2 = 0x20; + } + break; + } + + if (ines_header.ctrl1 & INES_TRAINER) + smc_header.emulation1 |= SMC_TRAINER; + + smc_header.id1 = 0xaa; + smc_header.id2 = 0xbb; +#if 0 // already set to 0 by memset() + smc_header.type = 0; +#endif + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".ffe"); + ucon64_file_handler (dest_name, src_name, 0); + + if (new_prg_size == -1) // don't resize PRG block + { + ucon64_fwrite (&smc_header, 0, SMC_HEADER_LEN, dest_name, "wb"); + fcopy (src_name, rominfo->buheader_len, size, dest_name, "ab"); + } + else + { + unsigned char *prg_data = (unsigned char *) malloc (new_prg_size); + int offset; + + if (prg_data == NULL) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], new_prg_size); + exit (1); + } + memset (prg_data, 0, new_prg_size); // pad with zeroes + ucon64_fread (prg_data, rominfo->buheader_len, prg_size, src_name); + + // write header + ucon64_fwrite (&smc_header, 0, SMC_HEADER_LEN, dest_name, "wb"); + + // copy trainer data if present + if (ines_header.ctrl1 & INES_TRAINER) + { + fcopy (src_name, rominfo->buheader_len, 512, dest_name, "ab"); + offset = 512; + } + else + offset = 0; + + // write resized PRG block + ucon64_fwrite (prg_data, SMC_HEADER_LEN + offset, new_prg_size, dest_name, "ab"); + + // copy CHR block + fcopy (src_name, rominfo->buheader_len + offset + prg_size, + size - offset - prg_size, dest_name, "ab"); + + free (prg_data); + } + + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return 0; +} + + +int +nes_ineshd (st_rominfo_t *rominfo) +{ + char dest_name[FILENAME_MAX]; + + if (type != INES) + { + fprintf (stderr, "ERROR: This option is only meaningful for iNES files\n"); + return -1; + } + + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".hdr"); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, rominfo->buheader_start, 16, dest_name, "wb"); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +nes_dint (void) +{ + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + FILE *srcfile, *destfile; + + if (type != INES) + { + // Do interleaved UNIF or Pasofami images exist? + fprintf (stderr, "ERROR: Currently only iNES images can be deinterleaved\n"); + return -1; + } + + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".nes"); + strcpy (src_name, ucon64.rom); + ucon64_file_handler (dest_name, src_name, 0); + if ((srcfile = fopen (src_name, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], src_name); + return -1; + } + if ((destfile = fopen (dest_name, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], dest_name); + return -1; + } + + register_func (remove_temp_file); + nes_destfname = dest_name; + nes_destfile = destfile; + register_func (remove_destfile); + // type == INES + if (nes_ines_ines (srcfile, destfile, 1) == -1) // -1 == error + exit (1); // calls remove_temp_file() & remove_destfile() + + unregister_func (remove_destfile); + fclose (srcfile); + fclose (destfile); + unregister_func (remove_temp_file); + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return 0; +} + + +static int +parse_prm (st_ines_header_t *header, const char *fname) +/* + Parse a .PRM file. The format has probably been designed be some mentally + impaired person. I am not mentally impaired, so I just read the information + that I can use :-) + The meaning of the bytes comes from Marat Fayzullin's NESLIST. +*/ +{ + unsigned char prm[71]; // .PRM files are 71 bytes in size + + ucon64_fread (prm, 0, 71, fname); + + if (prm[0] == 'V') // mirroring + header->ctrl1 |= INES_MIRROR; + else if (prm[0] == 'H') + header->ctrl1 &= ~INES_MIRROR; + + if (prm[1] == 'T') // ROM mapper type (2/4/not specified) + set_mapper (header, 2); + else if (prm[1] == 'N') + set_mapper (header, 4); + else + set_mapper (header, 0); + + // ignore VROM mapper type ('T' == mapper 2 or 4, nice design) + // ignore music mode + // ignore "something related to graphics" + // ignore "unknown" + // ignore "validity?" + // ignore "IRQ control?" + // ignore "something related to graphics" + // ignore "display validity?" + // ignore speed (NMI) control (always 'S') + // ignore default sprite size (always 'L') + // ignore default foreground/background (always 'R') + // ignore "break order?" + + if (prm[14] == 'E') // preserve extension RAM + header->ctrl1 |= INES_SRAM; + else + header->ctrl1 &= ~INES_SRAM; + + // ignore "unknown" + // ignore "something related to interrupts" (always 'S') + + if (prm[17] != 'M') // bank-switched ROM? + set_mapper (header, 0); + + // ignore 9 unknown bytes + // ignore "partial horizontal scroll?" (always 'X') + // ignore "don't scroll up to this scanline?" (always "02") + // ignore "line to do a scroll in?" (always '2') + // ignore "comment?" (always 'A') + + return 0; +} + + +int +nes_j (unsigned char **mem_image) +/* + The Pasofami format consists of several files: + - .PRM: header (uCON64 treats it as optional in order to support RAW images) + - .700: trainer data (optional) + - .PRG: ROM data + - .CHR: VROM data (optional) +*/ +{ + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + unsigned char *buffer; + int prg_size = 0, chr_size = 0, write_file = 0, size, bytes_read = 0, nparts = 0; + + if (type != PASOFAMI) + { + fprintf (stderr, "ERROR: Only Pasofami files can be joined (for NES)\n"); + return -1; + } + + if (mem_image == NULL) + write_file = 1; + + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".nes"); + if (write_file) + ucon64_file_handler (dest_name, NULL, 0); + + // build iNES header + memset (&ines_header, 0, INES_HEADER_LEN); + memcpy (&ines_header.signature, INES_SIG_S, 4); + + strcpy (src_name, ucon64.rom); + set_suffix (src_name, ".prm"); + if (access (src_name, F_OK) == 0) + { + parse_prm (&ines_header, src_name); + nparts++; + } + else if (write_file) // Don't print this from nes_init() + printf ("WARNING: No %s, using default values\n", src_name); + + // Don't do this in parse_prm(), because there might be no .PRM file available + if (UCON64_ISSET (ucon64.battery)) + { + if (ucon64.battery) + ines_header.ctrl1 |= INES_SRAM; + else + ines_header.ctrl1 &= ~INES_SRAM; + } + + if (UCON64_ISSET (ucon64.mirror)) + { + ines_header.ctrl1 &= ~(INES_MIRROR | INES_4SCREEN); // clear bits + if (ucon64.mirror == 0) + ; // default value in ctrl1 (0) is ok + else if (ucon64.mirror == 1) + ines_header.ctrl1 |= INES_MIRROR; + else if (ucon64.mirror == 4) + ines_header.ctrl1 |= INES_4SCREEN; + else + printf ("WARNING: Invalid mirroring type specified, using \"0\"\n"); + } + + strcpy (src_name, ucon64.rom); + set_suffix (src_name, ".700"); + if (access (src_name, F_OK) == 0 && fsizeof (src_name) >= 512) + { + ines_header.ctrl1 |= INES_TRAINER; + nparts++; + } + + set_suffix (src_name, ".prg"); + if (access (src_name, F_OK) != 0) // .PRG file must exist, but + { // not for nes_init() + if (write_file) + { + fprintf (stderr, "ERROR: No %s, can't make image without it\n", src_name); + exit (1); + } + } + else + { + prg_size = fsizeof (src_name); + nparts++; + } + ines_header.prg_size = prg_size >> 14; + + set_suffix (src_name, ".chr"); + if (access (src_name, F_OK) == 0) + { + chr_size = fsizeof (src_name); + nparts++; + } + ines_header.chr_size = chr_size >> 13; + + if (ucon64.mapr == NULL || strlen (ucon64.mapr) == 0) + { // maybe .PRM contained mapper + if (write_file) // Don't print this from nes_init() + printf ("WARNING: No mapper number specified, writing \"%d\"\n", + (ines_header.ctrl1 >> 4) | (ines_header.ctrl2 & 0xf0)); + } + else // mapper specified (override unreliable value from .PRM file) + set_mapper (&ines_header, strtol (ucon64.mapr, NULL, 10)); + + size = prg_size + chr_size + ((ines_header.ctrl1 & INES_TRAINER) ? 512 : 0); + if ((buffer = (unsigned char *) malloc (size)) == NULL) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], size); + return -1; + } + + if (ines_header.ctrl1 & INES_TRAINER) + { + set_suffix (src_name, ".700"); + ucon64_fread (buffer, 0, 512, src_name); // use 512 bytes at max + bytes_read = 512; + } + + if (prg_size > 0) + { + set_suffix (src_name, ".prg"); + ucon64_fread (buffer + bytes_read, 0, prg_size, src_name); + bytes_read += prg_size; + } + + if (chr_size > 0) + { + set_suffix (src_name, ".chr"); + ucon64_fread (buffer + bytes_read, 0, chr_size, src_name); + } + + if (write_file) + { + ucon64_fwrite (&ines_header, 0, INES_HEADER_LEN, dest_name, "wb"); + ucon64_fwrite (buffer, INES_HEADER_LEN, size, dest_name, "ab"); + printf (ucon64_msg[WROTE], dest_name); + free (buffer); + } + else + *mem_image = buffer; + + if (!UCON64_ISSET (ucon64.split)) + ucon64.split = nparts; + + return 0; +} + + +static int +write_prm (st_ines_header_t *header, const char *fname) +{ + unsigned char prm[72] = // .PRM files are 71 bytes in size (ASCII-z) + " SLR S X022A\r\n" + "1234567890123456789012345678901234\r\n\x1a"; + int mapper; + + if (UCON64_ISSET (ucon64.mirror)) + { + header->ctrl1 &= ~(INES_MIRROR | INES_4SCREEN); // clear bits + if (ucon64.mirror == 0) + ; // default value in ctrl1 (0) is ok + else if (ucon64.mirror == 1) + header->ctrl1 |= INES_MIRROR; +// Pasofami only stores horizontal or vertical mirroring types +// else if (ucon64.mirror == 4) +// header->ctrl1 |= INES_4SCREEN; + else + printf ("WARNING: Invalid mirroring type specified, using \"0\"\n"); + } + if (header->ctrl1 & INES_MIRROR) // mirroring + prm[0] = 'V'; + else + prm[0] = 'H'; + + mapper = (header->ctrl1 >> 4) | (header->ctrl2 & 0xf0); + if (mapper % 16 == 2) // mod of 16 to do the same as NESLIST, + { // but I (dbjh) think this is a bug + prm[1] = 'T'; // ROM mapper type (2/4/not specified) + prm[2] = 'T'; // VROM mapper type (2/4/not specified) + prm[4] = 'C'; // "something related to graphics" + } // (2/4/not specified) + else if (mapper % 16 == 4) + { + prm[1] = 'N'; + prm[2] = 'T'; + prm[4] = 'N'; + } + + if (UCON64_ISSET (ucon64.battery)) + { + if (ucon64.battery) + header->ctrl1 |= INES_SRAM; + else + header->ctrl1 &= ~INES_SRAM; + } + if (header->ctrl1 & INES_SRAM) // preserve extension RAM + prm[14] = 'E'; + + if (mapper) // bank-switched ROM? + prm[17] = 'M'; + + // don't write backups of parts, because one name is used + if (ucon64_fwrite (prm, 0, sizeof (prm), fname, "wb") == -1) + { + fprintf (stderr, ucon64_msg[WRITE_ERROR], fname); + return -1; // try to continue + } + else + printf (ucon64_msg[WROTE], fname); + + return 0; +} + + +int +nes_s (void) +{ + char dest_name[FILENAME_MAX]; + unsigned char *trainer_data = NULL, *prg_data = NULL, *chr_data = NULL; + int prg_size = 0, chr_size = 0, x; + FILE *srcfile; + + if (type != INES) + { + fprintf (stderr, "ERROR: Currently only iNES -> Pasofami is supported\n"); + return -1; + } + + if ((srcfile = fopen (ucon64.rom, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], ucon64.rom); + return -1; + } + + // read iNES file + fread (&ines_header, 1, INES_HEADER_LEN, srcfile); + if (ines_header.ctrl1 & INES_TRAINER) + { + if (read_block (&trainer_data, 512, srcfile, + "ERROR: Not enough memory for trainer buffer (%d bytes)\n", 512) != 512) + { + fprintf (stderr, "ERROR: %s is not a valid iNES file", ucon64.rom); + exit (1); + } + } + prg_size = ines_header.prg_size << 14; + prg_size = read_block (&prg_data, prg_size, srcfile, + "ERROR: Not enough memory for PRG buffer (%d bytes)\n", prg_size); + chr_size = ines_header.chr_size << 13; + if (chr_size > 0) + chr_size = read_block (&chr_data, chr_size, srcfile, + "ERROR: Not enough memory for CHR buffer (%d bytes)\n", chr_size); + + if (ucon64.mapr != NULL && strlen (ucon64.mapr) > 0) // mapper specified + { + x = strtol (ucon64.mapr, NULL, 10); + if (x == 0 || x == 2 || x == 4) + set_mapper (&ines_header, x); + else + printf ("WARNING: Pasofami can only store mapper numbers 0, 2 or 4; using old value\n"); + } + + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".prm"); + ucon64_output_fname (dest_name, 0); + write_prm (&ines_header, dest_name); + + if (ines_header.ctrl1 & INES_TRAINER) + { + set_suffix (dest_name, ".700"); + // don't write backups of parts, because one name is used + if (ucon64_fwrite (trainer_data, 0, 512, dest_name, "wb") == -1) + fprintf (stderr, ucon64_msg[WRITE_ERROR], dest_name); // try to continue + else + printf (ucon64_msg[WROTE], dest_name); + } + + if (prg_size > 0) + { + set_suffix (dest_name, ".prg"); + // don't write backups of parts, because one name is used + if (ucon64_fwrite (prg_data, 0, prg_size, dest_name, "wb") == -1) + fprintf (stderr, ucon64_msg[WRITE_ERROR], dest_name); // try to continue + else + printf (ucon64_msg[WROTE], dest_name); + } + else + printf ("WARNING: No PRG data in %s\n", ucon64.rom); + + if (chr_size > 0) + { + set_suffix (dest_name, ".chr"); + // don't write backups of parts, because one name is used + if (ucon64_fwrite (chr_data, 0, chr_size, dest_name, "wb") == -1) + fprintf (stderr, ucon64_msg[WRITE_ERROR], dest_name); // try to continue + else + printf (ucon64_msg[WROTE], dest_name); + } + + free (trainer_data); + free (prg_data); + free (chr_data); + fclose(srcfile); + return 0; +} + + +int +nes_n (const char *name) +{ + if (type != UNIF) + { + fprintf (stderr, "ERROR: This option is only meaningful for UNIF files\n"); + return -1; + } + + if (name != NULL && strlen (name) > 0) + internal_name = name; + else + internal_name = NULL; + + return nes_unif (); // will call nes_unif_unif() +} + + +int +nes_init (st_rominfo_t *rominfo) +{ + unsigned char magic[15], *rom_buffer; + int result = -1, size, x, y, n, crc = 0; + // currently 92 bytes is enough for ctrl_str, but extra space avoids + // introducing bugs when controller type text would be changed + char buf[MAXBUFSIZE], ctrl_str[200], *str, *str_list[8]; + st_unif_chunk_t *unif_chunk, *unif_chunk2; + st_nes_data_t *info, key; + + internal_name = NULL; // reset this var, see nes_n() + type = PASOFAMI; // reset type, see below + + ucon64_fread (magic, 0, 15, ucon64.rom); + if (memcmp (magic, "NES", 3) == 0) + /* + Check for "NES" and not for INES_SIG_S ("NES\x1a"), because there are two + NES files floating around on the internet with a pseudo iNES header: + "Home Alone 2 - Lost in New York (U) [b3]" (magic: "NES\x19") and + "Linus Music Demo (PD)" (magic: "NES\x1b") + */ + { + type = INES; + result = 0; + } + else if (memcmp (magic, UNIF_SIG_S, 4) == 0) + { + type = UNIF; + result = 0; + } + else if (memcmp (magic, FDS_SIG_S, 4) == 0) + { + type = FDS; + result = 0; + + rominfo->buheader_start = FDS_HEADER_START; + rominfo->buheader_len = FDS_HEADER_LEN; + // we use ffe_header to save some space in the exe + ucon64_fread (&ffe_header, FDS_HEADER_START, FDS_HEADER_LEN, ucon64.rom); + rominfo->buheader = &ffe_header; + } + else if (memcmp (magic, "\x01*NINTENDO-HVC*", 15) == 0) // "headerless" FDS/FAM file + { + if (ucon64.file_size % 65500 == 192) + type = FAM; + else + type = FDS; + result = 0; + } + + if (type == PASOFAMI) // INES, UNIF, FDS and FAM are much + { // more reliable than stricmp()s + str = (char *) get_suffix (ucon64.rom); + if (!stricmp (str, ".prm") || + !stricmp (str, ".700") || + !stricmp (str, ".prg") || + !stricmp (str, ".chr")) + { + type = PASOFAMI; + result = 0; + } + else if (magic[8] == 0xaa && magic[9] == 0xbb) + { // TODO: finding a reliable means + type = FFE; // for detecting FFE images + result = 0; + } + } + if (ucon64.console == UCON64_NES) + result = 0; + + switch (type) + { + case INES: + rominfo->copier_usage = ines_usage[0].help; + rominfo->buheader_start = INES_HEADER_START; + rominfo->buheader_len = INES_HEADER_LEN; + ucon64_fread (&ines_header, INES_HEADER_START, INES_HEADER_LEN, ucon64.rom); + rominfo->buheader = &ines_header; + ucon64.split = 0; // iNES files are never split + + sprintf (buf, "Internal size: %.4f Mb\n", + TOMBIT_F ((ines_header.prg_size << 14) + (ines_header.chr_size << 13))); + strcat (rominfo->misc, buf); + + sprintf (buf, "Internal PRG size: %.4f Mb\n", // ROM + TOMBIT_F (ines_header.prg_size << 14)); + strcat (rominfo->misc, buf); + + sprintf (buf, "Internal CHR size: %.4f Mb\n", // VROM + TOMBIT_F (ines_header.chr_size << 13)); + strcat (rominfo->misc, buf); + + x = (ines_header.ctrl1 >> 4) | (ines_header.ctrl2 & 0xf0); + if (ines_header.ctrl2 & 0xf) + sprintf (buf, "Memory mapper (iNES): %d (%d)\n", x, + x | ((ines_header.ctrl2 & 0xf) << 8)); + else + sprintf (buf, "Memory mapper (iNES): %d\n", x); + strcat (rominfo->misc, buf); + + if (ines_header.ctrl1 & INES_MIRROR) + str = "Vertical"; + else if (ines_header.ctrl1 & INES_4SCREEN) + str = "Four screens of VRAM"; + else + str = "Horizontal"; + sprintf (buf, "Mirroring: %s\n", str); + strcat (rominfo->misc, buf); + + sprintf (buf, "Save RAM: %s\n", (ines_header.ctrl1 & INES_SRAM) ? "Yes" : "No"); + strcat (rominfo->misc, buf); + + sprintf (buf, "512-byte trainer: %s\n", + (ines_header.ctrl1 & INES_TRAINER) ? "Yes" : "No"); + strcat (rominfo->misc, buf); + + sprintf (buf, "VS-System: %s", (ines_header.ctrl2 & 0x01) ? "Yes" : "No"); + strcat (rominfo->misc, buf); + break; + case UNIF: + rominfo->copier_usage = unif_usage[0].help; + rominfo->buheader_start = UNIF_HEADER_START; + rominfo->buheader_len = UNIF_HEADER_LEN; + ucon64_fread (&unif_header, UNIF_HEADER_START, UNIF_HEADER_LEN, ucon64.rom); + rominfo->buheader = &unif_header; + + rom_size = ucon64.file_size - UNIF_HEADER_LEN; + if ((rom_buffer = (unsigned char *) malloc (rom_size)) == NULL) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], rom_size); + return -1; //exit (1); please don't use exit () in init + } + ucon64_fread (rom_buffer, UNIF_HEADER_LEN, rom_size, ucon64.rom); + ucon64.split = 0; // UNIF files are never split + + x = me2le_32 (unif_header.revision); // Don't modify header data + sprintf (buf, "UNIF revision: %d\n", x); + strcpy (rominfo->misc, buf); + + if ((unif_chunk = read_chunk (MAPR_ID, rom_buffer, 0)) != NULL) + { + sprintf (buf, "Board name: %s\n", (char *) unif_chunk->data); + strcat (rominfo->misc, buf); + } + free (unif_chunk); + if ((unif_chunk = read_chunk (READ_ID, rom_buffer, 0)) != NULL) + { + sprintf (buf, "Comment: %s\n", (char *) unif_chunk->data); + strcat (rominfo->misc, buf); + } + free (unif_chunk); +#if UNIF_REVISION > 7 + if ((unif_chunk = read_chunk (WRTR_ID, rom_buffer, 0)) != NULL) + { + char ucon64_name[] = "uCON64"; + strcat (rominfo->misc, "Processed by: "); + y = 0; + do + { + if (y) + strcat (rominfo->misc, ", "); + /* + The format of the uCON64 WRTR chunk is: + uCON64 + but other tools needn't use the same format. We can only be + sure that the string starts with the tool name. + */ + y = strlen ((const char *) unif_chunk->data); + x = 0; + if (!strncmp ((const char *) unif_chunk->data, ucon64_name, strlen (ucon64_name))) + { + while (x < y) + { + if (((char *) unif_chunk->data)[x] == WRTR_MARKER) + ((char *) unif_chunk->data)[x] = ' '; + x++; + } + } + else + { + while (x < y) + { + if (((char *) unif_chunk->data)[x] == WRTR_MARKER) + { + ((char *) unif_chunk->data)[x] = 0; + break; + } + x++; + } + } + strcat (rominfo->misc, (const char *) unif_chunk->data); + y = 1; + free (unif_chunk); + } + while ((unif_chunk = read_chunk (WRTR_ID, rom_buffer, 1)) != NULL); + strcat (rominfo->misc, "\n"); + } +#endif + if ((unif_chunk = read_chunk (NAME_ID, rom_buffer, 0)) != NULL) + { +#if 0 + sprintf (buf, "Internal name: %s\n", (char *) unif_chunk->data); + strcat (rominfo->misc, buf); +#endif + memcpy (rominfo->name, unif_chunk->data, + unif_chunk->length > sizeof rominfo->name ? + sizeof rominfo->name : unif_chunk->length); + } + free (unif_chunk); + if ((unif_chunk = read_chunk (TVCI_ID, rom_buffer, 0)) != NULL) + { + str_list[0] = "NTSC"; + str_list[1] = "PAL"; + str_list[2] = "NTSC/PAL"; + x = *((unsigned char *) unif_chunk->data); + sprintf (buf, "Television standard: %s\n", x > 2 ? "Unknown" : str_list[x]); + strcat (rominfo->misc, buf); + } + free (unif_chunk); + if ((unif_chunk = read_chunk (DINF_ID, rom_buffer, 0)) != NULL) + { + st_dumper_info_t *info = (st_dumper_info_t *) unif_chunk->data; + sprintf (buf, "Dump info:\n" + " Dumper: %s\n" + " Date: %d-%d-%02d\n" + " Agent: %s\n", + info->dumper_name, + info->day, info->month, le2me_16 (info->year), + info->dumper_agent); + strcat (rominfo->misc, buf); + } + free (unif_chunk); + if ((unif_chunk = read_chunk (CTRL_ID, rom_buffer, 0)) != NULL) + { + str_list[0] = "Regular joypad"; + str_list[1] = "Zapper"; + str_list[2] = "R.O.B."; + str_list[3] = "Arkanoid controller"; + str_list[4] = "Power pad"; + str_list[5] = "Four-score adapter"; + str_list[6] = "Unknown"; // bit 6 and 7 are reserved + str_list[7] = str_list[6]; + ctrl_str[0] = 0; + + x = *((unsigned char *) unif_chunk->data); + y = 0; + for (n = 0; n < 8; n++) + if (x & (1 << n)) + { + if (y) + strcat (ctrl_str, ", "); + strcat (ctrl_str, str_list[n]); + y = 1; + } + sprintf (buf, "Supported controllers: %s\n", ctrl_str); + strcat (rominfo->misc, buf); + } + free (unif_chunk); + + size = 0; + // PRG chunk info + for (n = 0; n < 16; n++) + { + if ((unif_chunk = read_chunk (unif_prg_ids[n], rom_buffer, 0)) != NULL) + { + crc = crc32 (crc, (unsigned char *) unif_chunk->data, unif_chunk->length); + size += unif_chunk->length; + if ((unif_chunk2 = read_chunk (unif_pck_ids[n], rom_buffer, 0)) == NULL) + str = "not available"; + else + { + x = crc32 (0, (unsigned char *) unif_chunk->data, unif_chunk->length); +#ifdef WORDS_BIGENDIAN + x = bswap_32 (x); +#endif + str = (char *) +#ifdef USE_ANSI_COLOR + (ucon64.ansi_color ? + ((x == *((int *) unif_chunk2->data)) ? + "\x1b[01;32mok\x1b[0m" : "\x1b[01;31mbad\x1b[0m") + : + ((x == *((int *) unif_chunk2->data)) ? "ok" : "bad")); +#else + ((x == *((int *) unif_chunk2->data)) ? "ok" : "bad"); +#endif + } + sprintf (buf, "PRG%X: %.4f Mb, checksum %s\n", n, + TOMBIT_F (unif_chunk->length), str); + strcat (rominfo->misc, buf); + free (unif_chunk2); + } + free (unif_chunk); + } + + // CHR chunk info + for (n = 0; n < 16; n++) + { + if ((unif_chunk = read_chunk (unif_chr_ids[n], rom_buffer, 0)) != NULL) + { + crc = crc32 (crc, (unsigned char *) unif_chunk->data, unif_chunk->length); + size += unif_chunk->length; + if ((unif_chunk2 = read_chunk (unif_cck_ids[n], rom_buffer, 0)) == NULL) + str = "not available"; + else + { + x = crc32 (0, (unsigned char *) unif_chunk->data, unif_chunk->length); +#ifdef WORDS_BIGENDIAN + x = bswap_32 (x); +#endif + str = (char *) +#ifdef USE_ANSI_COLOR + (ucon64.ansi_color ? + ((x == *((int *) unif_chunk2->data)) ? + "\x1b[01;32mok\x1b[0m" : "\x1b[01;31mbad\x1b[0m") + : + ((x == *((int *) unif_chunk2->data)) ? "ok" : "bad")); +#else + ((x == *((int *) unif_chunk2->data)) ? "ok" : "bad"); +#endif + } + sprintf (buf, "CHR%X: %.4f Mb, checksum %s\n", n, + TOMBIT_F (unif_chunk->length), str); + strcat (rominfo->misc, buf); + free (unif_chunk2); + } + free (unif_chunk); + } + ucon64.crc32 = crc; + rominfo->data_size = size; + + // Don't introduce extra code just to make this line be printed above + // the previous two line types (PRG & CHR) + sprintf (buf, "Size: %.4f Mb\n", TOMBIT_F (rominfo->data_size)); + strcat (rominfo->misc, buf); + + x = 0; + if ((unif_chunk = read_chunk (BATR_ID, rom_buffer, 0)) != NULL) + x = 1; + sprintf (buf, "Save RAM: %s\n", x ? "Yes" : "No"); + strcat (rominfo->misc, buf); + free (unif_chunk); + if ((unif_chunk = read_chunk (MIRR_ID, rom_buffer, 0)) != NULL) + { + str_list[0] = "Horizontal (hard wired)"; + str_list[1] = "Vertical (hard wired)"; + str_list[2] = "All pages from $2000 (hard wired)"; + str_list[3] = "All pages from $2400 (hard wired)"; + str_list[4] = "Four screens of VRAM (hard wired)"; + str_list[5] = "Controlled by mapper hardware"; + x = *((unsigned char *) unif_chunk->data); + sprintf (buf, "Mirroring: %s\n", x > 5 ? "Unknown" : str_list[x]); + strcat (rominfo->misc, buf); + } + free (unif_chunk); + x = 0; + if ((unif_chunk = read_chunk (VROR_ID, rom_buffer, 0)) != NULL) + x = 1; + sprintf (buf, "VRAM override: %s", x ? "Yes" : "No"); + strcat (rominfo->misc, buf); + free (unif_chunk); + + free (rom_buffer); + break; + case PASOFAMI: + /* + Either a *.PRM header file, a 512-byte *.700 trainer file, a *.PRG + ROM data file or a *.CHR VROM data file. + */ + rominfo->copier_usage = pasofami_usage[0].help; + rominfo->buheader_start = 0; + strcpy (buf, ucon64.rom); + set_suffix (buf, ".prm"); + if (access (buf, F_OK) == 0) + { + rominfo->buheader_len = fsizeof (buf); + // we use ffe_header to save some space + ucon64_fread (&ffe_header, 0, rominfo->buheader_len, buf); + rominfo->buheader = &ffe_header; + } + else + rominfo->buheader_len = 0; + + /* + Build a temporary iNES image in memory from the Pasofami files. + In memory, because we want to be able to display info for Pasofami + files on read-only filesystems WITHOUT messing with/finding temporary + storage somewhere. We also want to calculate the CRC and it's handy to + have the data in memory for that. + Note that nes_j() wouldn't be much different if ucon64_chksum() would be + used. This function wouldn't be much different either. + */ + x = nes_j (&rom_buffer); + rominfo->data_size = (ines_header.prg_size << 14) + (ines_header.chr_size << 13) + + ((ines_header.ctrl1 & INES_TRAINER) ? 512 : 0); + if (x == 0) + { // use buf only if it could be allocated + ucon64.crc32 = crc32 (0, rom_buffer, rominfo->data_size); + free (rom_buffer); + } + + sprintf (buf, "Size: %.4f Mb\n", TOMBIT_F (rominfo->data_size)); + strcat (rominfo->misc, buf); + + sprintf (buf, "PRG size: %.4f Mb\n", // ROM, don't say internal, + TOMBIT_F (ines_header.prg_size << 14)); // because it's not + strcat (rominfo->misc, buf); + + sprintf (buf, "CHR size: %.4f Mb\n", // VROM + TOMBIT_F (ines_header.chr_size << 13)); + strcat (rominfo->misc, buf); + + sprintf (buf, "Memory mapper (iNES): %d\n", + (ines_header.ctrl1 >> 4) | (ines_header.ctrl2 & 0xf0)); + strcat (rominfo->misc, buf); + + sprintf (buf, "Mirroring: %s\n", + (ines_header.ctrl1 & INES_MIRROR) ? "Vertical" : "Horizontal"); + strcat (rominfo->misc, buf); + + sprintf (buf, "Save RAM: %s\n", + (ines_header.ctrl1 & INES_SRAM) ? "Yes" : "No"); + strcat (rominfo->misc, buf); + + sprintf (buf, "512-byte trainer: %s", + (ines_header.ctrl1 & INES_TRAINER) ? "Yes" : "No"); + strcat (rominfo->misc, buf); + break; + case FFE: + if (magic[10] == 1) + { + rominfo->buheader_len = SMC_HEADER_LEN; + strcpy (rominfo->name, "Name: N/A"); + rominfo->console_usage = NULL; + rominfo->copier_usage = smc_usage[0].help; + rominfo->maker = "Publisher: You?"; + rominfo->country = "Country: Your country?"; + rominfo->has_internal_crc = 0; + ucon64.split = 0; // RTS files are never split + strcat (rominfo->misc, "Type: Super Magic Card RTS file\n"); + return 0; // rest is nonsense for RTS file + } + + /* + 512-byte header + 512-byte trainer (optional) + ROM data + VROM data (optional) + + It makes no sense to make a temporary iNES image here. It makes sense + for Pasofami, because there might be a .PRM file and because there is + still other information about the image structure. + */ + rominfo->copier_usage = smc_usage[0].help; + rominfo->buheader_start = SMC_HEADER_START; + rominfo->buheader_len = SMC_HEADER_LEN; + ucon64_fread (&ffe_header, SMC_HEADER_START, SMC_HEADER_LEN, ucon64.rom); + rominfo->buheader = &ffe_header; + + sprintf (buf, "512-byte trainer: %s", + (ffe_header.emulation1 & SMC_TRAINER) ? "Yes" : "No"); + strcat (rominfo->misc, buf); + break; + case FDS: + rominfo->copier_usage = fds_usage[0].help; + rominfo->country = "Japan"; + strcat (rominfo->misc, "\n"); + nes_fdsl (rominfo, rominfo->misc); // will also fill in rominfo->name + break; + case FAM: + rominfo->copier_usage = fds_usage[0].help; + rominfo->country = "Japan"; + + // FAM files don't have a header. Instead they seem to have a 192 byte trailer. + rominfo->buheader_start = ucon64.file_size - FAM_HEADER_LEN; + rominfo->buheader_len = FAM_HEADER_LEN; + + // we use ffe_header to save some space + ucon64_fread (&ffe_header, rominfo->buheader_start, FAM_HEADER_LEN, ucon64.rom); + rominfo->buheader = &ffe_header; + strcat (rominfo->misc, "\n"); + nes_fdsl (rominfo, rominfo->misc); // will also fill in rominfo->name + + rom_size = ucon64.file_size - FAM_HEADER_LEN; + if ((rom_buffer = (unsigned char *) malloc (rom_size)) == NULL) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], rom_size); + return -1; + } + ucon64_fread (rom_buffer, 0, rom_size, ucon64.rom); + ucon64.crc32 = crc32 (0, rom_buffer, rom_size); + free (rom_buffer); + break; + } + + if (UCON64_ISSET (ucon64.buheader_len)) // -hd, -nhd or -hdn switch was specified + rominfo->buheader_len = ucon64.buheader_len; + + if (ucon64.crc32 == 0) + ucon64_chksum (NULL, NULL, &ucon64.crc32, ucon64.rom, rominfo->buheader_len); + + // additional info + key.crc32 = ucon64.crc32; + info = (st_nes_data_t *) bsearch (&key, nes_data, sizeof nes_data / sizeof (st_nes_data_t), + sizeof (st_nes_data_t), nes_compare); + if (info) + { + if (info->maker) + rominfo->maker = NULL_TO_UNKNOWN_S (nes_maker[MIN (info->maker, NES_MAKER_MAX - 1)]); + + rominfo->country = NULL_TO_UNKNOWN_S (nes_country[MIN (info->country, NES_COUNTRY_MAX - 1)]); + + if (info->date) + { + int month = info->date / 100, year = info->date % 100; + char format[80]; + + if (month) + { + sprintf (format, "\nDate: %%d/19%%d"); + strcat (format, (year == 8 || year == 9) ? "x" : ""); + sprintf (buf, format, month, year); + } + else + { + sprintf (format, "\nDate: 19%%d"); + strcat (format, (year == 8 || year == 9) ? "x" : ""); + sprintf (buf, format, year); + } + strcat (rominfo->misc, buf); + } + } + + rominfo->console_usage = nes_usage[0].help; + + return result; +} + + +static inline char * +to_func (char *s, int len, int (*func) (int)) +{ + char *p = s; + + for (; len > 0; p++, len--) + *p = func (*p); + + return s; +} + + +int +nes_fdsl (st_rominfo_t *rominfo, char *output_str) +/* + Note that NES people prefer $ to signify hexadecimal numbers (not 0x). + However we will print only addresses that way. + This code is based on Marat Fayzullin's FDSLIST. +*/ +{ + FILE *srcfile; + unsigned char buffer[58]; + char name[16], str_list_mem[6], *str_list[4], info_mem[MAXBUFSIZE], *info, line[80]; + int disk, n_disks, file, n_files, start, size, x, header_len = 0; + + if (output_str == NULL) + { + info = info_mem; + info[0] = 0; + } + else + info = output_str; + + if ((srcfile = fopen (ucon64.rom, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], ucon64.rom); + return -1; + } + + n_disks = (ucon64.file_size - rominfo->buheader_len) / 65500; + x = (ucon64.file_size - rominfo->buheader_len) % 65500; + if (x) + { + sprintf (line, "WARNING: %d excessive bytes\n", x); + strcat (info, line); + } + + if (type == FDS) + header_len = rominfo->buheader_len; + else if (type == FAM) // if type == FAM rominfo->buheader_len + header_len = 0; // contains the length of the trailer + for (disk = 0; disk < n_disks; disk++) + { + // go to the next disk + fseek (srcfile, disk * 65500 + header_len, SEEK_SET); + + // read the disk header + if (fread (buffer, 1, 58, srcfile) != 58) + { + fprintf (stderr, "ERROR: Can't read disk header\n"); + fclose (srcfile); + return -1; + } + + if (memcmp (buffer, "\x01*NINTENDO-HVC*", 15)) + { + fprintf (stderr, "ERROR: Invalid disk header\n"); + fclose (srcfile); + return -1; // should we return? + } + + if (buffer[56] != 2) + strcat (info, "WARNING: Invalid file number header\n"); + + memcpy (name, buffer + 16, 4); + name[4] = 0; + if (disk == 0 && output_str != NULL) + memcpy (rominfo->name, name, 4); + n_files = buffer[57]; + sprintf (line, "Disk: '%-4s' Side: %c Files: %d Maker: 0x%02x Version: 0x%02x\n", + name, (buffer[21] & 1) + 'A', n_files, buffer[15], buffer[20]); + strcat (info, line); + + file = 0; + while (file < n_files && fread (buffer, 1, 16, srcfile) == 16) + { + if (buffer[0] != 3) + { + sprintf (line, "WARNING: Invalid file header block ID (0x%02x)\n", buffer[0]); + strcat (info, line); + } + + // get name, data location, and size + strncpy (name, (const char *) buffer + 3, 8); + name[8] = 0; + start = buffer[11] + 256 * buffer[12]; + size = buffer[13] + 256 * buffer[14]; + + x = fgetc (srcfile); + if (x != 4) + { + sprintf (line, "WARNING: Invalid data block ID (0x%02x)\n", x); + strcat (info, line); + } + + str_list[0] = "Code"; + str_list[1] = "Tiles"; + str_list[2] = "Picture"; + if (buffer[15] > 2) + { + str_list[3] = str_list_mem; + sprintf (str_list[3], "0x%02x?", buffer[15]); + buffer[15] = 3; + } + /* + Some FDS files contain control characters in their names. sprintf() + won't print those character so we have to use to_func() with + toprint(). + */ + sprintf (line, "%03d $%02x '%-8s' $%04x-$%04x [%s]\n", + buffer[1], buffer[2], + to_func (name, strlen (name), toprint), + start, start + size - 1, str_list[buffer[15]]); + strcat (info, line); + + fseek (srcfile, size, SEEK_CUR); + file++; + } + if (disk != n_disks - 1) + strcat (info, "\n"); // print newline between disk info blocks + } + + if (output_str == NULL) + puts (info); + + fclose (srcfile); + return 0; +} + + +int +nes_fds (void) +/* + This function converts a Famicom Disk System disk image from FAM format to + FDS format. It does almost the same as -strip apart from three checks + whether the input file is a valid FAM file. + The "algorithm" comes from Marat Fayzullin's FAM2FDS. +*/ +{ + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX], *buffer; + int n; + + if (type != FAM) + { + fprintf (stderr, "ERROR: %s is not a FAM file\n", ucon64.rom); + return -1; + } + if ((buffer = (char *) malloc (65500)) == NULL) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], 65500); + return -1; + } + + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".fds"); + strcpy (src_name, ucon64.rom); + ucon64_file_handler (dest_name, src_name, 0); + + for (n = 0; n < 4; n++) + { + if (ucon64_fread (buffer, n * 65500, 65500, src_name) != 65500) + break; + + // check disk image for validity + if (buffer[0] != 1 || buffer[56] != 2 || buffer[58] != 3) + { + fprintf (stderr, "ERROR: %s is not a valid FAM file\n", ucon64.rom); + break; + } + // FAM2FDS also does the following: + // 1 - check if the last chunk is one of a new game (use buffer[16] - buffer[19]) + // 2 - if not, check if buffer[21] (side bit) differs from least significant bit of n + // 3 - if that is the case _print_ (but don't do anything else) "WRONG" else "OK" + + if (ucon64_fwrite (buffer, n * 65500, 65500, dest_name, n ? "ab" : "wb") != 65500) + break; + } + /* + FAM2FDS prints an error message if n isn't 4 at this point (a break was + executed). However, other information seems to indicate that FAM files can + hold fewer than 4 disk images. + */ + + free (buffer); + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return 0; +} diff --git a/ucon64/2.0/src/console/nes.h b/ucon64/2.0/src/console/nes.h new file mode 100644 index 0000000..9250bfa --- /dev/null +++ b/ucon64/2.0/src/console/nes.h @@ -0,0 +1,236 @@ +/* +nes.h - Nintendo Entertainment System support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2002 - 2003 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef NES_H +#define NES_H + +typedef enum { INES = 1, UNIF, PASOFAMI, FFE, FDS, FAM } nes_file_t; + +extern const st_getopt2_t nes_usage[]; + +/* + iNES Format (.NES) + ------------------ + + Following the iNES header description (size = $10 bytes): + + +--------+------+------------------------------------------+ + | Offset | Size | Content(s) | + +--------+------+------------------------------------------+ + | 0 | 3 | 'NES' | + | 3 | 1 | $1A | + | 4 | 1 | 16K PRG-ROM page count (size=x*0x4000) | + | 5 | 1 | 8K CHR-ROM page count (size=y*0x2000) | + | 6 | 1 | ROM Control Byte #1 | + | | | %####vTsM | + | | | | ||||+- 0=Horizontal Mirroring | + | | | | |||| 1=Vertical Mirroring | + | | | | |||+-- 1=SaveRAM enabled ($6000) | + | | | | ||+--- 1=Trainer data (512 bytes) | + | | | | |+---- 1=Four-screen mirroring | + | | | | | ($2000,$2400,$2800,$2C00) | + | | | +--+----- Mapper # (lower 4-bits) | + | 7 | 1 | ROM Control Byte #2 | + | | | %####00PU | + | | | | | |+- 1=VS Unisystem arcade | + | | | | | +-- 1=Playchoice-10 arcade | + | | | +--+----- Mapper # (upper 4-bits) | + | 8-15 | 8 | $00 | + | 16-.. | | Actual 16K PRG-ROM pages (in linear | + | ... | | order). If a trainer exists, it precedes | + | ... | | the first PRG-ROM bank. | + | ..-EOF | | CHR-ROM pages (in ascending order). | + +--------+------+------------------------------------------+ + + IMPORTANT: the iNES format DOES NOT support mappers greater than 255. + There are a couple of Famicom mappers > 255. They use byte #7 (low 4 bits), + which conflicts with VS Unisystem/PlayChoice-10 identification. +*/ + +#define INES_SIG_S "NES\x1a" + +// flags in st_ines_header_t.ctrl1 +#define INES_MIRROR 0x01 +#define INES_SRAM 0x02 +#define INES_TRAINER 0x04 +#define INES_4SCREEN 0x08 + +#define INES_HEADER_START 0 +#define INES_HEADER_LEN (sizeof (st_ines_header_t)) + +typedef struct +{ + char signature[4]; // 0x4e,0x45,0x53,0x1a (NES file signature) + unsigned char prg_size; // # 16 kB banks + unsigned char chr_size; // # 8 kB banks + unsigned char ctrl1; + unsigned char ctrl2; + unsigned int reserved1; // 0 + unsigned int reserved2; // 0 +} st_ines_header_t; + + +#define UNIF_SIG_S "UNIF" +#define UNIF_REVISION 8 // the "official" spec is at version 7 (10/08/2002) + +// numeric values of id strings in little endian format, e.g. +// UNIF_ID == 'F' << 24 | 'I' << 16 | 'N' << 8 | 'U' +#define UNIF_ID 0x46494E55 +#define MAPR_ID 0x5250414D +#define READ_ID 0x44414552 +#define NAME_ID 0x454D414E +#define TVCI_ID 0x49435654 +#define DINF_ID 0x464E4944 +#define CTRL_ID 0x4C525443 +#define BATR_ID 0x52544142 +#define VROR_ID 0x524F5256 +#define MIRR_ID 0x5252494D + +#define PCK0_ID 0x304B4350 +#define PCK1_ID 0x314B4350 +#define PCK2_ID 0x324B4350 +#define PCK3_ID 0x334B4350 +#define PCK4_ID 0x344B4350 +#define PCK5_ID 0x354B4350 +#define PCK6_ID 0x364B4350 +#define PCK7_ID 0x374B4350 +#define PCK8_ID 0x384B4350 +#define PCK9_ID 0x394B4350 +#define PCKA_ID 0x414B4350 +#define PCKB_ID 0x424B4350 +#define PCKC_ID 0x434B4350 +#define PCKD_ID 0x444B4350 +#define PCKE_ID 0x454B4350 +#define PCKF_ID 0x464B4350 + +#define CCK0_ID 0x304B4343 +#define CCK1_ID 0x314B4343 +#define CCK2_ID 0x324B4343 +#define CCK3_ID 0x334B4343 +#define CCK4_ID 0x344B4343 +#define CCK5_ID 0x354B4343 +#define CCK6_ID 0x364B4343 +#define CCK7_ID 0x374B4343 +#define CCK8_ID 0x384B4343 +#define CCK9_ID 0x394B4343 +#define CCKA_ID 0x414B4343 +#define CCKB_ID 0x424B4343 +#define CCKC_ID 0x434B4343 +#define CCKD_ID 0x444B4343 +#define CCKE_ID 0x454B4343 +#define CCKF_ID 0x464B4343 + +#define PRG0_ID 0x30475250 +#define PRG1_ID 0x31475250 +#define PRG2_ID 0x32475250 +#define PRG3_ID 0x33475250 +#define PRG4_ID 0x34475250 +#define PRG5_ID 0x35475250 +#define PRG6_ID 0x36475250 +#define PRG7_ID 0x37475250 +#define PRG8_ID 0x38475250 +#define PRG9_ID 0x39475250 +#define PRGA_ID 0x41475250 +#define PRGB_ID 0x42475250 +#define PRGC_ID 0x43475250 +#define PRGD_ID 0x44475250 +#define PRGE_ID 0x45475250 +#define PRGF_ID 0x46475250 + +#define CHR0_ID 0x30524843 +#define CHR1_ID 0x31524843 +#define CHR2_ID 0x32524843 +#define CHR3_ID 0x33524843 +#define CHR4_ID 0x34524843 +#define CHR5_ID 0x35524843 +#define CHR6_ID 0x36524843 +#define CHR7_ID 0x37524843 +#define CHR8_ID 0x38524843 +#define CHR9_ID 0x39524843 +#define CHRA_ID 0x41524843 +#define CHRB_ID 0x42524843 +#define CHRC_ID 0x43524843 +#define CHRD_ID 0x44524843 +#define CHRE_ID 0x45524843 +#define CHRF_ID 0x46524843 + +#if UNIF_REVISION > 7 +// UNIF revision 8 (if it ever comes out) will probably include this chunk type +#define WRTR_ID 0x52545257 +#define WRTR_MARKER ';' +#define WRTR_MARKER_S ";" +#endif + +#define BOARDNAME_MAXLEN 32 // chunk length, NOT string length + // string lentgh = chunk length - 1 +#define UNIF_HEADER_START 0 +#define UNIF_HEADER_LEN (sizeof (st_unif_header_t)) + +typedef struct +{ + char signature[4]; // 0x55,0x4e,0x49,0x46 ("UNIF") + unsigned int revision; // revision number + unsigned char expansion[24]; // reserved +} st_unif_header_t; + +typedef struct +{ + unsigned int id; // chunk identification string + unsigned int length; // data length, in little endian format + void *data; +} st_unif_chunk_t; + +// DINF chunk data +typedef struct +{ + char dumper_name[100]; // name of the person who dumped the cart + unsigned char day; // day of the month when cartridge was dumped + unsigned char month; // month of the year when cartridge was dumped + unsigned short year; // year during which the cartridge was dumped + char dumper_agent[100]; // name of the ROM-dumping means used (ASCII-Z string) +} st_dumper_info_t; + + +#define FDS_SIG_S "FDS\x1A" +#define FDS_HEADER_START 0 +#define FDS_HEADER_LEN 16 + + +// the FAM define is a guess based on FAM2FDS, more info is needed about the +// FAM format +#define FAM_HEADER_LEN 192 + + +extern int nes_fds (void); +extern int nes_fdsl (st_rominfo_t *rominfo, char *output_str); +extern int nes_n (const char *name); +extern int nes_s (void); +extern int nes_pasofami (void); +extern int nes_ineshd (st_rominfo_t *rominfo); +extern int nes_ffe (st_rominfo_t *rominfo); +extern int nes_ines (void); +extern int nes_init (st_rominfo_t *rominfo); +extern int nes_unif (void); +extern int nes_j (unsigned char **mem_image); +extern int nes_dint (void); +extern nes_file_t nes_get_file_type (void); + +#endif // NES_H diff --git a/ucon64/2.0/src/console/ngp.c b/ucon64/2.0/src/console/ngp.c new file mode 100644 index 0000000..7052486 --- /dev/null +++ b/ucon64/2.0/src/console/ngp.c @@ -0,0 +1,112 @@ +/* +ngp.c - NeoGeo Pocket support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2001 Gulliver + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include "misc/misc.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "ngp.h" +#include "backup/pl.h" + + +const st_getopt2_t ngp_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Neo Geo Pocket/Neo Geo Pocket Color"/*"1998/1999 SNK http://www.neogeo.co.jp"*/, + NULL + }, + { + "ngp", 0, 0, UCON64_NGP, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_NGP_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +typedef struct st_ngp_header +{ + char pad[48]; +} st_ngp_header_t; + +#define NGP_HEADER_START 0 +#define NGP_HEADER_LEN (sizeof (st_ngp_header_t)) + + +st_ngp_header_t ngp_header; + + +int +ngp_init (st_rominfo_t *rominfo) +{ + int result = -1; + char *snk_code = "COPYRIGHT BY SNK CORPORATION", + *third_code = " LICENSED BY SNK CORPORATION", buf[MAXBUFSIZE]; + + rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? ucon64.buheader_len : 0; + + ucon64_fread (&ngp_header, NGP_HEADER_START + rominfo->buheader_len, + NGP_HEADER_LEN, ucon64.rom); + + if (!strncmp ((const char *) &OFFSET (ngp_header, 0), snk_code, strlen (snk_code)) || + !strncmp ((const char *) &OFFSET (ngp_header, 0), third_code, strlen (third_code))) + result = 0; + else + result = -1; + if (ucon64.console == UCON64_NGP) + result = 0; + + rominfo->header_start = NGP_HEADER_START; + rominfo->header_len = NGP_HEADER_LEN; + rominfo->header = &ngp_header; + + // internal ROM name + strncpy (rominfo->name, (const char *) &OFFSET (ngp_header, 0x24), 12); + rominfo->name[12] = 0; + + // ROM maker + rominfo->maker = + !strncmp ((const char *) &OFFSET (ngp_header, 0), snk_code, strlen (snk_code)) ? + "SNK" : "Third party"; + + // misc stuff + sprintf (buf, "Mode: %s", + (OFFSET (ngp_header, 0x23) == 0x00) ? "Mono" : + (OFFSET (ngp_header, 0x23) == 0x10) ? "Color" : + "Unknown"); + strcat (rominfo->misc, buf); + + rominfo->console_usage = ngp_usage[0].help; + rominfo->copier_usage = !rominfo->buheader_len ? pl_usage[0].help : unknown_usage[0].help; + + return result; +} diff --git a/ucon64/2.0/src/console/ngp.h b/ucon64/2.0/src/console/ngp.h new file mode 100644 index 0000000..a7bcadd --- /dev/null +++ b/ucon64/2.0/src/console/ngp.h @@ -0,0 +1,26 @@ +/* +ngp.h - NeoGeo Pocket support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef NGP_H +#define NGP_H + +extern const st_getopt2_t ngp_usage[]; +extern int ngp_init (st_rominfo_t *rominfo); +#endif diff --git a/ucon64/2.0/src/console/pce.c b/ucon64/2.0/src/console/pce.c new file mode 100644 index 0000000..49f5e75 --- /dev/null +++ b/ucon64/2.0/src/console/pce.c @@ -0,0 +1,1200 @@ +/* +pce.c - PC-Engine support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2003 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include "misc/chksum.h" +#include "misc/misc.h" +#include "misc/string.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "pce.h" +#include "backup/mgd.h" +#include "backup/msg.h" + + +#define PCENGINE_HEADER_START 0x448 +#define PCENGINE_HEADER_LEN (sizeof (st_pce_header_t)) + + +// static unsigned int pcengine_chksum (st_rominfo_t *rominfo); + +const st_getopt2_t pcengine_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "PC-Engine (CD Unit/Core Grafx(II)/Shuttle/GT/LT/Super CDROM/DUO(-R(X)))\n" + "Super Grafx/Turbo (Grafx(16)/CD/DUO/Express)"/*"1987/19XX/19XX NEC"*/, + NULL + }, + { + "pce", 0, 0, UCON64_PCE, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_PCE_SWITCH] + }, + { + "int", 0, 0, UCON64_INT, + NULL, "force ROM is in interleaved (bit-swapped) format", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "nint", 0, 0, UCON64_NINT, + NULL, "force ROM is not in interleaved (bit-swapped) format", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "msg", 0, 0, UCON64_MSG, + NULL, "convert to Magic Super Griffin/MSG", + &ucon64_wf[WF_OBJ_PCE_DEFAULT] + }, + { + "mgd", 0, 0, UCON64_MGD, + NULL, "convert to Multi Game Doctor*/MGD2/RAW", + &ucon64_wf[WF_OBJ_ALL_DEFAULT_NO_SPLIT] + }, + { + "swap", 0, 0, UCON64_SWAP, + NULL, "swap bits of all bytes in file (TurboGrafx-16 <-> PC-Engine)", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE] + }, + { + "f", 0, 0, UCON64_F, + NULL, "fix region protection", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "multi", 1, 0, UCON64_MULTI, + "SIZE", "make multi-game file for use with PCE-PRO flash card, truncated\n" + "to SIZE Mbit; file with loader must be specified first, then\n" + "all the ROMs, multi-game file to create last", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_STOP] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} +}; + +#define PCE_MAKER_MAX 86 +static const char *pce_maker[PCE_MAKER_MAX] = + { + NULL, "ACCOLADE", "AICOM", "ARTMIC", "ASK KODANSHA", + "ASMIK", "ATLUS", "AZUMA", "BIG DON", "BIGCLUB", + "BIT2", "BULLET PROOF", "CAPCOM", "CINEMAWARE", "COCONUTS", + "CREAM", "DATA EAST", "DEKA", "DISNEY", "FACE", + "FUJITV", "FUN PROJECT", "GAMES EXPRESS", "HOMEDATA", "HUDSON", + "HUDSON V1", "HUDSON V2", "HUMAN", "ICOM", "IGS", + "IMAGE", "IMAX", "INTEC", "IREM", "KANEKO", + "KONAMI", "KSS", "LASER SOFT", "LORICIEL", "MAGAE CHIP VERSION", + "MANLEY & ASSOCIATES", "MASYNYA & NCS", "MASYNYA", "MEDIA RINGS", "NAMCO", + "NATSUME", "NATSUME", "NAXAT SOFT", "NAXAT", "NCS", + "NEC / HUDSON", "NEC AVENUE & TAITO", "NEC AVENUE", "NEC HOME ELECTRONICS", "NEC", + "NHK", "NICHIBUTSU", "NIHON BUSSAN", "PACK-IN-VIDEO", "PALSOFT", + "PSYGNOSYS", "RANDOM HOUSE", "SALIO", "SEGA", "SEIBU KAIHATSU", + "SGX", "SPECTRUM HOLOBYTE", "SSL", "SUMMER PROJECT", "SUNRISE", + "SUNSOFT", "TAISANG VERSION", "TAITO 384K STYLE", "TAITO", "TAKARA", + "TECHNOS", "TELENET", "TENGEN", "THE HU62680 TEAM", "TITUS", + "TONKIN HOUSE", "UNI POST", "UPL", "VICTOR", "VIDEO SYSTEM", + "WOLF TEAM" + }; + + +typedef struct +{ + uint32_t crc32; + uint8_t maker; + const char *serial; + uint32_t date; + const char *comment; +} st_pce_data_t; + + +static const st_pce_data_t pce_data[] = +{ + {0x0038b5b5, 70, "SS90002", 100890, NULL}, + {0x00c38e69, 73, "TP02012", 60790, NULL}, + {0x00f83029, 42, NULL, 0, NULL}, + {0x0112d0c7, 42, "NCS90005", 141290, "1p"}, + {0x013a747f, 24, "HC91051", 61291, "1p"}, + {0x01a76935, 44, "NC63003", 261288, NULL}, + {0x020dc2df, 58, "PV1001", 230389, "3M"}, + {0x0243453b, 24, "HC63016", 90289, "aka MILITARY MADNESS,2p"}, + {0x0258accb, 33, NULL, 0, NULL}, + {0x02a578c5, 24, NULL, 91, "1p"}, + {0x02db6fe5, 24, NULL, 0, NULL}, + {0x02dde03e, 22, "T4955754200915", 0, NULL}, + {0x033e8c4a, 73, "TP02006", 260190, "1p"}, + {0x03883ee8, 48, "NX90003", 60490, "2p"}, + {0x03e28cff, 24, "HC62003", 281287, "1p"}, + {0x04188c5c, 83, "JC63005", 290690, "3p"}, + {0x04bf5eaf, 54, "TGX030064", 0, NULL}, + {0x05054f4f, 12, NULL, 0, NULL}, + {0x0517da65, 35, "KM91001", 151191, NULL}, + {0x05362516, 24, "HC93065", 101293, "5p"}, + {0x053a0f83, 58, NULL, 0, "aka DEEP BLUE KAITEI SHINWA,1p"}, + {0x05453628, 19, "FA02-007", 71290, NULL}, + {0x0590a156, 33, "IC02004", 60790, NULL}, + {0x05a4b72e, 24, NULL, 0, "1p (AKA BLODIA)"}, + {0x07a226fb, 54, "TGX020003", 0, "1p"}, + {0x07bc34dc, 44, "TGX040085", 0, "aka GENPEI TORAMADEN VOLUME 2,1p"}, + {0x088d896d, 54, NULL, 0, "1p"}, + {0x08a09b9a, 24, "HC90030", 270490, "aka BLUE BLINK"}, + {0x09048174, 58, "PV1001", 230389, "3M"}, + {0x09509315, 73, NULL, 0, NULL}, + {0x09a0bfcc, 73, "TP03016", 250191, "4p"}, + {0x09cbb5e6, 84, "VS-90002", 70290, NULL}, + {0x0aa88f33, 22, "T4955754200939", 0, "1p"}, + {0x0ad97b04, 57, NULL, 93, NULL}, + {0x0b7f6e5f, 42, "NCS91002", 290391, "5p"}, + {0x0be0e0a8, 24, "TGX020039", 0, "aka ADVENTURE ISLAND,1p"}, + {0x0d766139, 3, "NX91005", 61291, "1p"}, + {0x0df57c90, 44, NULL, 0, NULL}, + {0x106bb7b2, 70, "SS90003", 121090, "1p"}, + {0x109ba474, 42, "NCS91001", 270491, "aka SHOCKMAN,2p"}, + {0x10b60601, 13, NULL, 290493, "5p"}, + {0x113dd5f0, 24, "HC89019", 70789, "aka BLAZING LAZERS,1p"}, + {0x11a36745, 44, "NC63004", 110888, NULL}, + {0x12c4e6fd, 19, "FA02-007", 71290, NULL}, + {0x13bf0409, 27, "HM89002", 231289, NULL}, + {0x149d0511, 33, "HC63007", 250388, "1p (BOOTLEG TRAINER VERSION)"}, + {0x14daf737, 83, NULL, 0, NULL}, + {0x14fad3ba, 44, NULL, 0, "2p"}, + {0x1555697e, 68, "HC91050", 201291, NULL}, + {0x166a0e44, 48, "NX90004", 200790, "2p"}, + {0x1772a6bc, 73, "TP02007", 230290, NULL}, + {0x1772b229, 32, "IG90001", 0, NULL}, + {0x17a47d0d, 48, "NX90008", 71290, NULL}, + {0x17ba3032, 48, "NX89003", 131089, "1p"}, + {0x1828d2e5, 56, "NB91005", 291191, NULL}, + {0x19ff94e5, 73, "TP03021", 130392, NULL}, + {0x1a8393c6, 44, "NC63002", 150788, "1p"}, + {0x1b1a80a2, 6, "HC63015", 40389, "5p"}, + {0x1b2d0077, 57, "NB96002", 271192, "1p"}, + {0x1b5b1cb1, 56, "NB90003", 280990, NULL}, + {0x1bc36b36, 29, "AI02005", 141290, "aka SINISTRON,1p"}, + {0x1c6ff459, 43, "MR90002", 141290, "1p,linkable"}, + {0x1cad4b7f, 2, "AC89003", 11289, NULL}, + {0x1e1d0319, 73, "NAPH-1009", 0, NULL}, + {0x1e2cbcf8, 0, NULL, 0, "5p"}, + {0x1eb30eeb, 27, "HM89001", 220689, NULL}, + {0x20a7d128, 48, "NX90006", 261090, NULL}, + {0x20ef87fd, 8, "PV-2004", 230390, NULL}, + {0x21b5409c, 24, "HC91039", 210691, NULL}, + {0x231b1535, 73, NULL, 0, NULL}, + {0x23d22d63, 29, "ITGX10001", 0, "aka WORLD BEACH VOLLEYBALL,4p"}, + {0x23ec8970, 23, "HD90001", 100890, NULL}, + {0x2546efe0, 61, "NAPH-1021", 270991, NULL}, + {0x25a02bee, 19, "FA03-009", 120791, "1p"}, + {0x25be2b81, 34, "TGX040051", 0, "2p"}, + {0x25de250a, 24, "HC90030", 270490, "aka BLUE BLINK"}, + {0x25e0f6e9, 5, "AS02002", 130490, "TGX020005"}, + {0x26020c77, 24, NULL, 0, NULL}, + {0x261f1013, 24, "HC90028", 300390, "aka CHEW-MAN-FU,2p"}, + {0x2739b927, 44, NULL, 0, NULL}, + {0x2762792b, 48, NULL, 0, "2p"}, + {0x27a4d11a, 42, "NCS89003", 280389, "2p"}, + {0x283b74e0, 54, NULL, 0, NULL}, + {0x2841fd1e, 24, NULL, 0, NULL}, + {0x284ebe25, 24, NULL, 0, NULL}, + {0x29eec024, 24, "HC92058", 101092, "aka WORLD SPORTS COMPETITION,5p"}, + {0x2a3e08e2, 16, NULL, 0, NULL}, + {0x2b54cba2, 7, "HC63016", 221288, "5p"}, + {0x2b94aedc, 26, "HC91047", 270991, "1p"}, + {0x2bc023fc, 68, "HC91050", 201291, NULL}, + {0x2cb5cd55, 24, "HC89024", 151289, "1p"}, + {0x2cb796e2, 24, "HC89024", 151289, "1p"}, + {0x2cb92290, 33, "IC01002", 11289, "aka MR.HELI's BIG ADVENTURE,1p"}, + {0x2cee30ee, 54, "TGX040077", 0, NULL}, + {0x2db4c1fd, 28, "TGX040076", 0, NULL}, + {0x2df97bd0, 19, NULL, 0, NULL}, + {0x2e5ac9c0, 43, "TGX010031", 0, "1p"}, + {0x2e955051, 80, "TON90002", 140990, NULL}, + {0x2f8935aa, 24, "HC63012", 300888, "aka KEITH COURAGE IN ALPHA ZONES"}, + {0x2fd65312, 9, "BG01004", 220889, NULL}, + {0x3028f7ca, 24, "HC91043", 190791, "1p"}, + {0x30cc3563, 24, "HC91046", 90891, "2p"}, + {0x30d4bd0e, 26, NULL, 0, "1p"}, + {0x31dd1c32, 48, "NX89001", 300589, NULL}, + {0x31e2e7b6, 24, "HC91041", 50491, NULL}, + {0x320f5018, 0, "HC692", 91, "EXTRA BACKUP RAM CARD"}, + {0x3219849c, 44, "NC91005", 271291, NULL}, + {0x345f43e9, 24, "HC90031", 210990, NULL}, + {0x348022f7, 24, "TGX020014", 0, "aka KATO & KEN CHAN"}, + {0x34e089a9, 44, "NC63001", 200588, NULL}, + {0x34fd4ef2, 29, "AI02005", 141290, "aka SINISTRON,1p"}, + {0x364508da, 42, "NCS91002", 290391, "5p"}, + {0x38e2917d, 24, NULL, 0, NULL}, + {0x390710ec, 29, NULL, 0, NULL}, + {0x3920105a, 54, NULL, 0, NULL}, + {0x3aea2f8f, 29, "AI-03004", 60791, "aka TRICKY KICK"}, + {0x3b13af61, 0, "HC89026", 301189, NULL}, + {0x3b3808bd, 24, NULL, 0, "EXTRA SAVE RAM THING BY HUDSON"}, + {0x3e4eaf98, 15, "CC-01001", 81289, NULL}, + {0x3e647d8b, 24, "HC91039", 210691, NULL}, + {0x3e79734c, 33, "ICO3006", 190791, NULL}, + {0x3f982d0f, 1, "ATGX04TUTG", 0, NULL}, + {0x3f9f95a4, 54, NULL, 0, NULL}, + {0x4148fd7c, 14, "CJ92002", 130392, NULL}, + {0x428f36cd, 42, "NCS89002", 230289, "5p"}, + {0x43efc974, 48, "NX90001", 10390, "aka PSYCHOSIS,2p"}, + {0x442405d5, 42, "NCS91003", 270991, NULL}, + {0x44af9bea, 24, "TGX020027", 0, "aka DORAEMON MEIKYU DAISAKUSEN,1p"}, + {0x44e7df53, 11, "MC91002", 240591, NULL}, + {0x44f60137, 32, "IG89002", 0, NULL}, + {0x457f2bc4, 23, "HD91013", 291191, NULL}, + {0x45885afb, 73, NULL, 0, "2p"}, + {0x462256fb, 9, "BG01004", 220889, NULL}, + {0x469a0fdf, 21, "JC63002", 240389, "aka WAR OF THE DEAD,1p"}, + {0x471903c6, 5, "AS01001", 81289, NULL}, + {0x47afe6d7, 16, "TGX040037", 0, NULL}, + {0x4938b8bb, 24, "HC91041", 50491, NULL}, + {0x4a135429, 18, "TGX040066", 0, "1p"}, + {0x4a3df3ca, 44, "NC90003", 270490, NULL}, + {0x4bd38f17, 82, "UP02002", 280990, "1p"}, + {0x4c2126b0, 24, "HC91044", 220291, "1p"}, + {0x4caa6be9, 16, NULL, 0, "1p"}, + {0x4cef0456, 42, "NCS90005", 141290, "1p"}, + {0x4d344c8c, 24, NULL, 0, NULL}, + {0x4d3b0bc9, 44, "NC91001", 150391, NULL}, + {0x4d539c9f, 83, "JC63011", 20891, "1p"}, + {0x4df54b81, 54, "TGX030064", 0, NULL}, + {0x4f2844b0, 80, NULL, 0, NULL}, + {0x4f2bd39f, 71, "H54G-1004", 140789, NULL}, + {0x500472d4, 12, NULL, 0, NULL}, + {0x5157a395, 24, NULL, 0, NULL}, + {0x51e86451, 73, NULL, 0, "2p"}, + {0x52520bc6, 54, NULL, 0, NULL}, + {0x53109ae6, 24, "HC62005", 220188, NULL}, + {0x534e8808, 27, "HM92006", 131192, NULL}, + {0x53b7784b, 43, NULL, 60392, NULL}, + {0x560d2305, 27, "HM91004", 10391, NULL}, + {0x56488b36, 58, "PV-1007", 291191, NULL}, + {0x56739bc7, 45, NULL, 0, NULL}, + {0x574352c6, 50, NULL, 0, NULL}, + {0x57615647, 49, "TGX040087", 0, "2p"}, + {0x57a436a2, 1, NULL, 0, NULL}, + {0x57f183ae, 24, NULL, 89, NULL}, + {0x589d33eb, 83, NULL, 0, NULL}, + {0x595bb22a, 24, "HC63016", 90289, "2p"}, + {0x59d07314, 44, "NC64001", 210489, "1p"}, + {0x59e44f45, 24, "HS93054", 100293, "5p"}, + {0x5c4d1991, 15, "CC-01001", 81289, NULL}, + {0x5c78fee1, 20, "MC66680", 40889, "1p"}, + {0x5cdb3f5b, 70, "SS89001", 170389, "1p"}, + {0x5cf59d80, 35, "KM92004", 280292, NULL}, + {0x5d0e3105, 24, "HE-1097", 60790, "1p"}, + {0x5e4fa713, 24, NULL, 0, NULL}, + {0x5f2c9a45, 22, "T4955754200984", 0, "2p"}, + {0x6069c5e7, 24, "HC62006", 301187, "aka JJ & JEFF"}, + {0x60ecae22, 48, "NX89001", 300589, NULL}, + {0x60edf4e1, 48, "NX63001", 140988, NULL}, + {0x616ea179, 16, "DE90004", 290391, "1p"}, + {0x61a6e210, 48, NULL, 0, NULL}, + {0x61b80005, 58, "PV1003", 221289, "1p"}, + {0x6203de23, 22, NULL, 0, NULL}, + {0x625221a6, 24, "HC90034", 200790, NULL}, + {0x6257cce7, 52, NULL, 0, NULL}, + {0x62654ad5, 73, "TPO1002", 300689, NULL}, + {0x6273a9d4, 44, "TGX020018", 0, "1p"}, + {0x62ec2956, 44, "NC63004", 110888, NULL}, + {0x633a3d48, 73, "TP02010", 310590, "2p"}, + {0x637ba71d, 30, NULL, 0, "BOOTLEG"}, + {0x64301ff1, 24, "TGX040058", 0, "1p"}, + {0x64580427, 52, "H67G-1002", 91288, "1p"}, + {0x647718f9, 35, "KM92003", 210292, NULL}, + {0x65fdb863, 75, "NX90002", 300390, "aka HOT BLOOD HIGHSCHOOL DODGEBALL"}, + {0x67573bac, 24, "HC92052", 240192, NULL}, + {0x67aab7a1, 58, "PV-1005", 141290, "1p"}, + {0x67aede77, 24, NULL, 0, NULL}, + {0x67ec5ec4, 16, "DE90005", 300390, "aka DROP OFF"}, + {0x6923d736, 24, "HC62004", 301087, "1p"}, + {0x6976d5b3, 30, "1992", 0, NULL}, + {0x6a628982, 63, NULL, 0, NULL}, + {0x6b319457, 44, NULL, 0, "1p"}, + {0x6c30f0ac, 30, NULL, 0, "BOOTLEG"}, + {0x6c34aaea, 56, "NB1001", 10290, "1p"}, + {0x6cca614c, 24, "HC90040", 221290, NULL}, + {0x6e297e49, 82, "UP02002", 280990, "1p"}, + {0x6eab778c, 71, "HC63009", 30688, "1p"}, + {0x6f4fd790, 73, "TP02006", 260190, "1p"}, + {0x6fd6827c, 49, "NCS63001", 230988, NULL}, + {0x70749841, 13, NULL, 290493, "5p"}, + {0x70d90e20, 44, "TGX020019", 0, "4p"}, + {0x7146027c, 27, "HM94007", 150194, NULL}, + {0x727f4656, 1, "JC63012", 240792, NULL}, + {0x72814acb, 44, NULL, 0, NULL}, + {0x72a2c22c, 73, "TP01003", 291189, NULL}, + {0x72cb0f9d, 63, "H49G-1001", 141088, NULL}, + {0x72d6860b, 19, NULL, 0, NULL}, + {0x72e00bc4, 44, "NC92003", 250692, NULL}, + {0x73614660, 54, NULL, 0, NULL}, + {0x73e994a0, 48, "NX90001", 10390, "aka PSYCHOSIS,2p"}, + {0x740491c2, 24, "HC92053", 201192, "1p"}, + {0x7424452e, 66, "TGX040067", 0, "1p,linkable"}, + {0x745408ae, 42, "NCS89002", 230289, "5p"}, + {0x74903426, 48, "NX91003", 120491, NULL}, + {0x756a1802, 24, NULL, 0, NULL}, + {0x76164593, 73, "TP02014", 141290, NULL}, + {0x7632db90, 2, "AC89001", 200389, NULL}, + {0x767245cd, 24, "TGX020008", 0, "aka THE KUNG FU,1p"}, + {0x775bd3e1, 73, "TPO3019", 200991, NULL}, + {0x786d9bbd, 73, NULL, 0, "2p"}, + {0x79362389, 12, "NX91002", 220391, "2p"}, + {0x7aa9d4dc, 24, "TGX030010", 0, "aka GUNHED,1p"}, + {0x7acb60c8, 73, "TP03019", 200991, "4p"}, + {0x7b96317c, 24, "HC90034", 200790, NULL}, + {0x7d3e6f33, 24, NULL, 0, NULL}, + {0x7d48d2fc, 73, "TP02015", 180191, "2p"}, + {0x805a34b9, 83, "JC63013", 290493, NULL}, + {0x80c3f824, 44, "NC62001", 0, "aka GHOST TRAVEL STORY"}, + {0x82ae3b16, 12, "JC63004", 230290, "aka TIGER ROAD"}, + {0x82def9ee, 36, "NV91001", 131291, NULL}, + {0x83213ade, 48, "NX90005", 280990, NULL}, + {0x8420b12b, 24, "HC92056", 100792, NULL}, + {0x850829f2, 64, "HC91049", 221191, "1p"}, + {0x85101c20, 52, "NAPH-1007", 220690, NULL}, + {0x854c37b3, 44, "NC89003", 70789, "2p"}, + {0x85a1e7b6, 27, "HM90003", 270490, NULL}, + {0x85aa49d0, 29, "ITGX10007", 0, "aka VIOLENT SOLDIER"}, + {0x85b85ff9, 73, "TP02012", 60790, NULL}, + {0x85cc9b60, 45, NULL, 0, NULL}, + {0x86087b39, 42, "NCS89006", 290989, "2p"}, + {0x8793758c, 44, "NC92002", 70492, "aka Samurai Ghost,1p"}, + {0x87fd22ad, 24, "HC90036", 71290, "5p,linkable"}, + {0x88796264, 24, "TGX020035", 0, "aka BE BALL,2p"}, + {0x8a046cdc, 44, "NC92003", 250692, NULL}, + {0x8aa4b220, 24, "HC90037", 100890, "2p"}, + {0x8acfc8aa, 0, NULL, 0, "2p"}, + {0x8bf34ffa, 24, NULL, 0, NULL}, + {0x8c4588e2, 24, "HC91048", 230891, "2p"}, + {0x8c565cb6, 44, "NC89004", 80989, "1p"}, + {0x8dc0d85f, 73, "TP02015", 180191, "2p"}, + {0x8def5aa1, 24, "HC93062", 250693, NULL}, + {0x8e25dc77, 73, "TP02009", 270390, NULL}, + {0x8e4d75a8, 73, "TP02007", 230290, NULL}, + {0x8e71d4f3, 24, NULL, 0, "aka DRAGON'S CURSE,1p"}, + {0x8f02fd20, 24, "HC89018", 250589, NULL}, + {0x8f4d9f94, 31, "IM92001", 0, "aka THE LOST SUNHEART"}, + {0x90ed6575, 27, "HM89001", 220689, NULL}, + {0x9107bcc8, 52, NULL, 0, NULL}, + {0x91e6896f, 19, NULL, 261090, "FA02-005,1p"}, + {0x92521f34, 54, "TGX040062", 0, "1p"}, + {0x92c919ea, 70, "SS90003", 121090, "1p"}, + {0x93f05168, 24, NULL, 0, NULL}, + {0x93f316f7, 24, "TGX030015", 0, "aka NECTARIS,2p"}, + {0x94c55627, 73, "TP01005", 271289, "1p"}, + {0x951aa310, 24, NULL, 0, NULL}, + {0x951ed380, 44, "NC92001", 100192, "1p"}, + {0x958bcd09, 24, "HC90027", 230390, "1p"}, + {0x95f90dec, 41, NULL, 90, NULL}, + {0x965c95b3, 73, "TP02011", 290690, "1p"}, + {0x968770f6, 71, "HC90028", 300390, "aka CHEW-MAN-FU,2p"}, + {0x968d908a, 83, "JC63008", 290391, NULL}, + {0x9693d259, 60, "CJ0001", 131291, "2p"}, + {0x96e0cd9d, 73, "TP01002", 300689, NULL}, + {0x9759a20d, 54, NULL, 0, NULL}, + {0x97c5ee9a, 83, "JC63009", 141290, NULL}, + {0x9893e0e6, 48, "NX90001", 10390, "aka PSYCHOSIS,2p"}, + {0x9897fa86, 19, "FA02-006", 70990, NULL}, + {0x98b03ec9, 0, NULL, 0, "CRACKED TO WORK ON JAP PC-E"}, + {0x99033916, 43, "MR92005", 60392, NULL}, + {0x9913a9de, 24, "TGX080097", 0, "2p"}, + {0x99496db3, 73, "TP02008", 20390, "1p"}, + {0x99f2865c, 24, "HC89025", 250590, "1p"}, + {0x99f7a572, 37, "TJ03002", 290391, "1p,linkable"}, + {0x9a41c638, 13, NULL, 0, "5p"}, + {0x9abb4d1f, 24, "HC90036", 71290, "5p,linkable"}, + {0x9b5ebc58, 16, "DE64001", 30389, "4p"}, + {0x9bb8d362, 78, NULL, 0, NULL}, + {0x9c49ef11, 26, "HC89022", 171189, "1p"}, + {0x9c7a8ee4, 29, NULL, 0, "aka TRICKY"}, + {0x9d1a0f5a, 25, "HC89022", 171189, "1p"}, + {0x9e86ffb0, 42, "NCS89006", 290989, "2p"}, + {0x9ec6fc6c, 22, NULL, 0, NULL}, + {0x9edc0aea, 73, "TPO2014", 141290, NULL}, + {0x9fb4de48, 33, "ICO3006", 190791, NULL}, + {0xa019b724, 44, NULL, 0, NULL}, + {0xa0c97557, 29, "AI-02001", 90390, "1p"}, + {0xa15a1f37, 44, "NC90008", 111290, "1p"}, + {0xa170b60e, 24, "HC93063", 20493, "2p"}, + {0xa17d4d7e, 24, "HC89019", 70789, "aka BLAZING LAZERS,1p"}, + {0xa2a0776e, 84, "VS-89001", 190689, "1p"}, + {0xa32430d5, 55, "NV92001", 310192, NULL}, + {0xa326334a, 62, "SL01001", 221189, "1p"}, + {0xa3303978, 44, "NC90006", 90990, "1p"}, + {0xa5290dd0, 43, "MR91004", 131291, NULL}, + {0xa586d190, 73, NULL, 0, NULL}, + {0xa594fac0, 80, "TON90003", 121090, NULL}, + {0xa6088275, 73, NULL, 0, NULL}, + {0xa6539306, 80, "TON90004", 91190, NULL}, + {0xa71d70d0, 42, "NCS90001", 260190, NULL}, + {0xa80c565f, 33, "IC02003", 270790, NULL}, + {0xa9084d6e, 42, "NCS89004", 0, NULL}, + {0xa98d276a, 29, "TGX030030", 0, "1p"}, + {0xa9a94e1b, 25, NULL, 0, "1p"}, + {0xa9ab2954, 44, NULL, 0, NULL}, + {0xa9fab7d2, 40, "TGX040069", 0, "1p"}, + {0xab3c5804, 24, "HE-1097", 60790, "1p"}, + {0xad226f30, 73, "TP01005", 271289, "1p"}, + {0xad450dfc, 53, NULL, 0, NULL}, + {0xad6e0376, 83, NULL, 0, "3p"}, + {0xae26f30f, 24, "TGX060078", 0, "1p"}, + {0xae9fe1aa, 33, "TGX040050", 0, NULL}, + {0xaf2dd2af, 24, "HC91045", 50791, "1p"}, + {0xb01ee703, 24, "HC92059", 250992, NULL}, + {0xb01f70c2, 16, "DE89003", 10989, "1p"}, + {0xb0ba689f, 51, NULL, 0, NULL}, + {0xb101b333, 48, "XX-XX-90", 0, NULL}, + {0xb122787b, 24, "HC63010", 80788, NULL}, + {0xb18d102d, 48, "NX91003", 120491, NULL}, + {0xb24e6504, 28, "TGX040054", 0, NULL}, + {0xb268f2a2, 56, NULL, 92, NULL}, + {0xb2ef558d, 73, NULL, 0, NULL}, + {0xb300c5d0, 24, "HC92061", 111292, "5p,linkable"}, + {0xb3eeea2e, 44, "NC91004", 181091, "1p"}, + {0xb486a8ed, 12, "NAPH-1008", 270790, "1p"}, + {0xb4d29e3b, 48, "NX91004", 291191, "1p"}, + {0xb5326b16, 80, NULL, 0, NULL}, + {0xb54debd1, 69, "TGX020001", 0, "aka MAJIN EIYU WATARU,1p"}, + {0xb552c906, 54, "TGX020008", 0, "1p"}, + {0xb630ab25, 24, "HC89024", 151289, "1p"}, + {0xb64de6fd, 48, NULL, 0, NULL}, + {0xb74ec562, 17, "DE90006", 80191, NULL}, + {0xb77f2e2f, 43, NULL, 0, NULL}, + {0xb866d282, 22, "T4955754200946", 0, NULL}, + {0xb926c682, 44, "NC90001", 160390, "1p"}, + {0xb9899178, 44, "NC90007", 280990, NULL}, + {0xb99a85b6, 64, "HC91049", 221191, "1p"}, + {0xb9dfc085, 27, "HM89002", 231289, NULL}, + {0xba4d0dd4, 73, "TP03018", 90891, NULL}, + {0xbb3ca04a, 3, "NX91005", 61291, "1p"}, + {0xbb654d1c, 24, "HC92057", 70892, "2p"}, + {0xbb761f3b, 19, "FA02-006", 70990, NULL}, + {0xbc655cf3, 5, "AS01001", 81289, NULL}, + {0xbe62eef5, 16, "DE89002", 10489, NULL}, + {0xbe850530, 29, NULL, 270790, "aka SONIC SPIKE"}, + {0xbe8b6e3b, 58, "PV-1008", 0, NULL}, + {0xbe990010, 48, NULL, 0, NULL}, + {0xbf3e2cc7, 19, "FA64-0001", 10389, NULL}, + {0xbf52788e, 83, "JC63006", 300390, NULL}, + {0xbf797067, 12, "JC63004", 230290, "aka TIGER ROAD"}, + {0xc02b1b59, 34, NULL, 0, NULL}, + {0xc0905ca9, 74, "HC63008", 220488, "aka VICTORY LIFE,5p"}, + {0xc0af0947, 42, NULL, 0, NULL}, + {0xc150637a, 0, "BG01003", 280689, NULL}, + {0xc159761b, 40, NULL, 0, NULL}, + {0xc2287894, 58, NULL, 0, NULL}, + {0xc267e25d, 73, "TP01003", 291189, NULL}, + {0xc28b0d8a, 33, "IC03005", 130391, "2p"}, + {0xc3212c24, 81, "PJ91001", 60491, NULL}, + {0xc356216b, 2, NULL, 0, "2p"}, + {0xc42b6d76, 70, "SS89002", 221289, "1p"}, + {0xc4eb68a5, 44, "NC63004", 110888, NULL}, + {0xc5fdfa89, 24, "HC89020", 80889, "2p"}, + {0xc614116c, 58, "PV1003", 221289, "1p"}, + {0xc6f764ec, 22, "T4955754200922", 0, "1p"}, + {0xc6fa6373, 24, "HC90032", 180191, "1p"}, + {0xc7327632, 42, "NCS90002", 230290, NULL}, + {0xc74ffbc9, 77, "TG90001", 100890, "1p"}, + {0xc7847df7, 24, NULL, 0, NULL}, + {0xc81d0371, 48, NULL, 0, "aka PARANOIA"}, + {0xc8a412e1, 33, NULL, 0, "1p"}, + {0xc8c084e3, 44, "NC89003", 70789, "2p"}, + {0xc8c7d63e, 63, "HG8G-1006", 290989, "aka ALTERED BEAST,1p"}, + {0xc90971ba, 44, "NC90007", 280990, NULL}, + {0xc9d7426a, 48, "NX89002", 100889, "4p"}, + {0xca12afba, 2, "AC90001", 211290, "1p"}, + {0xca68ff21, 42, "NCS89005", 190489, NULL}, + {0xca72a828, 63, "NAPH-1011", 280990, NULL}, + {0xcaad79ce, 73, "TP01004", 221289, NULL}, + {0xcab21b2e, 10, "NX89004", 260190, "1p"}, + {0xcacc06fb, 6, "BG01003", 280689, "aka LEGENDARY AXE II"}, + {0xcae1f5db, 67, "TGX040072", 0, "1p"}, + {0xcc799d92, 11, "MC91002", 240591, NULL}, + {0xcc7d3eeb, 73, NULL, 0, NULL}, + {0xce2e4f9f, 84, "VS-90002", 70290, NULL}, + {0xcec3d28a, 33, "HC63007", 250388, "1p"}, + {0xcf73d8fc, 24, "HC89017", 270489, "1p"}, + {0xcfc5a395, 73, "TP02013", 30890, "aka TOUR OF HELL"}, + {0xcfcfc7be, 44, "NC89003", 70789, "2p"}, + {0xcfec1d6a, 19, "TGX040090", 0, NULL}, + {0xd0c250ca, 19, "FA01-002", 230689, NULL}, + {0xd15cb6bb, 12, "HE93002", 120693, "2p"}, + {0xd20f382f, 79, "NX91001", 150391, NULL}, + {0xd233c05a, 48, "NX90008", 71290, NULL}, + {0xd329cf9a, 24, "HC89020", 80889, "2p"}, + {0xd3fd6971, 24, "HC90037", 100890, "2p"}, + {0xd4c5af46, 6, NULL, 0, NULL}, + {0xd50ff730, 52, "H54G-1005", 250889, NULL}, + {0xd5c782f2, 24, NULL, 0, "1p"}, + {0xd5ce2d5f, 24, "HC89021", 150989, NULL}, + {0xd634d931, 54, NULL, 0, NULL}, + {0xd6fc51ce, 22, "T4955754200984", 0, "2p"}, + {0xd7921df2, 52, "H54G-1003", 270189, NULL}, + {0xd7cfd70f, 56, "NB91004", 120791, NULL}, + {0xd8373de6, 84, "VS-90003", 191090, NULL}, + {0xd9e1549a, 24, "HC90040", 221290, NULL}, + {0xda059c9b, 83, "JC63008", 290391, NULL}, + {0xdb872a64, 77, "TTGX20001", 0, "1p"}, + {0xdc268242, 54, "TGX020008", 0, "1p"}, + {0xdc47434b, 29, "AI-02002", 20690, "1p"}, + {0xdc760a07, 24, "HC89023", 311089, "aka CRATERMAZE,1p"}, + {0xdca24a76, 63, "H49G-1001", 141088, NULL}, + {0xdcd3e602, 19, "FA01-002", 230689, NULL}, + {0xdcf3675c, 44, NULL, 261288, NULL}, + {0xdd0ebf8c, 59, "PL91001", 151191, "1p"}, + {0xdd175efd, 82, "UP01001", 190190, NULL}, + {0xdd1d1035, 42, "NCS89005", 190489, NULL}, + {0xdd35451d, 54, NULL, 0, NULL}, + {0xddc3e809, 63, "NAPH-1015", 71290, "1p"}, + {0xde963b7e, 39, "H54G-1004", 140789, NULL}, + {0xdf10c895, 82, NULL, 0, NULL}, + {0xdf804dc7, 70, "SS90001", 20390, "1p"}, + {0xdfd4593a, 33, "IC04007", 21092, "1p"}, + {0xe14dee08, 56, "NB89002", 140990, NULL}, + {0xe1a73797, 70, NULL, 0, NULL}, + {0xe203f223, 63, "NAPH-1016", 211290, NULL}, + {0xe3284ba7, 24, "TGX040084", 0, "1p"}, + {0xe4124fe0, 33, NULL, 0, "1p"}, + {0xe415ea19, 24, "TGX040080", 0, "1p"}, + {0xe439f299, 24, "HC90028", 300390, "aka CHEW-MAN-FU,2p"}, + {0xe4b3415c, 83, "JC63006", 300390, NULL}, + {0xe5b6b3e5, 22, "T4955754200977", 0, NULL}, + {0xe5e7b8b7, 12, "H54G-1004", 140789, NULL}, + {0xe6458212, 73, NULL, 0, "2p"}, + {0xe6c93ae7, 47, "NX90007", 211290, NULL}, + {0xe6ee1468, 58, "PV1004", 270790, NULL}, + {0xe6f16616, 54, "PCE-SC1", 0, NULL}, + {0xe749a22c, 72, "TP03021", 130392, NULL}, + {0xe7529890, 83, "JC63013", 290493, NULL}, + {0xe84890a5, 73, NULL, 0, NULL}, + {0xe8702d51, 33, "IC04007", 21092, "1p"}, + {0xe87190f1, 6, "AT03001", 41091, "aka SOMER ASSAULT,1p"}, + {0xe88987bb, 27, "HM91005", 300891, NULL}, + {0xea324f07, 24, "HC89018", 250589, NULL}, + {0xeb4e600b, 33, "IC63001", 140189, "1p"}, + {0xeb923de5, 73, "TP01004", 221289, NULL}, + {0xeb9452f0, 42, NULL, 0, NULL}, + {0xed3a71f8, 24, "TGX040079", 0, "1p"}, + {0xeda32d95, 54, "TGX040056", 0, "1p"}, + {0xee156721, 43, "MR91003", 80391, "1p"}, + {0xeeb6dd43, 80, NULL, 0, NULL}, + {0xeecfa5fd, 38, "TGX040061", 0, "4p"}, + {0xf0227837, 6, "TGX040089", 0, "aka MESOPOTAMIA"}, + {0xf022be13, 2, "AC89002", 280789, NULL}, + {0xf0ab946e, 25, "HC91047", 270991, "1p"}, + {0xf0cc3363, 84, NULL, 0, NULL}, + {0xf207ecae, 33, "HC63009", 30688, "1p"}, + {0xf2e46d25, 4, "AK91002", 221191, NULL}, + {0xf2e6856d, 22, NULL, 0, NULL}, + {0xf370b58e, 54, "TGX040060", 0, NULL}, + {0xf4148600, 19, "FA02-004", 290690, "1p"}, + {0xf42aa73e, 73, "TP02010", 310590, "2p"}, + {0xf45afbca, 73, NULL, 0, NULL}, + {0xf46298e3, 83, "JC63005", 290690, "3p"}, + {0xf5b90d55, 19, "FA01-003", 241189, "1p"}, + {0xf70112e5, 32, "IG89001", 11189, NULL}, + {0xf79657dd, 24, "HC63015", 40389, "5p"}, + {0xf860455c, 68, "HC92060", 41292, NULL}, + {0xf8861456, 70, "SS90002", 100890, NULL}, + {0xf8f85eec, 44, "NC90004", 290690, "1p"}, + {0xf91b055f, 70, "SS90001", 20390, "1p"}, + {0xf999356f, 24, "HC63014", 181188, NULL}, + {0xfaa6e187, 76, NULL, 0, NULL}, + {0xfae0fc60, 67, "TGX040072", 0, "1p"}, + {0xfaecce20, 35, "KM91002", 61291, "2p"}, + {0xfb0fdcfe, 4, "AK90001", 200490, NULL}, + {0xfb37ddc4, 43, NULL, 0, "aka SOHKO BAN WORLD"}, + {0xfba3a1a4, 71, "NX90006", 261090, NULL}, + {0xfde08d6d, 48, "NX91002", 220391, "2p"}, + {0xff898f87, 52, "NAPH-1010", 310890, NULL}, + {0xffd92458, 22, "T4955754200953", 0, "1p"} +}; + + +typedef struct st_pce_header +{ + char pad[48]; +} st_pce_header_t; + +st_pce_header_t pce_header; + + +static int +pce_compare (const void *key, const void *found) +{ + /* + The return statement looks overly complicated, but is really necessary. + This construct: + return ((st_pce_data_t *) key)->crc32 - ((st_pce_data_t *) found)->crc32; + does *not* work correctly for all cases. + */ + return (int) (((int64_t) ((st_pce_data_t *) key)->crc32 - + (int64_t) ((st_pce_data_t *) found)->crc32) / 2); +} + + +static void +swapbits (unsigned char *buffer, int size) +{ + int n, byte, bit; + + for (n = 0; n < size; n++) + { + byte = 0; + for (bit = 0; bit < 8; bit++) + if (buffer[n] & (1 << bit)) + byte |= 1 << (7 - bit); + buffer[n] = byte; + } +} + + +// header format is specified in src/backup/ffe.h +int +pcengine_msg (st_rominfo_t *rominfo) +{ + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + unsigned char *rom_buffer = NULL; + st_msg_header_t header; + int size = ucon64.file_size - rominfo->buheader_len; + + if (rominfo->interleaved) + if (!(rom_buffer = (unsigned char *) malloc (size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size); + return -1; + } + + memset (&header, 0, MSG_HEADER_LEN); + header.size = size / 8192; + header.emulation = size == 3 * MBIT ? 1 : 0; + header.id1 = 0xaa; + header.id2 = 0xbb; + header.type = 2; + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".msg"); + ucon64_file_handler (dest_name, src_name, 0); + + ucon64_fwrite (&header, 0, MSG_HEADER_LEN, dest_name, "wb"); + if (rominfo->interleaved) + { + // Magic Super Griffin files should not be "interleaved" + ucon64_fread (rom_buffer, rominfo->buheader_len, size, src_name); + swapbits (rom_buffer, size); + ucon64_fwrite (rom_buffer, MSG_HEADER_LEN, size, dest_name, "ab"); + free (rom_buffer); + } + else + fcopy (src_name, rominfo->buheader_len, size, dest_name, "ab"); + + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return 0; +} + + +// see src/backup/mgd.h for the file naming scheme +int +pcengine_mgd (st_rominfo_t *rominfo) +{ + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + unsigned char *rom_buffer = NULL; + int size = ucon64.file_size - rominfo->buheader_len; + + if (!rominfo->interleaved) + if (!(rom_buffer = (unsigned char *) malloc (size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size); + return -1; + } + + strcpy (src_name, ucon64.rom); + mgd_make_name (ucon64.rom, UCON64_PCE, size, dest_name); + ucon64_file_handler (dest_name, src_name, OF_FORCE_BASENAME); + + // bit-swapping images for the MGD2 only makes sense for owners of a TG-16 + // (American version of the PCE) + if (!rominfo->interleaved) + { + ucon64_fread (rom_buffer, rominfo->buheader_len, size, src_name); + swapbits (rom_buffer, size); + ucon64_fwrite (rom_buffer, 0, size, dest_name, "wb"); + free (rom_buffer); + } + else + fcopy (src_name, rominfo->buheader_len, size, dest_name, "wb"); + + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + + mgd_write_index_file ((char *) basename2 (dest_name), 1); + return 0; +} + + +int +pcengine_swap (st_rominfo_t *rominfo) +{ + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + unsigned char *rom_buffer; + int size = ucon64.file_size - rominfo->buheader_len; + + if (!(rom_buffer = (unsigned char *) malloc (size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size); + return -1; + } + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, src_name, 0); + + if (rominfo->buheader_len) // copy header (if present) + fcopy (src_name, 0, rominfo->buheader_len, dest_name, "wb"); + + ucon64_fread (rom_buffer, rominfo->buheader_len, size, src_name); + swapbits (rom_buffer, size); + ucon64_fwrite (rom_buffer, rominfo->buheader_len, size, dest_name, + rominfo->buheader_len ? "ab" : "wb"); + free (rom_buffer); + + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return 0; +} + + +int +pcengine_f (st_rominfo_t *rominfo) +/* + Region protection codes are found in (American) TurboGrafx-16 games. It + prevents those games from running on a PC-Engine. One search pattern seems + sufficient to fix/crack all TG-16 games. In addition to that, the protection + code appears to be always somewhere in the first 32 kB. +*/ +{ + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX], buffer[32 * 1024]; + int bytesread, n; + + puts ("Attempting to fix region protection code..."); + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, src_name, 0); + fcopy (src_name, 0, ucon64.file_size, dest_name, "wb"); // no copy if one file + + if ((bytesread = ucon64_fread (buffer, rominfo->buheader_len, 32 * 1024, src_name)) <= 0) + return -1; + + // '!' == ASCII 33 (\x21), '*' == 42 (\x2a) + if (rominfo->interleaved) + n = change_mem (buffer, bytesread, "\x94\x02\x0f", 3, '*', '!', "\x01", 1, 0); + else + n = change_mem (buffer, bytesread, "\x29\x40\xf0", 3, '*', '!', "\x80", 1, 0); + + ucon64_fwrite (buffer, rominfo->buheader_len, 32 * 1024, dest_name, "r+b"); + + printf ("Found %d pattern%s\n", n, n != 1 ? "s" : ""); + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return n; +} + + +static void +write_game_table_entry (FILE *destfile, int file_no, int totalsize) +{ + int n; + unsigned char name[0x1c]; + const char *p; + + fseek (destfile, 0xb000 + (file_no - 1) * 0x20, SEEK_SET); + fputc (0xff, destfile); // 0x0 = 0xff + + memset (name, ' ', 0x1c); + p = basename2 (ucon64.rom); + n = strlen (p); + if (n > 0x1c) + n = 0x1c; + memcpy (name, p, n); + for (n = 0; n < 0x1c; n++) + { + if (!isprint ((int) name[n])) + name[n] = '.'; + else + name[n] = toupper (name[n]); // loader only supports upper case characters + } + fwrite (name, 1, 0x1c, destfile); // 0x1 - 0x1c = name + fputc (0, destfile); // 0x1d = 0 + fputc (totalsize / MBIT, destfile); // 0x1e = bank code + fputc (0, destfile); // 0x1f = flags (x, D (reserved), x, x, x, x, x, x) +} + + +int +pcengine_multi (int truncate_size, char *fname) +{ +#define BUFSIZE (32 * 1024) + int n, n_files, file_no, bytestowrite, byteswritten, totalsize = 0, done, + truncated = 0, paddedsize, org_do_not_calc_crc = ucon64.do_not_calc_crc; + struct stat fstate; + FILE *srcfile, *destfile; + char destname[FILENAME_MAX]; + unsigned char buffer[BUFSIZE]; + + if (truncate_size == 0) + { + fprintf (stderr, "ERROR: Can't make multi-game file of 0 bytes\n"); + return -1; + } + + if (fname != NULL) + { + strcpy (destname, fname); + n_files = ucon64.argc; + } + else + { + strcpy (destname, ucon64.argv[ucon64.argc - 1]); + n_files = ucon64.argc - 1; + } + + ucon64_file_handler (destname, NULL, OF_FORCE_BASENAME); + if ((destfile = fopen (destname, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], destname); + return -1; + } + + printf ("Creating multi-game file for PCE-PRO: %s\n", destname); + + file_no = 0; + for (n = 1; n < n_files; n++) + { + if (access (ucon64.argv[n], F_OK)) + continue; // "file" does not exist (option) + stat (ucon64.argv[n], &fstate); + if (!S_ISREG (fstate.st_mode)) + continue; + if (file_no == 32) // loader + 31 games + { + printf ("WARNING: A multi-game file can contain a maximum of 31 games. The other files\n" + " are ignored.\n"); + break; + } + + ucon64.console = UCON64_UNKNOWN; + ucon64.rom = ucon64.argv[n]; + ucon64.file_size = fsizeof (ucon64.rom); + // DON'T use fstate.st_size, because file could be compressed + ucon64.rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? + ucon64.buheader_len : 0; + ucon64.rominfo->interleaved = UCON64_ISSET (ucon64.interleaved) ? + ucon64.interleaved : 0; + ucon64.do_not_calc_crc = 1; + if (pcengine_init (ucon64.rominfo) != 0) + printf ("WARNING: %s does not appear to be a PC-Engine ROM\n", ucon64.rom); + + if ((srcfile = fopen (ucon64.rom, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], ucon64.rom); + continue; + } + if (ucon64.rominfo->buheader_len) + fseek (srcfile, ucon64.rominfo->buheader_len, SEEK_SET); + + if (file_no == 0) + { + printf ("Loader: %s\n", ucon64.rom); + if (ucon64.file_size - ucon64.rominfo->buheader_len != 7 * 8192) + printf ("WARNING: Are you sure %s is a loader binary?\n", ucon64.rom); + } + else + { + printf ("ROM%d: %s\n", file_no, ucon64.rom); + write_game_table_entry (destfile, file_no, totalsize); + fseek (destfile, totalsize, SEEK_SET); // restore file pointer + } + file_no++; + + done = 0; + byteswritten = 0; // # of bytes written per file + while (!done) + { + bytestowrite = fread (buffer, 1, BUFSIZE, srcfile); + if (totalsize + bytestowrite > truncate_size) + { + bytestowrite = truncate_size - totalsize; + done = 1; + truncated = 1; + printf ("Output file is %d Mbit, truncating %s, skipping %d bytes\n", + truncate_size / MBIT, ucon64.rom, ucon64.file_size - + ucon64.rominfo->buheader_len - (byteswritten + bytestowrite)); + } + totalsize += bytestowrite; + if (bytestowrite == 0) + done = 1; + fwrite (buffer, 1, bytestowrite, destfile); + byteswritten += bytestowrite; + } + fclose (srcfile); + if (truncated) + break; + + // games have to be aligned to (start at) a 1 Mbit boundary + paddedsize = (totalsize + MBIT - 1) & ~(MBIT - 1); +// printf ("paddedsize: %d (%f); totalsize: %d (%f)\n", +// paddedsize, paddedsize / (1.0 * MBIT), totalsize, totalsize / (1.0 * MBIT)); + if (paddedsize > totalsize) + { + if (paddedsize > truncate_size) + { + truncated = 1; // not *really* truncated + paddedsize = truncate_size; + } + + memset (buffer, 0, BUFSIZE); + while (totalsize < paddedsize) + { + bytestowrite = paddedsize - totalsize > BUFSIZE ? + BUFSIZE : paddedsize - totalsize; + fwrite (buffer, 1, bytestowrite, destfile); + totalsize += bytestowrite; + } + } + if (truncated) + break; + } + // fill the next game table entry + fseek (destfile, 0xb000 + (file_no - 1) * 0x20, SEEK_SET); + fputc (0, destfile); // indicate no next game + fclose (destfile); + ucon64.console = UCON64_PCE; + ucon64.do_not_calc_crc = org_do_not_calc_crc; + + return 0; +} + + +static int +pcengine_check (unsigned char *buf, unsigned int len) +// This function was contributed by Cowering. Comments are his unless stated +// otherwise. +{ + int i, f1 = 0, f2 = 0, f3 = 0, f4 = 0, f5 = 0; + + // don't crash for too small (non-PCE) files - dbjh + if (len < 0x200) + return 0; + + // Directly look for swapped dead moon (U). Lots of zeroes at file start + // fakes out header detector. + // We probably don't need this (no "header detector") - dbjh + if (buf[0] == 0x1e && buf[1] == 0x2a && buf[2] == 0x95) + return 1; + if (buf[0] == 0x12 && buf[1] == 0xe0 && buf[16] == 0x4a) // F-1 Pilot + return 1; + if (buf[0] == 0x20 && buf[1] == 0x81 && buf[16] == 0x81) // Cyber Knight + return 1; + if (buf[0] == 0x03 && buf[1] == 0x03 && buf[16] == 0x02) // Gaia no monsho + return 1; +#if 0 // no reason to not recognise this dump - dbjh + if (len == 0x40400) // BIOS with 2 headers on front, deleted from database now + return 0; +#endif +/* + // two headers on a CD-ROM BIOS (bad dump) + if (len == 0x40200 && buf[0x200] == 0x32 && buf[0x210] == 0xa0 && buf[0x220] == 0xf7) + return 1; +*/ + // super mario pirate conversion + if (len > 65000 && !strncmp ((const char*) &buf[0x77f0], "JGGKGKGGGGGGJGJG", 16)) + return 1; + if (buf[0x20] == 0x7c && buf[0x21] == 0xe3 && buf[0x22] == 0xe6) // 5 in 1 + return 1; + if (buf[0x10] == 0xc7 && buf[0x30] == 0xe9 && buf[0x50] == 0xa1) // Sekikehara + return 1; + if (!strncmp ((const char *) &buf[0], "!BM FORMAT!", 11)) // boxy boy (U) + return 1; + if (!strncmp ((const char *) &buf[0], "PUSH RUN BUTTON", 15)) // boxy boy (J) + return 1; + // US CD sys 2.01 + if (len > 0x3ffc7 && !strncmp ((const char *) &buf[0x3ffb6], "PC Engine CD-ROM", 16)) + return 1; + + for (i = 0 ; i < 0x200; i++) + { + if (buf[i] == 0x00) + f1++; + if (buf[i] == 0xff) + f4++; + if (buf[i] == 0x78) + f2 = 1; + if (buf[i] == 0xa9) + f3 = 1; + if (buf[i] == 0x85 || buf[i] == 0x8d) + f5 = 1; + } + if ((f4 > 200) || ((f1 > 200) && (f2 + f3 != 2))) + return 0; + if (f2 + f3 + f5 >= 2) + return 1; + return 0; +} + + +int +pcengine_init (st_rominfo_t *rominfo) +{ + int result = -1, size, swapped, x; + unsigned char *rom_buffer; + st_pce_data_t *info, key; + + x = ucon64.file_size % (16 * 1024); + rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? + ucon64.buheader_len : (ucon64.file_size > x ? x : 0); + + size = ucon64.file_size - rominfo->buheader_len; + if (!(rom_buffer = (unsigned char *) malloc (size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size); + return -1; + } + ucon64_fread (rom_buffer, rominfo->buheader_len, size, ucon64.rom); + + if (pcengine_check (rom_buffer, size) == 1) + result = 0; + + swapped = -1; + /* + 29 40 f0 => unhacked + 29 40 80 => U.S.A. -> Jap hack + 94 02 0f => bit-swapped and unhacked + 94 02 01 => bit-swapped and hacked + + According to Cowering 2 or 3 games don't use these opcodes to check if a + Japanese game is running on an American console. If they are present then + it is in the first 500 (or so) bytes. + The check for "A..Z" is a fix for Puzznic (J). The check for "HESM" a fix + for: + Fire Pro Wrestling - 2nd Bout Sounds + Fire Pro Wrestling 3 - Legend Bout Sounds + */ + x = size > 32768 ? 32768 : size; + if ((memmem2 (rom_buffer, x, "\x94\x02\x0f", 3, 0) || + memmem2 (rom_buffer, x, "\x94\x02\x01", 3, 0)) && + memmem2 (rom_buffer, x, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26, 0) == 0 && + memcmp (rom_buffer, "HESM", 4)) + swapped = 1; + if (UCON64_ISSET (ucon64.interleaved)) + swapped = ucon64.interleaved; + + if ((result == -1 && swapped != 0) || swapped == 1) + { // don't swap the bits if -nint is specified + if (!UCON64_ISSET (ucon64.do_not_calc_crc) || swapped == 1) + ucon64.fcrc32 = crc32 (0, rom_buffer, size); + swapbits (rom_buffer, size); + if (pcengine_check (rom_buffer, size) == 1) + { + swapped = 1; + result = 0; + } + if (swapped != 1) + { + ucon64.crc32 = ucon64.fcrc32; + ucon64.fcrc32 = 0; + } + } + if (swapped != -1) + rominfo->interleaved = swapped; + + if (ucon64.console == UCON64_PCE) + result = 0; + + rominfo->header_start = PCENGINE_HEADER_START; + rominfo->header_len = PCENGINE_HEADER_LEN; + rominfo->console_usage = pcengine_usage[0].help; + rominfo->copier_usage = rominfo->buheader_len ? msg_usage[0].help : mgd_usage[0].help; + + if (!UCON64_ISSET (ucon64.do_not_calc_crc) && result == 0) + { + if (!ucon64.crc32) + ucon64.crc32 = crc32 (0, rom_buffer, size); + // additional info + key.crc32 = ucon64.crc32; + info = (st_pce_data_t *) bsearch (&key, pce_data, + sizeof pce_data / sizeof (st_pce_data_t), + sizeof (st_pce_data_t), pce_compare); + if (info) + { + if (info->maker) + rominfo->maker = NULL_TO_UNKNOWN_S (pce_maker[MIN (info->maker, + PCE_MAKER_MAX - 1)]); + + if (info->serial) + if (info->serial[0]) + { + strcat (rominfo->misc, "\nSerial: "); + strcat (rominfo->misc, info->serial); + } + + if (info->date) + { + int day = info->date / 10000, month = (info->date % 10000) / 100, + year = info->date % 100; + char date[80]; + + date[0] = 0; + if (day) + sprintf (date, "\nDate: %d/%d/19%d", day, month, year); + else if (month) + sprintf (date, "\nDate: %d/19%d", month, year); + else if (year) + sprintf (date, "\nDate: 19%d", year); + strcat (rominfo->misc, date); + } + + if (info->comment) + if (info->comment[0]) + { + strcat (rominfo->misc, "\nComment: "); + strcat (rominfo->misc, info->comment); + } + } + } + + free (rom_buffer); + return result; +} diff --git a/ucon64/2.0/src/console/pce.h b/ucon64/2.0/src/console/pce.h new file mode 100644 index 0000000..6b086e0 --- /dev/null +++ b/ucon64/2.0/src/console/pce.h @@ -0,0 +1,31 @@ +/* +pce.h - PC-Engine support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef PCE_H +#define PCE_H +extern const st_getopt2_t pcengine_usage[]; + +extern int pcengine_init (st_rominfo_t *rominfo); +extern int pcengine_mgd (st_rominfo_t *rominfo); +extern int pcengine_msg (st_rominfo_t *rominfo); +extern int pcengine_swap (st_rominfo_t *rominfo); +extern int pcengine_f (st_rominfo_t *rominfo); +extern int pcengine_multi (int truncate_size, char *fname); +#endif diff --git a/ucon64/2.0/src/console/psx.c b/ucon64/2.0/src/console/psx.c new file mode 100644 index 0000000..c1ad6b3 --- /dev/null +++ b/ucon64/2.0/src/console/psx.c @@ -0,0 +1,66 @@ +/* +psx.c - Playstation support for uCON64 + +Copyright (c) 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "misc/misc.h" +#include "misc/itypes.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "psx.h" + + +const st_getopt2_t psx_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Playstation (One)/Playstation 2 (CD only)"/*"1994/(2000) Sony http://www.playstation.com"*/, + NULL + }, + { + "psx", 0, 0, UCON64_PSX, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_PSX_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} +}; + + +int +psx_init (st_rominfo_t *rominfo) +{ + int result = -1; + + rominfo->console_usage = psx_usage[0].help; +// rominfo->copier_usage = cdrw_usage[0].help; + + return result; +} diff --git a/ucon64/2.0/src/console/psx.h b/ucon64/2.0/src/console/psx.h new file mode 100644 index 0000000..06b1592 --- /dev/null +++ b/ucon64/2.0/src/console/psx.h @@ -0,0 +1,25 @@ +/* +psx.h - Playstation support for uCON64 + +Copyright (c) 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef PSX_H +#define PSX_H +extern int psx_init (st_rominfo_t *rominfo); +extern const st_getopt2_t psx_usage[]; +#endif diff --git a/ucon64/2.0/src/console/sms.c b/ucon64/2.0/src/console/sms.c new file mode 100644 index 0000000..d62b2a5 --- /dev/null +++ b/ucon64/2.0/src/console/sms.c @@ -0,0 +1,699 @@ +/* +sms.c - Sega Master System/Game Gear support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2003 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include "misc/chksum.h" +#include "misc/misc.h" +#include "misc/string.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "sms.h" +#include "backup/mgd.h" +#include "backup/smd.h" + + +#define SMS_HEADER_START 0x7ff0 +#define SMS_HEADER_LEN (sizeof (st_sms_header_t)) + +static int sms_chksum (unsigned char *rom_buffer, int rom_size); + + +const st_getopt2_t sms_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Sega Master System(II/III)/Game Gear (Handheld)"/*"1986/19XX SEGA http://www.sega.com"*/, + NULL + }, + { + "sms", 0, 0, UCON64_SMS, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_SMS_SWITCH] + }, + { + "int", 0, 0, UCON64_INT, + NULL, "force ROM is in interleaved format (SMD)", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "nint", 0, 0, UCON64_NINT, + NULL, "force ROM is not in interleaved format (RAW)", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "mgd", 0, 0, UCON64_MGD, + NULL, "convert to Multi Game*/MGD2/MGH/RAW (gives SMS name)", + &ucon64_wf[WF_OBJ_ALL_DEFAULT_NO_SPLIT] + }, + { + "mgdgg", 0, 0, UCON64_MGDGG, + NULL, "same as " OPTION_LONG_S "mgd, but gives GG name", + &ucon64_wf[WF_OBJ_SMS_DEFAULT_NO_SPLIT] + }, + { + "smd", 0, 0, UCON64_SMD, + NULL, "convert to Super Magic Drive/SMD", + &ucon64_wf[WF_OBJ_ALL_DEFAULT_NO_SPLIT] + }, + { + "smds", 0, 0, UCON64_SMDS, + NULL, "convert emulator (*.srm) SRAM to Super Magic Drive/SMD", + NULL + }, + { + "chk", 0, 0, UCON64_CHK, + NULL, "fix ROM checksum (SMS only)", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "multi", 1, 0, UCON64_MULTI, + "SIZE", "make multi-game file for use with SMS-PRO/GG-PRO flash card,\n" + "truncated to SIZE Mbit; file with loader must be specified\n" + "first, then all the ROMs, multi-game file to create last", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_STOP] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + +typedef struct st_sms_header +{ + char signature[8]; // "TMR "{"SEGA", "ALVS", "SMSC"}/"TMG SEGA" + unsigned char pad[2]; // 8 + unsigned char checksum_low; // 10 + unsigned char checksum_high; // 11 + unsigned char partno_low; // 12 + unsigned char partno_high; // 13 + unsigned char version; // 14 + unsigned char checksum_range; // 15, and country info +} st_sms_header_t; + +static st_sms_header_t sms_header; +static int is_gamegear; + + +// see src/backup/mgd.h for the file naming scheme +int +sms_mgd (st_rominfo_t *rominfo, int console) +{ + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + unsigned char *buffer; + int size = ucon64.file_size - rominfo->buheader_len; + + strcpy (src_name, ucon64.rom); + mgd_make_name (ucon64.rom, console, size, dest_name); + ucon64_file_handler (dest_name, src_name, OF_FORCE_BASENAME); + + if (!(buffer = (unsigned char *) malloc (size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size); + exit (1); + } + ucon64_fread (buffer, rominfo->buheader_len, size, src_name); + if (rominfo->interleaved) + smd_deinterleave (buffer, size); + + ucon64_fwrite (buffer, 0, size, dest_name, "wb"); + free (buffer); + + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + + mgd_write_index_file ((char *) basename2 (dest_name), 1); + + if (size <= 4 * MBIT) + printf ("NOTE: It may be necessary to change the suffix in order to make the game work\n" + " on an MGD2. You could try suffixes like .010, .024, .040, .048 or .078.\n"); + return 0; +} + + +int +sms_smd (st_rominfo_t *rominfo) +{ + st_smd_header_t header; + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + unsigned char *buffer; + int size = ucon64.file_size - rominfo->buheader_len; + + memset (&header, 0, SMD_HEADER_LEN); + header.size = size / 8192 >> 8; + header.id0 = 3; //size / 8192; + header.id1 = 0xaa; + header.id2 = 0xbb; + header.type = 6; + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".smd"); + ucon64_file_handler (dest_name, src_name, 0); + + if (!(buffer = (unsigned char *) malloc (size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size); + exit (1); + } + ucon64_fread (buffer, rominfo->buheader_len, size, src_name); + if (!rominfo->interleaved) + smd_interleave (buffer, size); + + ucon64_fwrite (&header, 0, SMD_HEADER_LEN, dest_name, "wb"); + ucon64_fwrite (buffer, rominfo->buheader_len, size, dest_name, "ab"); + free (buffer); + + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return 0; +} + + +int +sms_smds (void) +{ + st_smd_header_t header; + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + + memset (&header, 0, SMD_HEADER_LEN); + header.id1 = 0xaa; + header.id2 = 0xbb; + header.type = 7; // SRAM file + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".sav"); + ucon64_file_handler (dest_name, src_name, 0); + + ucon64_fwrite (&header, 0, SMD_HEADER_LEN, dest_name, "wb"); + fcopy (src_name, 0, fsizeof (src_name), dest_name, "ab"); + + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return 0; +} + + +int +sms_chk (st_rominfo_t *rominfo) +{ + char buf[2], dest_name[FILENAME_MAX]; + int offset = rominfo->header_start + 10; + + if (is_gamegear) + { + fprintf (stderr, "ERROR: This option works only for SMS (not Game Gear) files\n"); + return -1; + } + + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + + buf[0] = rominfo->current_internal_crc; // low byte + buf[1] = rominfo->current_internal_crc >> 8; // high byte + // change checksum + if (rominfo->interleaved) + { + ucon64_fputc (dest_name, rominfo->buheader_len + + (offset & ~0x3fff) + 0x2000 + (offset & 0x3fff) / 2, buf[0], "r+b"); + ucon64_fputc (dest_name, rominfo->buheader_len + + (offset & ~0x3fff) + (offset & 0x3fff) / 2, buf[1], "r+b"); + } + else + ucon64_fwrite (buf, rominfo->buheader_len + offset, 2, dest_name, "r+b"); + + dumper (stdout, buf, 2, rominfo->buheader_len + offset, DUMPER_HEX); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +static void +write_game_table_entry (FILE *destfile, int file_no, int totalsize) +{ + static int sram_page = 0, sram_msg_printed = 0; + int n; + unsigned char name[0x0c], flags = 0; // x, D (reserved), x, x, x, x, S1, S0 + const char *p; + + fseek (destfile, 0x2000 + (file_no - 1) * 0x10, SEEK_SET); + fputc (0xff, destfile); // 0x0 = 0xff + + memset (name, ' ', 0x0c); + p = basename2 (ucon64.rom); + n = strlen (p); + if (n > 0x0c) + n = 0x0c; + memcpy (name, p, n); + for (n = 0; n < 0x0c; n++) + { + if (!isprint ((int) name[n])) + name[n] = '.'; + else + name[n] = toupper (name[n]); // loader only supports upper case characters + } + fwrite (name, 1, 0x0c, destfile); // 0x01 - 0x0c = name + fputc (0, destfile); // 0x0d = 0 + fputc (totalsize / 16384, destfile); // 0x0e = bank code + + if (sram_page > 3) + { + if (!sram_msg_printed) + { + puts ("NOTE: This ROM (and all following) will share SRAM with ROM 4"); + sram_msg_printed = 1; + } + sram_page = 3; + } + flags = sram_page++; + + fputc (flags, destfile); // 0x1f = flags +} + + +int +sms_multi (int truncate_size, char *fname) +{ +#define BUFSIZE 0x20000 +// BUFSIZE must be a multiple of 16 kB (for deinterleaving) and larger than or +// equal to 1 Mbit (for check sum calculation) + int n, n_files, file_no, bytestowrite, byteswritten, totalsize = 0, done, + truncated = 0, paddedsize, org_do_not_calc_crc = ucon64.do_not_calc_crc; + struct stat fstate; + FILE *srcfile, *destfile; + char destname[FILENAME_MAX]; + unsigned char *buffer; + + if (truncate_size == 0) + { + fprintf (stderr, "ERROR: Can't make multi-game file of 0 bytes\n"); + return -1; + } + if (!(buffer = (unsigned char *) malloc (BUFSIZE))) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFSIZE); + return -1; + } + + if (fname != NULL) + { + strcpy (destname, fname); + n_files = ucon64.argc; + } + else + { + strcpy (destname, ucon64.argv[ucon64.argc - 1]); + n_files = ucon64.argc - 1; + } + + ucon64_file_handler (destname, NULL, OF_FORCE_BASENAME); + if ((destfile = fopen (destname, "w+b")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], destname); + return -1; + } + + printf ("Creating multi-game file for SMS-PRO/GG-PRO: %s\n", destname); + + file_no = 0; + for (n = 1; n < n_files; n++) + { + if (access (ucon64.argv[n], F_OK)) + continue; // "file" does not exist (option) + stat (ucon64.argv[n], &fstate); + if (!S_ISREG (fstate.st_mode)) + continue; + if (file_no == 32) // loader + 31 games + { + printf ("WARNING: A multi-game file can contain a maximum of 31 games. The other files\n" + " are ignored.\n"); + break; + } + + ucon64.console = UCON64_UNKNOWN; + ucon64.rom = ucon64.argv[n]; + ucon64.file_size = fsizeof (ucon64.rom); + // DON'T use fstate.st_size, because file could be compressed + ucon64.rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? + ucon64.buheader_len : 0; + ucon64.rominfo->interleaved = UCON64_ISSET (ucon64.interleaved) ? + ucon64.interleaved : 0; + ucon64.do_not_calc_crc = 1; + if (sms_init (ucon64.rominfo) != 0) + printf ("WARNING: %s does not appear to be an SMS/GG ROM\n", ucon64.rom); + + if ((srcfile = fopen (ucon64.rom, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], ucon64.rom); + continue; + } + if (ucon64.rominfo->buheader_len) + fseek (srcfile, ucon64.rominfo->buheader_len, SEEK_SET); + + if (file_no == 0) + { + printf ("Loader: %s\n", ucon64.rom); + if (ucon64.file_size - ucon64.rominfo->buheader_len != 3 * 16384) + printf ("WARNING: Are you sure %s is a loader binary?\n", ucon64.rom); + } + else + { + printf ("ROM%d: %s\n", file_no, ucon64.rom); + write_game_table_entry (destfile, file_no, totalsize); + fseek (destfile, totalsize, SEEK_SET); // restore file pointer + } + file_no++; + + done = 0; + byteswritten = 0; // # of bytes written per file + while (!done) + { + bytestowrite = fread (buffer, 1, BUFSIZE, srcfile); + if (ucon64.rominfo->interleaved) + smd_deinterleave (buffer, BUFSIZE); + // yes, BUFSIZE. bytestowrite might not be n * 16 kB + if (totalsize + bytestowrite > truncate_size) + { + bytestowrite = truncate_size - totalsize; + done = 1; + truncated = 1; + printf ("Output file is %d Mbit, truncating %s, skipping %d bytes\n", + truncate_size / MBIT, ucon64.rom, ucon64.file_size - + ucon64.rominfo->buheader_len - (byteswritten + bytestowrite)); + } + totalsize += bytestowrite; + if (bytestowrite == 0) + done = 1; + fwrite (buffer, 1, bytestowrite, destfile); + byteswritten += bytestowrite; + } + fclose (srcfile); + if (truncated) + break; + + // games have to be aligned to (start at) a 16 kB boundary + paddedsize = (totalsize + 16384 - 1) & ~(16384 - 1); +// printf ("paddedsize: %d (%f); totalsize: %d (%f)\n", +// paddedsize, paddedsize / (1.0 * MBIT), totalsize, totalsize / (1.0 * MBIT)); + if (paddedsize > totalsize) + { + if (paddedsize > truncate_size) + { + truncated = 1; // not *really* truncated + paddedsize = truncate_size; + } + + memset (buffer, 0, BUFSIZE); + while (totalsize < paddedsize) + { + bytestowrite = paddedsize - totalsize > BUFSIZE ? + BUFSIZE : paddedsize - totalsize; + fwrite (buffer, 1, bytestowrite, destfile); + totalsize += bytestowrite; + } + } + if (truncated) + break; + } + // fill the next game table entry + fseek (destfile, 0x2000 + (file_no - 1) * 0x10, SEEK_SET); + fputc (0, destfile); // indicate no next game + + /* + The SMS requires the check sum to match the data. The ToToTEK loaders have + the lower nibble of the "check sum range byte" set to 0x0f. Maybe ToToTEK + will change this or maybe somebody else will write a loader. To avoid extra + code to handle those cases we just overwite the value. + We don't handle a GG multi-game file differently, because we cannot detect + that the user intended to make such a file (other than by adding a new GG + multi-game option). ToToTEK's GG loader has an SMS header. + */ + fseek (destfile, 0, SEEK_SET); + n = fread (buffer, 1, 0x20000, destfile); // 0x0f => check sum range = 0x20000 + buffer[SMS_HEADER_START + 15] |= 0x0f; // overwrite check sum range byte + sms_header.checksum_range = 0x0f; // sms_chksum() uses this variable + n = sms_chksum (buffer, n); + + buffer[SMS_HEADER_START + 10] = n; // low byte + buffer[SMS_HEADER_START + 11] = n >> 8; // high byte + fseek (destfile, SMS_HEADER_START + 10, SEEK_SET); + fwrite (buffer + SMS_HEADER_START + 10, 1, 6, destfile); + + fclose (destfile); + ucon64.console = UCON64_SMS; + ucon64.do_not_calc_crc = org_do_not_calc_crc; + + return 0; +} + + +static int +sms_testinterleaved (st_rominfo_t *rominfo) +{ + unsigned char buf[0x4000] = { 0 }; + + ucon64_fread (buf, rominfo->buheader_len + 0x4000, // header in 2nd 16 kB block + 0x2000 + (SMS_HEADER_START - 0x4000 + 8) / 2, ucon64.rom); + if (!(memcmp (buf + SMS_HEADER_START - 0x4000, "TMR SEGA", 8) && + memcmp (buf + SMS_HEADER_START - 0x4000, "TMR ALVS", 8) && // SMS + memcmp (buf + SMS_HEADER_START - 0x4000, "TMR SMSC", 8) && // SMS (unofficial) + memcmp (buf + SMS_HEADER_START - 0x4000, "TMG SEGA", 8))) // GG + return 0; + + smd_deinterleave (buf, 0x4000); + if (!(memcmp (buf + SMS_HEADER_START - 0x4000, "TMR SEGA", 8) && + memcmp (buf + SMS_HEADER_START - 0x4000, "TMR ALVS", 8) && + memcmp (buf + SMS_HEADER_START - 0x4000, "TMR SMSC", 8) && + memcmp (buf + SMS_HEADER_START - 0x4000, "TMG SEGA", 8))) + return 1; + + return 0; // unknown, act as if it's not interleaved +} + + +#define SEARCHBUFSIZE (SMS_HEADER_START + 8 + 16 * 1024) +#define N_SEARCH_STR 4 +static int +sms_header_len (void) +/* + At first sight it seems reasonable to also determine whether the file is + interleaved in this function. However, we run into a chicken-and-egg problem: + in order to deinterleave the data we have to know the header length. And in + order to determine the header length we have to know whether the file is + interleaved :-) Of course we could assume the header has an even size, but it + turns out that that is not always the case. For example, there is a copy of + GG Shinobi (E) [b1] floating around with a "header" of 5 bytes. + In short: this function works only for files that are not interleaved. +*/ +{ + // first two hacks for Majesco Game Gear BIOS (U) [!] + if (ucon64.file_size == 1024) + return 0; + else if (ucon64.file_size == 1024 + SMD_HEADER_LEN) + return SMD_HEADER_LEN; + else + { + char buffer[SEARCHBUFSIZE], *ptr, search_str[N_SEARCH_STR][9] = + { "TMR SEGA", "TMR ALVS", "TMR SMSC", "TMG SEGA" }; + int n; + + ucon64_fread (buffer, 0, SEARCHBUFSIZE, ucon64.rom); + + for (n = 0; n < N_SEARCH_STR; n++) + if ((ptr = (char *) + memmem2 (buffer, SEARCHBUFSIZE, search_str[n], 8, 0)) != NULL) + return ptr - buffer - SMS_HEADER_START; + + n = ucon64.file_size % (16 * 1024); // SMD_HEADER_LEN + if (ucon64.file_size > n) + return n; + else + return 0; + } +} +#undef SEARCHBUFSIZE +#undef N_SEARCH_STR + + +int +sms_init (st_rominfo_t *rominfo) +{ + int result = -1, x; + unsigned char buf[16384] = { 0 }, *rom_buffer; + + is_gamegear = 0; + memset (&sms_header, 0, SMS_HEADER_LEN); + + if (UCON64_ISSET (ucon64.buheader_len)) // -hd, -nhd or -hdn option was specified + rominfo->buheader_len = ucon64.buheader_len; + else + rominfo->buheader_len = sms_header_len (); + + rominfo->interleaved = UCON64_ISSET (ucon64.interleaved) ? + ucon64.interleaved : sms_testinterleaved (rominfo); + + if (rominfo->interleaved) + { + ucon64_fread (buf, rominfo->buheader_len + 0x4000, // header in 2nd 16 kB block + 0x2000 + (SMS_HEADER_START - 0x4000 + SMS_HEADER_LEN) / 2, ucon64.rom); + smd_deinterleave (buf, 0x4000); + memcpy (&sms_header, buf + SMS_HEADER_START - 0x4000, SMS_HEADER_LEN); + } + else + ucon64_fread (&sms_header, rominfo->buheader_len + SMS_HEADER_START, + SMS_HEADER_LEN, ucon64.rom); + + rominfo->header_start = SMS_HEADER_START; + rominfo->header_len = SMS_HEADER_LEN; + rominfo->header = &sms_header; + + ucon64_fread (buf, 0, 11, ucon64.rom); + // Note that the identification bytes are the same as for Genesis SMD files + // The init function for Genesis files is called before this function so it + // is alright to set result to 0 + if ((buf[8] == 0xaa && buf[9] == 0xbb && buf[10] == 6) || + !(memcmp (sms_header.signature, "TMR SEGA", 8) && + memcmp (sms_header.signature, "TMR ALVS", 8) && // SMS + memcmp (sms_header.signature, "TMR SMSC", 8) && // SMS (unofficial) + memcmp (sms_header.signature, "TMG SEGA", 8)) || // GG + ucon64.console == UCON64_SMS) + result = 0; + else + result = -1; + + x = sms_header.checksum_range & 0xf0; + if (x == 0x50 || x == 0x60 || x == 0x70) + is_gamegear = 1; + + switch (x) + { + case 0x30: // SMS, falling through + case 0x50: // GG + rominfo->country = "Japan"; + break; + case 0x40: // SMS, falling through + case 0x70: // GG + rominfo->country = "U.S.A. & Europe"; + break; + case 0x60: // GG + rominfo->country = "Japan, U.S.A. & Europe"; + break; + default: + rominfo->country = "Unknown"; + break; + } + + if (!UCON64_ISSET (ucon64.do_not_calc_crc) && result == 0) + { + int size = ucon64.file_size - rominfo->buheader_len; + if (!(rom_buffer = (unsigned char *) malloc (size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size); + return -1; + } + ucon64_fread (rom_buffer, rominfo->buheader_len, size, ucon64.rom); + + if (rominfo->interleaved) + { + ucon64.fcrc32 = crc32 (0, rom_buffer, size); + smd_deinterleave (rom_buffer, size); + } + ucon64.crc32 = crc32 (0, rom_buffer, size); + + if (!is_gamegear) + { + rominfo->has_internal_crc = 1; + rominfo->internal_crc_len = 2; + rominfo->current_internal_crc = sms_chksum (rom_buffer, size); + rominfo->internal_crc = sms_header.checksum_low; + rominfo->internal_crc += sms_header.checksum_high << 8; + } + + free (rom_buffer); + } + + sprintf ((char *) buf, "Part number: 0x%04x\n", + sms_header.partno_low + (sms_header.partno_high << 8) + + ((sms_header.version & 0xf0) << 12)); + strcat (rominfo->misc, (char *) buf); + + sprintf ((char *) buf, "Version: %d", sms_header.version & 0xf); + strcat (rominfo->misc, (char *) buf); + + rominfo->console_usage = sms_usage[0].help; + rominfo->copier_usage = rominfo->buheader_len ? smd_usage[0].help : mgd_usage[0].help; + + return result; +} + + +int +sms_chksum (unsigned char *rom_buffer, int rom_size) +{ + unsigned short int sum; + int i, i_end; + + switch (sms_header.checksum_range & 0xf) + { + case 0xc: + i_end = 0x7ff0; + break; + case 0xe: // falling through + case 0xf: + i_end = 0x20000; + break; + case 0: + i_end = 0x40000; + break; + case 1: + i_end = 0x80000; + break; + default: + i_end = rom_size; + break; + } + if (i_end > rom_size) + i_end = rom_size; + + sum = 0; + for (i = 0; i < i_end; i++) + sum += rom_buffer[i]; + + if (i_end >= (int) (SMS_HEADER_START + SMS_HEADER_LEN)) + for (i = SMS_HEADER_START; i < (int) (SMS_HEADER_START + SMS_HEADER_LEN); i++) + sum -= rom_buffer[i]; + + return sum; +} diff --git a/ucon64/2.0/src/console/sms.h b/ucon64/2.0/src/console/sms.h new file mode 100644 index 0000000..2911295 --- /dev/null +++ b/ucon64/2.0/src/console/sms.h @@ -0,0 +1,36 @@ +/* +sms.h - Sega Master System/Game Gear support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2003 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef SMS_H +#define SMS_H + +extern const st_getopt2_t sms_usage[]; + +extern int sms_gg (st_rominfo_t *rominfo); +extern int sms_ggd (st_rominfo_t *rominfo); +extern int sms_gge (st_rominfo_t *rominfo); +extern int sms_init (st_rominfo_t *rominfo); +extern int sms_mgd (st_rominfo_t *rominfo, int console); +extern int sms_smd (st_rominfo_t *rominfo); +extern int sms_smds (void); +extern int sms_chk (st_rominfo_t *rominfo); +extern int sms_multi (int truncate_size, char *fname); +#endif diff --git a/ucon64/2.0/src/console/snes.c b/ucon64/2.0/src/console/snes.c new file mode 100644 index 0000000..de2bc87 --- /dev/null +++ b/ucon64/2.0/src/console/snes.c @@ -0,0 +1,4403 @@ +/* +snes.c - Super NES support for uCON64 + +Copyright (c) 1999 - 2002 NoisyB +Copyright (c) 2001 - 2004 dbjh +Copyright (c) 2002 - 2003 John Weidman +Copyright (c) 2004 JohnDie + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include "misc/bswap.h" +#include "misc/misc.h" +#include "misc/string.h" +#include "misc/chksum.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "snes.h" +#include "backup/mgd.h" +#include "backup/gd.h" +#include "backup/swc.h" +#include "backup/fig.h" +#include "backup/ufo.h" + + +#define SNES_HEADER_LEN (sizeof (st_snes_header_t)) +#define SNES_NAME_LEN 21 +#define GD3_HEADER_MAPSIZE 0x18 +#define NSRT_HEADER_VERSION 22 // version 2.2 header +#define DETECT_NOTGOOD_DUMPS // makes _a_ complete GoodSNES 0.999.5 set detected +#define DETECT_SMC_COM_FUCKED_UP_LOROM // adds support for interleaved LoROMs +#define DETECT_INSNEST_FUCKED_UP_LOROM // only adds support for its 24 Mbit + // interleaved LoROM "format" +//#define PAD_40MBIT_GD3_DUMPS // padding works for + // Dai Kaiju Monogatari 2 (J) + +static int snes_chksum (st_rominfo_t *rominfo, unsigned char **rom_buffer, + int rom_size); +static int snes_deinterleave (st_rominfo_t *rominfo, unsigned char **rom_buffer, + int rom_size); +static unsigned short int get_internal_sums (st_rominfo_t *rominfo); +static int snes_check_bs (void); +static inline int snes_isprint (char *s, int len); +static int check_banktype (unsigned char *rom_buffer, int header_offset); +static void reset_header (void *header); +static void set_nsrt_info (st_rominfo_t *rominfo, unsigned char *header); +static void get_nsrt_info (unsigned char *rom_buffer, int header_start, + unsigned char *buheader); +static void handle_nsrt_header (st_rominfo_t *rominfo, unsigned char *header, + const char **snes_country); + + +const st_getopt2_t snes_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Super Nintendo Entertainment System/SNES/Super Famicom" + /*"1990 Nintendo http://www.nintendo.com"*/, + NULL + }, + { + "snes", 0, 0, UCON64_SNES, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_SNES_SWITCH] + }, + { + "hi", 0, 0, UCON64_HI, + NULL, "force ROM is HiROM", + &ucon64_wf[WF_OBJ_SNES_SWITCH] + }, + { + "nhi", 0, 0, UCON64_NHI, + NULL, "force ROM is not HiROM", + &ucon64_wf[WF_OBJ_SNES_SWITCH] + }, + { + "erom", 0, 0, UCON64_EROM, + NULL, "force ROM is \"Extended\" (combine with -hi for Extended HiROM)", + &ucon64_wf[WF_OBJ_SNES_SWITCH] + }, +#if 0 + { + "hd", 0, 0, UCON64_HD, + NULL, "force ROM has SMC/FIG/SWC header (+512 Bytes)", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "nhd", 0, 0, UCON64_NHD, + NULL, "force ROM has no SMC/FIG/SWC header (MGD2/MGH/RAW)", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "ns", 0, 0, UCON64_NS, + NULL, "force ROM is not split", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, +#endif +#if 0 +// the next switch remains undocumented until we know of a good checksum algorithm + { + "id", 0, 0, UCON64_ID, + NULL, "force -gd3 to produce a unique file name", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, +#endif + { + "int", 0, 0, UCON64_INT, + NULL, "force ROM is in interleaved format (GD3/UFO)", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "int2", 0, 0, UCON64_INT2, + NULL, "force ROM is in interleaved format 2 (SFX)", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "nint", 0, 0, UCON64_NINT, + NULL, "force ROM is not in interleaved format", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "bs", 0, 0, UCON64_BS, + NULL, "force ROM is a Broadcast Satellaview dump", + &ucon64_wf[WF_OBJ_SNES_SWITCH] + }, + { + "nbs", 0, 0, UCON64_NBS, + NULL, "force ROM is a regular cartridge dump", + &ucon64_wf[WF_OBJ_SNES_SWITCH] + }, + { + "n", 1, 0, UCON64_N, + "NEW_NAME", "change internal ROM name to NEW_NAME", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "fig", 0, 0, UCON64_FIG, + NULL, "convert to *Pro Fighter*/FIG", + &ucon64_wf[WF_OBJ_SNES_DEFAULT_NO_SPLIT] + }, + { + "figs", 0, 0, UCON64_FIGS, + NULL, "convert emulator *.srm (SRAM) to *Pro Fighter*/FIG", + &ucon64_wf[WF_OBJ_SNES_INIT_PROBE] + }, + { + "gd3", 0, 0, UCON64_GD3, + NULL, "convert to Game Doctor SF3(SF6/SF7)/Professor SF(SF II)", + &ucon64_wf[WF_OBJ_SNES_DEFAULT_NO_SPLIT] + }, + { + "gd3s", 0, 0, UCON64_GD3S, + NULL, "convert emulator *.srm (SRAM) to GD SF3(SF6/SF7)/Professor SF*", + &ucon64_wf[WF_OBJ_SNES_INIT_PROBE] + }, + { + "mgd", 0, 0, UCON64_MGD, + NULL, "convert to Multi Game*/MGD2/MGH/RAW", + &ucon64_wf[WF_OBJ_ALL_DEFAULT_NO_SPLIT] + }, + { + "smc", 0, 0, UCON64_SMC, + NULL, "convert to Super Magicom/SMC", + &ucon64_wf[WF_OBJ_SNES_DEFAULT_NO_SPLIT] + }, + { + "swc", 0, 0, UCON64_SWC, + NULL, "convert to Super Wild Card*/SWC", + &ucon64_wf[WF_OBJ_SNES_DEFAULT_NO_SPLIT] + }, + { + "swcs", 0, 0, UCON64_SWCS, + NULL, "convert emulator *.srm (SRAM) to Super Wild Card*/SWC", + &ucon64_wf[WF_OBJ_SNES_INIT_PROBE] + }, + { + "ufo", 0, 0, UCON64_UFO, + NULL, "convert to Super UFO", + &ucon64_wf[WF_OBJ_SNES_DEFAULT_NO_SPLIT] + }, + { + "ufos", 0, 0, UCON64_UFOS, + NULL, "convert emulator *.srm (SRAM) to Super UFO", + &ucon64_wf[WF_OBJ_SNES_INIT_PROBE] + }, + { + "stp", 0, 0, UCON64_STP, + NULL, "convert SRAM from backup unit for use with an emulator\n" + OPTION_LONG_S "stp just strips the first 512 bytes", + NULL + }, + { + "dbuh", 0, 0, UCON64_DBUH, + NULL, "display (relevant part of) backup unit header", + &ucon64_wf[WF_OBJ_SNES_DEFAULT] + }, + { + "dint", 0, 0, UCON64_DINT, + NULL, "deinterleave ROM (regardless whether the ROM is interleaved)", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_NO_SPLIT] + }, + { + "ctrl", 1, 0, UCON64_CTRL, + "TYPE", "specify type of controller in port 1 for emu when converting\n" + "TYPE=0 gamepad\n" + "TYPE=1 mouse\n" + "TYPE=2 mouse / gamepad\n" + "TYPE=6 multitap", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "ctrl2", 1, 0, UCON64_CTRL2, + "TYPE", "specify type of controller in port 2 for emu when converting\n" + "TYPE=0 gamepad\n" + "TYPE=1 mouse\n" + "TYPE=2 mouse / gamepad\n" + "TYPE=3 super scope\n" + "TYPE=4 super scope / gamepad\n" + "TYPE=5 Konami's justifier\n" + "TYPE=6 multitap\n" + "TYPE=7 mouse / super scope / gamepad", + &ucon64_wf[WF_OBJ_SNES_SWITCH] + }, + { + "col", 1, 0, UCON64_COL, + "0xCOLOR", "convert 0xRRGGBB (HTML) <-> 0xXXXX (SNES)" + /*"this routine was used to find green colors in games and\n" + "to replace them with red colors (blood mode)"*/, + &ucon64_wf[WF_OBJ_SNES_NO_ROM] + }, + { + "j", 0, 0, UCON64_J, + NULL, "join split ROM", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE] + }, + { + "s", 0, 0, UCON64_S, + NULL, "split ROM; default part size is 8 Mb", + &ucon64_wf[WF_OBJ_ALL_DEFAULT_NO_SPLIT] + }, + { + "ssize", 1, 0, UCON64_SSIZE, + "SIZE", "specify split part size in Mbit (not for Game Doctor SF3)", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, +#if 0 + { + "p", 0, 0, UCON64_P, + NULL, "pad ROM to full Mb", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, +#endif + { + "k", 0, 0, UCON64_K, + NULL, "remove protection (crack)", + &ucon64_wf[WF_OBJ_SNES_DEFAULT] + }, + { + "f", 0, 0, UCON64_F, + NULL, "remove NTSC/PAL protection", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "l", 0, 0, UCON64_L, + NULL, "remove SlowROM checks", + &ucon64_wf[WF_OBJ_SNES_DEFAULT] + }, + { + "chk", 0, 0, UCON64_CHK, + NULL, "fix ROM checksum", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "multi", 1, 0, UCON64_MULTI, + "SIZE", "make multi-game file for use with Super Flash flash card,\n" + "truncated to SIZE Mbit; file with loader must be specified\n" + "first, then all the ROMs, multi-game file to create last", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_STOP] + }, + { + "dmirr", 0, 0, UCON64_DMIRR, + NULL, "\"de-mirror\" ROM (strip mirrored block from end of ROM)", + &ucon64_wf[WF_OBJ_SNES_DEFAULT] + }, + { + "dnsrt", 0, 0, UCON64_DNSRT, + NULL, "\"de-NSRT\" ROM (restore name and checksum from NSRT header)", + &ucon64_wf[WF_OBJ_SNES_DEFAULT] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + +typedef struct st_snes_header +{ + unsigned char maker_high; // 0 + unsigned char maker_low; // 1 + unsigned char game_id_prefix; // 2 + unsigned char game_id_low; // 3 + unsigned char game_id_high; // 4 + unsigned char game_id_country; // 5 + // 'E' = USA, 'F' = France, 'G' = Germany, 'J' = Japan, 'P' = Europe, 'S' = Spain + unsigned char pad1[7]; // 6 + unsigned char sfx_sram_size; // 13 + unsigned char pad2[2]; // 14 + unsigned char name[SNES_NAME_LEN]; // 16 + unsigned char map_type; // 37, a.k.a. ROM makeup + unsigned char rom_type; // 38 +#define bs_month rom_type // release date, month + unsigned char rom_size; // 39 +#define bs_day rom_size // release date, day + unsigned char sram_size; // 40 +#define bs_map_type sram_size + unsigned char country; // 41 +#define bs_type country + unsigned char maker; // 42 + unsigned char version; // 43 + /* + If we combine the following 4 bytes in 2 short int variables, + inverse_checksum and checksum, they will have an incorrect value on big + endian machines. + */ + unsigned char inverse_checksum_low; // 44 + unsigned char inverse_checksum_high; // 45 + unsigned char checksum_low; // 46 + unsigned char checksum_high; // 47 +} st_snes_header_t; + +static st_snes_header_t snes_header; +static int snes_split, snes_sramsize, snes_sfx_sramsize, snes_header_base, + snes_hirom, snes_hirom_ok, nsrt_header, bs_dump, st_dump; +static snes_file_t type; + +static unsigned char gd3_hirom_8mb_map[GD3_HEADER_MAPSIZE] = + { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 + }; +static unsigned char gd3_hirom_16mb_map[GD3_HEADER_MAPSIZE] = + { + 0x20, 0x21, 0x20, 0x21, 0x20, 0x21, 0x20, 0x21, + 0x20, 0x21, 0x20, 0x21, 0x20, 0x21, 0x20, 0x21, + 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23 + }; +static unsigned char gd3_hirom_24mb_map[GD3_HEADER_MAPSIZE] = + { + 0x20, 0x21, 0x22, 0x00, 0x20, 0x21, 0x22, 0x00, + 0x20, 0x21, 0x22, 0x00, 0x20, 0x21, 0x22, 0x00, + 0x24, 0x25, 0x23, 0x00, 0x24, 0x25, 0x23, 0x00 + }; +static unsigned char gd3_hirom_32mb_map[GD3_HEADER_MAPSIZE] = + { + 0x20, 0x21, 0x22, 0x23, 0x20, 0x21, 0x22, 0x23, + 0x20, 0x21, 0x22, 0x23, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x24, 0x25, 0x26, 0x27 + }; +// map for Dai Kaiju Monogatari 2 (J) +static unsigned char gd3_hirom_40mb_map[GD3_HEADER_MAPSIZE] = + { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x22, 0x23, 0x24, 0x25, 0x22, 0x23, 0x24, 0x25, + 0x21, 0x21, 0x21, 0x21, 0x26, 0x27, 0x28, 0x29 + }; +// map for Tales of Phantasia (J) +static unsigned char gd3_hirom_48mb_map[GD3_HEADER_MAPSIZE] = + { + 0x20, 0x21, 0x20, 0x21, 0x20, 0x21, 0x40, 0x40, + 0x24, 0x25, 0x26, 0x27, 0x24, 0x25, 0x26, 0x27, + 0x22, 0x23, 0x40, 0x40, 0x28, 0x29, 0x2a, 0x2b + }; + +static unsigned char gd3_lorom_4mb_map[GD3_HEADER_MAPSIZE] = + { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 + }; +static unsigned char gd3_lorom_8mb_map[GD3_HEADER_MAPSIZE] = + { + 0x20, 0x21, 0x20, 0x21, 0x20, 0x21, 0x20, 0x21, + 0x20, 0x21, 0x20, 0x21, 0x20, 0x21, 0x20, 0x21, + 0x20, 0x21, 0x20, 0x21, 0x20, 0x21, 0x20, 0x21 + }; +static unsigned char gd3_lorom_16mb_map[GD3_HEADER_MAPSIZE] = + { + 0x20, 0x21, 0x22, 0x23, 0x20, 0x21, 0x22, 0x23, + 0x20, 0x21, 0x22, 0x23, 0x20, 0x21, 0x22, 0x23, + 0x20, 0x21, 0x22, 0x23, 0x20, 0x21, 0x22, 0x23 + }; +static unsigned char gd3_lorom_32mb_map[GD3_HEADER_MAPSIZE] = + { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x24, 0x25, 0x26, 0x27, 0x24, 0x25, 0x26, 0x27 + }; + + +int +snes_get_snes_hirom (void) +{ + return snes_hirom; +} + + +snes_file_t +snes_get_file_type (void) +{ + return type; +} + + +int +snes_col (const char *color) +/* +The Nintendo Super Famicom is capable of displaying 256 colours from a +palette of 32,768. These 256 colours are split into 8 palettes of 32 colours +each. + +To change the colours the following needs to be done: + +Loading the palette control register ($2121) with the colour number you wish +to change (0-255, 0=background). +Then load the colour into the palette data register first the low 8 bits, +followed by the high 7 bits (this gives you the maximum 32768 colours +possible $0000-$7fff). + +Colour data is made up of 3 components (Red,Green,Blue) each of 5 bits (The +Amiga uses exactly the same system, but only using 4 bits per component). +Saying that, Nintendo being the stupid japanese idiots they are decided that +R,G,B wasn't alphabetically correct and so opted to store the bits as B,G,R. + + 00000 00000 00000 + \ / \ / \ / + \ / \ / \ / + B G R + +Examples: +~~~~~~~~~ + 11111 00000 00000 = $7C00 (Bright Blue) + 00000 11111 00000 = $03E0 (Bright Green) + 00000 00000 11111 = $001F (Bright Red) + 00000 00000 00000 = $0000 (Black) + 11111 11111 11111 = $7FFF (White) + +Remember to load the lowest 8 bits first, then the top 7 bits. +*/ +{ + int r, g, b; + unsigned int col; + + sscanf (color, "%x", &col); + + r = (col & 0xff0000) >> 16; + g = (col & 0xff00) >> 8; + b = col & 0xff; + + printf ("0x%02x%02x%02x (html) == ", r, g, b); + + r = (r * 0x1f) / 0xff; + g = (g * 0x1f) / 0xff; + b = (b * 0x1f) / 0xff; + + col = b; + col = col << 5; + col = col + g; + col = col << 5; + col = col + r; + + printf ("0x%02x%02x (snes)\n", col & 0xff, (col & 0x7f00) / 0x100); + + sscanf (color, "%x", &col); + + if (col < 0xff7f + 1) + { + printf ("0x%04x (snes) == ", col & 0xff7f); + + col = ((col & 0x7f) * 0x100) + ((col & 0xff00) / 0x100); + + r = col & 0x1f; + g = (col & (0x1f << 5)) >> 5; + b = (col & (0x1f << 10)) >> 10; + + r = r * 0xff / 0x1f; + g = g * 0xff / 0x1f; + b = b * 0xff / 0x1f; + + printf ("0x%02x%02x%02x (html)\n", r, g, b); + } + fputc ('\n', stdout); + return 0; +} + + +static int +snes_convert_sramfile (int org_header_len, const void *new_header) +{ + FILE *srcfile, *destfile; + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX], buf[32 * 1024]; + unsigned int blocksize, byteswritten, new_header_len; + + strcpy (src_name, ucon64.rom); + if (new_header) + { + new_header_len = SWC_HEADER_LEN; + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".sav"); + } + else // code for Game Doctor SRAM file + { + int n; + + new_header_len = 0; + sprintf (dest_name, "SF8%.3s", basename2 (ucon64.rom)); + strupr (dest_name); + // avoid trouble with filenames containing spaces + for (n = 3; n < 6; n++) // skip "SF" and first digit + if (dest_name[n] == ' ') + dest_name[n] = '_'; + set_suffix (dest_name, ".B00"); + } + ucon64_file_handler (dest_name, src_name, 0); + + if ((srcfile = fopen (src_name, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], src_name); + return -1; + } + if ((destfile = fopen (dest_name, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], dest_name); + return -1; + } + + fseek (srcfile, org_header_len, SEEK_SET); + if (new_header) + { + fwrite (new_header, 1, new_header_len, destfile); // write header + byteswritten = new_header_len; + } + else + byteswritten = 0; + + blocksize = fread (buf, 1, 32 * 1024, srcfile); // read 32 kB at max + while (byteswritten < 32 * 1024 + new_header_len) + { + // Pad SRAM data to 32 kB by repeating it. At least the SWC DX2 does + // something similar. + fwrite (buf, 1, byteswritten + blocksize <= 32 * 1024 + new_header_len ? + blocksize : 32 * 1024 + new_header_len - byteswritten, destfile); + byteswritten += blocksize; + } + + fclose (srcfile); + fclose (destfile); + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return 0; +} + + +int +snes_swcs (st_rominfo_t *rominfo) +{ + st_swc_header_t header; + + memset (&header, 0, SWC_HEADER_LEN); + header.id1 = 0xaa; + header.id2 = 0xbb; + header.type = 5; // size needn't be set for the SWC + // (SWC itself doesn't set it either) + return snes_convert_sramfile (rominfo->buheader_len, &header); +} + + +int +snes_figs (st_rominfo_t *rominfo) +{ + st_fig_header_t header; + + memset (&header, 0, FIG_HEADER_LEN); + header.size_low = 4; // 32 kB == 4*8 kB, size_high is already 0 + + return snes_convert_sramfile (rominfo->buheader_len, &header); +} + + +int +snes_ufos (st_rominfo_t *rominfo) +{ + unsigned char header[SWC_HEADER_LEN]; + + memset (&header, 0, SWC_HEADER_LEN); + memcpy (&header[8], "SUPERUFO", 8); + + return snes_convert_sramfile (rominfo->buheader_len, &header); +} + + +int +snes_gd3s (st_rominfo_t *rominfo) +{ + return snes_convert_sramfile (rominfo->buheader_len, NULL); +} + + +static void +write_deinterleaved_data (st_rominfo_t *rominfo, const char *src_name, + const char *dest_name, int size, int buheader_len) +{ + unsigned char *buffer; + if (!(buffer = (unsigned char *) malloc (size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size); + exit (1); + } + ucon64_fread (buffer, rominfo->buheader_len, size, src_name); + snes_deinterleave (rominfo, &buffer, size); + ucon64_fwrite (buffer, buheader_len, size, dest_name, buheader_len ? "ab" : "wb"); + free (buffer); +} + + +int +snes_dint (st_rominfo_t *rominfo) +{ + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + int buheader_len = rominfo->buheader_len > (int) SWC_HEADER_LEN ? + (int) SWC_HEADER_LEN : rominfo->buheader_len; + + puts ("Converting to deinterleaved format..."); + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".tmp"); + ucon64_file_handler (dest_name, src_name, 0); + + if (!rominfo->interleaved) + printf ("WARNING: Deinterleaving a ROM that was not detected as interleaved\n"); + fcopy (src_name, 0, buheader_len, dest_name, "wb"); + write_deinterleaved_data (rominfo, src_name, dest_name, + ucon64.file_size - rominfo->buheader_len, buheader_len); + + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return 0; +} + + +static int +snes_ffe (st_rominfo_t *rominfo, char *ext) +{ + st_swc_header_t header; + int size = ucon64.file_size - rominfo->buheader_len; + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + + ucon64_fread (&header, 0, rominfo->buheader_len > (int) SWC_HEADER_LEN ? + (int) SWC_HEADER_LEN : rominfo->buheader_len, ucon64.rom); + reset_header (&header); + header.size_low = size / 8192; + header.size_high = size / 8192 >> 8; + + header.emulation = snes_split ? 0x40 : 0; + header.emulation |= snes_hirom ? 0x30 : 0; + // bit 3 & 2 are already ok for 32 kB SRAM size + if (snes_sramsize == 8 * 1024) + header.emulation |= 0x04; + else if (snes_sramsize == 2 * 1024) + header.emulation |= 0x08; + else if (snes_sramsize == 0) + header.emulation |= 0x0c; + + header.id1 = 0xaa; + header.id2 = 0xbb; + header.type = 4; + + set_nsrt_info (rominfo, (unsigned char *) &header); + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ext); + ucon64_file_handler (dest_name, src_name, 0); + + ucon64_fwrite (&header, 0, SWC_HEADER_LEN, dest_name, "wb"); + if (rominfo->interleaved) + write_deinterleaved_data (rominfo, src_name, dest_name, size, SWC_HEADER_LEN); + else + fcopy (src_name, rominfo->buheader_len, size, dest_name, "ab"); + + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return 0; +} + + +// header format is specified in src/backup/ffe.h +int +snes_smc (st_rominfo_t *rominfo) +{ + if ((bs_dump ? snes_header.bs_map_type : snes_header.map_type) & 0x10) + printf ("NOTE: This game might not work with a Super Magicom because it's a FastROM game\n"); + + return snes_ffe (rominfo, ".smc"); +} + + +// header format is specified in src/backup/ffe.h +int +snes_swc (st_rominfo_t *rominfo) +{ + return snes_ffe (rominfo, ".swc"); +} + + +// header format is specified in src/backup/fig.h +void +snes_set_fig_header (st_rominfo_t *rominfo, st_fig_header_t *header) +{ + int size = ucon64.file_size - rominfo->buheader_len, uses_DSP; + + header->size_low = size / 8192; + header->size_high = size / 8192 >> 8; + header->multi = snes_split ? 0x40 : 0; + header->hirom = snes_hirom ? 0x80 : 0; + + uses_DSP = snes_header.rom_type == 3 || snes_header.rom_type == 5 || + snes_header.rom_type == 0xf6; + + if ((snes_header.rom_type & 0xf0) == 0x10) // uses FX(2) chip + { + header->emulation1 = 0x11; + header->emulation2 = 2; + } + else + { +#if 0 // memset() set all fields to 0 + header->emulation1 = 0; // default value for LoROM dumps + if (snes_sramsize == 32 * 1024) + header->emulation2 = 0; + else +#endif + if (snes_sramsize == 8 * 1024 || snes_sramsize == 2 * 1024) + header->emulation2 = 0x80; + else if (snes_sramsize == 0) + { + header->emulation1 = 0x77; + header->emulation2 = 0x83; + } + + if (snes_hirom) + { + header->emulation2 |= 2; + if (uses_DSP) + header->emulation1 |= 0xf0; + if (snes_sramsize != 0) + header->emulation1 |= 0xdd; + } + else if (uses_DSP) // LoROM + { + header->emulation1 &= 0x0f; + header->emulation1 |= 0x40; // LoROM && SRAM == 0 && DSP => 0x47 + } + } +} + + +int +snes_fig (st_rominfo_t *rominfo) +{ + st_fig_header_t header; + int size = ucon64.file_size - rominfo->buheader_len; + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + + ucon64_fread (&header, 0, rominfo->buheader_len > (int) FIG_HEADER_LEN ? + (int) FIG_HEADER_LEN : rominfo->buheader_len, ucon64.rom); + reset_header (&header); + snes_set_fig_header (rominfo, &header); + set_nsrt_info (rominfo, (unsigned char *) &header); + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".fig"); + ucon64_file_handler (dest_name, src_name, 0); + + ucon64_fwrite (&header, 0, FIG_HEADER_LEN, dest_name, "wb"); + if (rominfo->interleaved) + write_deinterleaved_data (rominfo, src_name, dest_name, size, FIG_HEADER_LEN); + else + fcopy (src_name, rominfo->buheader_len, size, dest_name, "ab"); + + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return 0; +} + + +// see src/backup/mgd.h for the file naming scheme +int +snes_mgd (st_rominfo_t *rominfo) +{ + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + int size = ucon64.file_size - rominfo->buheader_len; + + if (snes_hirom) + printf ("NOTE: This game might not work with a MGD because it's a HiROM game\n"); + + strcpy (src_name, ucon64.rom); + mgd_make_name (ucon64.rom, UCON64_SNES, size, dest_name); + ucon64_file_handler (dest_name, src_name, OF_FORCE_BASENAME); + + if (rominfo->interleaved) + write_deinterleaved_data (rominfo, src_name, dest_name, size, 0); + else + fcopy (src_name, rominfo->buheader_len, ucon64.file_size, dest_name, "wb"); + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + + mgd_write_index_file ((char *) basename2 (dest_name), 1); + + return 0; +} + + +void +snes_int_blocks (const unsigned char *deintptr, unsigned char *ipl, + unsigned char *iph, int nblocks) +{ + int i; + + // interleave 64 K blocks + for (i = nblocks; i > 0; i--) + { + memmove (ipl, deintptr, 0x8000); + memmove (iph, deintptr + 0x8000, 0x8000); + deintptr += 0x10000; + ipl += 0x8000; + iph += 0x8000; + } +} + + +void +snes_mirror (unsigned char *dstbuf, unsigned int start, unsigned int data_end, + unsigned int mirror_end) +{ + int datasize, totsize, nchunks, surplus; + + datasize = data_end - start; + totsize = mirror_end - start; + + if (datasize >= totsize) + return; + + nchunks = totsize / datasize - 1; + surplus = totsize % datasize; + while (nchunks-- > 0) + { + memcpy (dstbuf + data_end, dstbuf + start, datasize); + data_end += datasize; + } + if (surplus > 0) + memmove (dstbuf + data_end, dstbuf + start, mirror_end - data_end); +} + + +static void +make_gd_name (const char *filename, st_rominfo_t *rominfo, char *name, + unsigned char *buffer, int newsize) +{ + char dest_name[FILENAME_MAX], *p, id_str[3]; + int n, size = ucon64.file_size - rominfo->buheader_len; + + strcpy (dest_name, filename); + + if (UCON64_ISSET (ucon64.id)) + { + /* + We include the underscore so that we can encode a base 37 number (10 + digits + 26 characters in alphabet + underscore = 37). The ID is 3 + characters long which makes it possible to have 37^3 = 50653 different + IDs. If we wouldn't include the underscore we would have 46656 + different IDs. + We can't use the SNES checksum because several ROM dumps have the same + checksum (not only PD files!). Nor can we use the internal SNES + checksum, because several beta ROM dumps have an internal checksum of + 0 or 0xffff. + */ + unsigned int local_buffer = !buffer, d2, d1, d0, id = 0; + + if (local_buffer) + { + if (!(buffer = (unsigned char *) malloc (size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size); + exit (1); + } + ucon64_fread (buffer, ucon64.rominfo->buheader_len, size, dest_name); + if (rominfo->interleaved) + snes_deinterleave (rominfo, &buffer, size); + } + + for (n = 0; n < size; n++) + id += buffer[n] ^ (n & 0xff); + id %= 37 * 37 * 37; // ensure value can be encoded with 3 base 37 digits + + if (local_buffer) + free (buffer); + + d2 = id / (37 * 37); + d1 = (id % (37 * 37)) / 37; + d0 = id % 37; + id_str[0] = d2 == 36 ? '_' : (d2 <= 9 ? d2 + '0' : d2 + 'A' - 10); + id_str[1] = d1 == 36 ? '_' : (d1 <= 9 ? d1 + '0' : d1 + 'A' - 10); + id_str[2] = d0 == 36 ? '_' : (d0 <= 9 ? d0 + '0' : d0 + 'A' - 10); + + p = id_str; + } + else + p = (char *) basename2 (dest_name); + + sprintf (name, "sf%d%s", newsize / MBIT, p); + if (newsize < 10 * MBIT) + { + if (!strnicmp (name, p, 3)) + strcpy (name, p); + } + else + { + if (!strnicmp (name, p, 4)) + strcpy (name, p); + } + + if ((p = strrchr (name, '.'))) + *p = 0; + strcat (name, "___"); + if (newsize < 10 * MBIT) + name[6] = 0; + else + name[7] = 0; + // avoid trouble with filenames containing spaces + for (n = 3; n < 7; n++) // skip "sf" and first digit + if (name[n] == ' ') + name[n] = '_'; +} + + +int +snes_gd3 (st_rominfo_t *rominfo) +{ + char header[GD_HEADER_LEN], src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + unsigned char *srcbuf, *dstbuf; + int n, n4Mbparts, surplus4Mb, total4Mbparts, size, newsize, pad, + half_size_4Mb, half_size_1Mb; + + size = ucon64.file_size - rominfo->buheader_len; + n4Mbparts = size / (4 * MBIT); + surplus4Mb = size % (4 * MBIT); + total4Mbparts = n4Mbparts + (surplus4Mb > 0 ? 1 : 0); + + strcpy (src_name, ucon64.rom); + if (!(srcbuf = (unsigned char *) malloc (size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size); + exit (1); + } + ucon64_fread (srcbuf, rominfo->buheader_len, size, ucon64.rom); + if (rominfo->interleaved) + snes_deinterleave (rominfo, &srcbuf, size); + + if (snes_hirom) + { + if (!((size >= 2 * MBIT && total4Mbparts <= 8) || + total4Mbparts == 10 || total4Mbparts == 12)) + { + fprintf (stderr, "ERROR: ROM size is %d Mbit -- conversion not yet implemented/verified\n", + size / MBIT); + return -1; + } + else if (total4Mbparts > 8 && snes_header_base != SNES_EROM) + { + fprintf (stderr, "ERROR: Normal ROM > 32 Mbit -- conversion not yet implemented\n"); + return -1; + } + + if (total4Mbparts == 5) + total4Mbparts = 6; // 20 Mbit HiROMs get padded to 24 Mbit + else if (total4Mbparts == 7) + total4Mbparts = 8; // 28 Mbit HiROMs get padded to 32 Mbit +#ifdef PAD_40MBIT_GD3_DUMPS // (a 28 Mbit ROM needs 40 Mbit of GD DRAM) + else if (total4Mbparts == 10) + { + total4Mbparts = 12; // 40 Mbit HiROMs get padded to 48 Mbit + printf ("NOTE: Paddding to 48 Mbit\n"); + } +#endif + + // create the header + ucon64_fread (header, 0, rominfo->buheader_len > GD_HEADER_LEN ? + GD_HEADER_LEN : rominfo->buheader_len, ucon64.rom); + reset_header (header); + memcpy (header, "GAME DOCTOR SF 3", 0x10); + + if (snes_sramsize == 8 * 1024) + header[0x10] = (unsigned char) 0x81; // 64 kb + else if (snes_sramsize == 2 * 1024) + header[0x10] = (unsigned char) 0x82; // 16 kb + else + header[0x10] = (unsigned char) 0x80; // 0 kb or 256 kb + + if (total4Mbparts <= 2) + memcpy (&header[0x11], gd3_hirom_8mb_map, GD3_HEADER_MAPSIZE); + else if (total4Mbparts <= 4) + memcpy (&header[0x11], gd3_hirom_16mb_map, GD3_HEADER_MAPSIZE); + else if (total4Mbparts <= 6) + memcpy (&header[0x11], gd3_hirom_24mb_map, GD3_HEADER_MAPSIZE); + else if (total4Mbparts <= 8) + memcpy (&header[0x11], gd3_hirom_32mb_map, GD3_HEADER_MAPSIZE); + else if (total4Mbparts <= 10) + memcpy (&header[0x11], gd3_hirom_40mb_map, GD3_HEADER_MAPSIZE); + else + memcpy (&header[0x11], gd3_hirom_48mb_map, GD3_HEADER_MAPSIZE); + + if (snes_sramsize != 0) + { + if (snes_header_base == SNES_EROM) + { + header[0x29] = 0x00; + header[0x2a] = 0x0f; + } + else + { + header[0x29] = 0x0c; + header[0x2a] = 0x0c; + } + } + // Adjust sram map for exceptions - a couple of 10-12 Mb HiROM games + // (Liberty or Death, Brandish). May not be necessary + + // interleave the image + if (n4Mbparts) + newsize = 4 * total4Mbparts * MBIT; + else + newsize = ((size + MBIT - 1) / MBIT) * MBIT; + + // special pad code should only be executed for 10, 20 and if + // PAD_40MBIT_GD3_DUMPS is defined, 40 Mbit ROMs + if (total4Mbparts == 3 || total4Mbparts == 6 || total4Mbparts == 12) + pad = (newsize - size) / 2; + else + pad = 0; + + if (!(dstbuf = (unsigned char *) malloc (newsize))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], newsize); + exit (1); + } + if (newsize > size) + { + if (!(srcbuf = (unsigned char *) realloc (srcbuf, newsize))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], newsize); + exit (1); + } + memset (srcbuf + size, 0, newsize - size); + memset (dstbuf + size, 0, newsize - size); + } + + if (snes_header_base == SNES_EROM) + { + int size2 = newsize - 32 * MBIT; // size of second ROM (16 Mbit if ToP) + // interleave the 32 Mbit ROM + snes_int_blocks (srcbuf, dstbuf + size2 + 16 * MBIT, dstbuf + size2, + 32 * MBIT / 0x10000); + // interleave the second ROM + snes_int_blocks (srcbuf + 32 * MBIT, dstbuf + size2 / 2, dstbuf, + size2 / 0x10000); + if (pad > 0) + { + snes_mirror (dstbuf, 0, 4 * MBIT, 8 * MBIT); + snes_mirror (dstbuf, 8 * MBIT, 12 * MBIT, 16 * MBIT); + } + } + else if (total4Mbparts == 6) + { + snes_int_blocks (srcbuf, dstbuf + 16 * MBIT, dstbuf, 16 * MBIT / 0x10000); + snes_int_blocks (srcbuf + 16 * MBIT, dstbuf + 12 * MBIT, + dstbuf + 8 * MBIT, (size - 16 * MBIT) / 0x10000); + if (pad > 0) + { + snes_mirror (dstbuf, 8 * MBIT, 10 * MBIT, 12 * MBIT); + snes_mirror (dstbuf, 12 * MBIT, 14 * MBIT, 16 * MBIT); + } + } + else + { + n = newsize / 2; + snes_int_blocks (srcbuf, dstbuf + n, dstbuf, newsize / 0x10000); + if (pad > 0) + { + half_size_4Mb = (size / 2) & ~(4 * MBIT - 1); + half_size_1Mb = (size / 2 + MBIT - 1) & ~(MBIT - 1); + snes_mirror (dstbuf, half_size_4Mb, half_size_1Mb, n); + snes_mirror (dstbuf, n + half_size_4Mb, n + half_size_1Mb, newsize); + } + } + } + else + { + if (total4Mbparts > 8) + { + fprintf (stderr, "ERROR: This ROM > 32 Mbit LoROM -- can't convert\n"); + return -1; + } + + ucon64_fread (header, 0, rominfo->buheader_len > GD_HEADER_LEN ? + GD_HEADER_LEN : rominfo->buheader_len, ucon64.rom); + reset_header (header); + memcpy (header, "GAME DOCTOR SF 3", 0x10); + + if (snes_sramsize == 8 * 1024) + header[0x10] = (unsigned char) 0x81; // 64 kb + else if (snes_sramsize == 2 * 1024) + header[0x10] = (unsigned char) 0x82; // 16 kb + else + header[0x10] = (unsigned char) 0x80; // 0 kb or 256 kb + + if (total4Mbparts <= 1) + memcpy (&header[0x11], gd3_lorom_4mb_map, GD3_HEADER_MAPSIZE); + else if (total4Mbparts <= 2) + memcpy (&header[0x11], gd3_lorom_8mb_map, GD3_HEADER_MAPSIZE); + else if (total4Mbparts <= 4) + memcpy (&header[0x11], gd3_lorom_16mb_map, GD3_HEADER_MAPSIZE); + else + memcpy (&header[0x11], gd3_lorom_32mb_map, GD3_HEADER_MAPSIZE); + + if (snes_sramsize != 0) + { + header[0x24] = 0x40; + header[0x28] = 0x40; + } + + dstbuf = srcbuf; + newsize = size; + } + + set_nsrt_info (rominfo, (unsigned char *) &header); + + make_gd_name (ucon64.rom, rominfo, dest_name, srcbuf, newsize); + // here we could also use NULL as second argument for + // ucon64_file_handler(), because we've already loaded the data + ucon64_file_handler (dest_name, src_name, OF_FORCE_BASENAME); + ucon64_fwrite (header, 0, GD_HEADER_LEN, dest_name, "wb"); + ucon64_fwrite (dstbuf, GD_HEADER_LEN, newsize, dest_name, "ab"); + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + + free (srcbuf); + if (snes_hirom) + free (dstbuf); + + return 0; +} + + +// header format is specified in src/backup/ufo.h +int +snes_ufo (st_rominfo_t *rominfo) +{ + st_ufo_header_t header; + int size = ucon64.file_size - rominfo->buheader_len; + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + + ucon64_fread (&header, 0, rominfo->buheader_len > (int) UFO_HEADER_LEN ? + (int) UFO_HEADER_LEN : rominfo->buheader_len, ucon64.rom); + reset_header (&header); + header.multi = snes_split ? 0x40 : 0; // TODO + memcpy (header.id, "SUPERUFO", 8); + header.isrom = 1; + header.banktype = snes_hirom ? 0 : 1; + + if (snes_sramsize > 32 * 1024) + header.sram_size = 8; + else if (snes_sramsize > 8 * 1024) // 64 kb < size <= 256 kb + header.sram_size = 3; + else if (snes_sramsize > 2 * 1024) // 16 kb < size <= 64 kb + header.sram_size = 2; + else if (snes_sramsize > 0) // 1 - 16 kb + header.sram_size = 1; + // header.sram_size is already ok for snes_sramsize == 0 + + header.sram_type = snes_hirom ? 0 : 3; + + set_nsrt_info (rominfo, (unsigned char *) &header); + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".ufo"); + ucon64_file_handler (dest_name, src_name, 0); + + if (snes_hirom) + { + unsigned char *srcbuf, *dstbuf; + int half_size_4Mb, half_size_1Mb, + newsize = size >= 10 * MBIT && size <= 12 * MBIT ? + 12 * MBIT : ((size + MBIT - 1) & ~(MBIT - 1)), + half_newsize = newsize / 2, pad = (newsize - size) / 2; + + header.size_low = newsize / 8192; + header.size_high = newsize / 8192 >> 8; + header.size = newsize / MBIT; + + if (snes_sramsize != 0) + header.sram_a20_a21 = 0x0c; // try 3 if game gives protection message + header.sram_a22_a23 = 2; + // Tales of Phantasia (J) & Dai Kaiju Monogatari 2 (J) [14-17]: 0 0x0e 0 0 + + if (!(srcbuf = (unsigned char *) malloc (size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size); + exit (1); + } + if (!(dstbuf = (unsigned char *) malloc (newsize))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], newsize); + exit (1); + } + + ucon64_fread (srcbuf, rominfo->buheader_len, size, src_name); + if (rominfo->interleaved) + snes_deinterleave (rominfo, &srcbuf, size); + if (newsize > size) + memset (dstbuf + size, 0, newsize - size); + + snes_int_blocks (srcbuf, dstbuf + half_newsize, dstbuf, size / 0x10000); + if (pad > 0) + { + half_size_4Mb = (size / 2) & ~(4 * MBIT - 1); + half_size_1Mb = (size / 2 + MBIT - 1) & ~(MBIT - 1); + snes_mirror (dstbuf, half_size_4Mb, half_size_1Mb, half_newsize); + snes_mirror (dstbuf, half_newsize + half_size_4Mb, + half_newsize + half_size_1Mb, newsize); + } + + ucon64_fwrite (&header, 0, UFO_HEADER_LEN, dest_name, "wb"); + ucon64_fwrite (dstbuf, UFO_HEADER_LEN, newsize, dest_name, "ab"); + + free (srcbuf); + free (dstbuf); + } + else // LoROM + { + header.size_low = size / 8192; + header.size_high = size / 8192 >> 8; + header.size = size / MBIT; + + if (snes_sramsize == 0) + { + // check if the game uses a DSP chip + if (snes_header.rom_type == 3 || snes_header.rom_type == 5 || + snes_header.rom_type == 0xf6) + { + header.sram_a15 = 1; + header.sram_a20_a21 = 0x0c; + } + else // no SRAM & doesn't use a DSP chip + { + header.sram_a22_a23 = 2; + header.sram_type = 0; + } + } + else // cartridge contains SRAM + { + header.sram_a15 = 2; // try 1 if game gives protection error + header.sram_a20_a21 = 0x0f; + header.sram_a22_a23 = 3; + } + + ucon64_fwrite (&header, 0, UFO_HEADER_LEN, dest_name, "wb"); + if (rominfo->interleaved) + write_deinterleaved_data (rominfo, src_name, dest_name, size, UFO_HEADER_LEN); + else + fcopy (src_name, rominfo->buheader_len, size, dest_name, "ab"); + } + + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return 0; +} + + +int +snes_make_gd_names (const char *filename, st_rominfo_t *rominfo, char **names) +{ + char dest_name[FILENAME_MAX]; + int nparts, surplus, n, n_names = 0, size = ucon64.file_size - rominfo->buheader_len; + + // Don't use PARTSIZE here, because the Game Doctor doesn't support + // arbitrary part sizes + nparts = size / (8 * MBIT); + surplus = size % (8 * MBIT); + + make_gd_name (filename, rominfo, dest_name, NULL, size); + strupr (dest_name); + dest_name[7] = 'A'; + dest_name[8] = 0; + // avoid trouble with filenames containing spaces + for (n = 3; n < 7; n++) // skip "SF" and first digit + if (dest_name[n] == ' ') + dest_name[n] = '_'; + + if (snes_hirom && size <= 16 * MBIT) + { + // 8 Mbit or less HiROMs, X is used to pad filename to 8 (SF4###XA) + if (size < 10 * MBIT) + dest_name[8 - 2] = 'X'; + strcpy (names[n_names++], dest_name); + + dest_name[8 - 1]++; + strcpy (names[n_names++], dest_name); + } + else + { + for (n = 0; n < nparts; n++) + { + strcpy (names[n_names++], dest_name); + dest_name[8 - 1]++; + } + if (surplus != 0) + strcpy (names[n_names++], dest_name); + } + if (n_names == 1) + names[0][7] = 0; // 'A' causes trouble for 1-part split files + return n_names; +} + + +static void +snes_split_gd3 (st_rominfo_t *rominfo, int size) +{ + char dest_name[FILENAME_MAX], *names[GD3_MAX_UNITS], + names_mem[GD3_MAX_UNITS][9]; + int nparts, surplus, n, half_size, name_i = 0; + + // Don't use part_size here, because the Game Doctor doesn't support + // arbitrary part sizes + nparts = size / (8 * MBIT); + surplus = size % (8 * MBIT); + + // We don't want to malloc() ridiculously small chunks (of 9 bytes) + for (n = 0; n < GD3_MAX_UNITS; n++) + names[n] = names_mem[n]; + snes_make_gd_names (ucon64.rom, rominfo, (char **) names); + + if (snes_hirom && size <= 16 * MBIT) + { + half_size = size / 2; + + sprintf (dest_name, "%s.078", names[name_i++]); + ucon64_output_fname (dest_name, OF_FORCE_BASENAME); + // don't write backups of parts, because one name is used + fcopy (ucon64.rom, 0, half_size + rominfo->buheader_len, dest_name, "wb"); + printf (ucon64_msg[WROTE], dest_name); + + sprintf (dest_name, "%s.078", names[name_i++]); + ucon64_output_fname (dest_name, OF_FORCE_BASENAME); + fcopy (ucon64.rom, half_size + rominfo->buheader_len, size - half_size, dest_name, "wb"); + printf (ucon64_msg[WROTE], dest_name); + } + else + { + for (n = 0; n < nparts; n++) + { + // don't write backups of parts, because one name is used + sprintf (dest_name, "%s.078", names[name_i++]); + ucon64_output_fname (dest_name, OF_FORCE_BASENAME); + fcopy (ucon64.rom, n * 8 * MBIT + (n ? rominfo->buheader_len : 0), + 8 * MBIT + (n ? 0 : rominfo->buheader_len), dest_name, "wb"); + printf (ucon64_msg[WROTE], dest_name); + } + + if (surplus != 0) + { + // don't write backups of parts, because one name is used + sprintf (dest_name, "%s.078", names[name_i++]); + ucon64_output_fname (dest_name, OF_FORCE_BASENAME); + fcopy (ucon64.rom, n * 8 * MBIT + (n ? rominfo->buheader_len : 0), + surplus + (n ? 0 : rominfo->buheader_len), dest_name, "wb"); + printf (ucon64_msg[WROTE], dest_name); + } + } + + // An index file is not used by the GD, but by the MGD. We don't have a + // special function for splitting MGD files, so we do it here. + if (!rominfo->buheader_len) + mgd_write_index_file (names, name_i); +} + + +static void +snes_split_ufo (st_rominfo_t *rominfo, int size, int part_size) +{ + char header[512], dest_name[FILENAME_MAX], *p; + int nparts, surplus, n, nbytesdone; + + if (snes_hirom) + { + if (size > 32 * MBIT) + { + fprintf (stderr, "ERROR: HiROM > 32 Mbit -- conversion not yet implemented\n"); + return; + } + if (UCON64_ISSET (ucon64.part_size)) + printf ("WARNING: Splitting Super UFO HiROM, ignoring switch "OPTION_LONG_S"ssize\n"); + } + + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".1gm"); + ucon64_output_fname (dest_name, 0); + p = strrchr (dest_name, '.') + 1; + + ucon64_fread (header, 0, UFO_HEADER_LEN, ucon64.rom); + + if (snes_hirom) + { + typedef struct + { + unsigned char value; + unsigned char size; + unsigned char list[8]; + } st_value_list_t; + + st_value_list_t size_to_partsizes[5] = + { + { 2, 2, {1, 1} }, + { 4, 2, {2, 2} }, + { 12, 4, {4, 2, 4, 2} }, // 10 Mbit files are padded by snes_ufo() + { 20, 6, {4, 4, 2, 4, 4, 2} }, + { 32, 8, {4, 4, 4, 4, 4, 4, 4, 4} } + }, *size_to_partsizes_ptr = NULL; + st_value_list_t size_to_flags[8] = + { + { 2, 2, {0x10, 0} }, + { 4, 2, {0x10, 0} }, + { 8, 2, {0x10, 0} }, + { 12, 4, {0x40, 0x10, 0x10, 0} }, + { 16, 4, {0x40, 0x10, 0x10, 0} }, + { 20, 6, {0x40, 0x40, 0x10, 0x10, 0x10, 0} }, + { 24, 6, {0x40, 0x40, 0x10, 0x10, 0x10, 0} }, + { 32, 8, {0x40, 0x40, 0x40, 0x10, 0x10, 0x10, 0x10, 0} } + }, *size_to_flags_ptr = NULL; + int x = size / MBIT; + + nparts = 0; + surplus = 0; + for (n = 0; n < 4; n++) + if (size_to_partsizes[n].value == x) + { + size_to_partsizes_ptr = &size_to_partsizes[n]; + nparts = size_to_partsizes[n].size; + surplus = 0; + } + if (!size_to_partsizes_ptr) // size was not found + { + size_to_partsizes_ptr = &size_to_partsizes[4]; + nparts = size / (4 * MBIT); + surplus = size % (4 * MBIT); + } + + for (n = 0; n < 7; n++) + if (size_to_flags[n].value == x) + size_to_flags_ptr = &size_to_flags[n]; + if (!size_to_flags_ptr) + size_to_flags_ptr = &size_to_flags[7]; + + nbytesdone = rominfo->buheader_len; + for (n = 0; n < nparts; n++) + { + part_size = size_to_partsizes_ptr->list[n] * MBIT; + header[0] = part_size / 8192; + header[1] = part_size / 8192 >> 8; + header[2] = size_to_flags_ptr->list[n]; + + if (surplus == 0 && n == nparts - 1) + header[2] = 0; // last file -> clear bit 6 + + ucon64_fwrite (&header, 0, SWC_HEADER_LEN, dest_name, "wb"); + fcopy (ucon64.rom, nbytesdone, part_size, dest_name, "ab"); + printf (ucon64_msg[WROTE], dest_name); + + nbytesdone += part_size; + (*p)++; + } + } + else + { + nparts = size / part_size; + surplus = size % part_size; + + header[0] = part_size / 8192; + header[1] = part_size / 8192 >> 8; + header[2] |= 0x40; + + nbytesdone = rominfo->buheader_len; + for (n = 0; n < nparts; n++) + { + if (surplus == 0 && n == nparts - 1) + header[2] = 0; // last file -> clear bit 6 + + ucon64_fwrite (&header, 0, SWC_HEADER_LEN, dest_name, "wb"); + fcopy (ucon64.rom, nbytesdone, part_size, dest_name, "ab"); + printf (ucon64_msg[WROTE], dest_name); + + nbytesdone += part_size; + (*p)++; + } + } + + if (surplus != 0) + { + header[0] = surplus / 8192; + header[1] = surplus / 8192 >> 8; + header[2] = 0; // last file -> clear bit 6 + + ucon64_fwrite (&header, 0, SWC_HEADER_LEN, dest_name, "wb"); + fcopy (ucon64.rom, nbytesdone, surplus, dest_name, "ab"); + printf (ucon64_msg[WROTE], dest_name); + } +} + + +static void +snes_split_smc (st_rominfo_t *rominfo, int size, int part_size) +// this function splits both SWC and FIG files +{ + char header[512], dest_name[FILENAME_MAX], *p; + int nparts, surplus, n; + + nparts = size / part_size; + surplus = size % part_size; + + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".1"); + ucon64_output_fname (dest_name, 0); + p = strrchr (dest_name, '.') + 1; + + ucon64_fread (header, 0, SWC_HEADER_LEN, ucon64.rom); + header[0] = part_size / 8192; + header[1] = part_size / 8192 >> 8; + // if header[2], bit 6 == 0 -> SWC/FIG knows this is the last file of the ROM + header[2] |= 0x40; + + for (n = 0; n < nparts; n++) + { + if (surplus == 0 && n == nparts - 1) + header[2] &= ~0x40; // last file -> clear bit 6 + + // don't write backups of parts, because one name is used + ucon64_fwrite (header, 0, SWC_HEADER_LEN, dest_name, "wb"); + fcopy (ucon64.rom, n * part_size + rominfo->buheader_len, part_size, dest_name, "ab"); + printf (ucon64_msg[WROTE], dest_name); + + (*p)++; + } + + if (surplus != 0) + { + header[0] = surplus / 8192; + header[1] = surplus / 8192 >> 8; + header[2] &= ~0x40; // last file -> clear bit 6 + + // don't write backups of parts, because one name is used + ucon64_fwrite (header, 0, SWC_HEADER_LEN, dest_name, "wb"); + fcopy (ucon64.rom, n * part_size + rominfo->buheader_len, surplus, dest_name, "ab"); + printf (ucon64_msg[WROTE], dest_name); + } +} + + +#define PARTSIZE (8 * MBIT) // default split part size +int +snes_s (st_rominfo_t *rominfo) +{ + int size = ucon64.file_size - rominfo->buheader_len, part_size; + + if (UCON64_ISSET (ucon64.part_size) && !(type == GD3 || type == UFO)) + { + part_size = ucon64.part_size; + /* + Don't allow too small part sizes, because then the files that come + after the file with suffix ".9" will get filenames that can't be stored + on a FAT filesystem (filesystem that SWC requires to be on diskette). + For example the first file after the file with suffix ".9" will get + suffix ".:". The SWC does ask for that filename though. + Also don't base the minimum part size on the actual file size, because + that will probably only confuse users. + We ignore the few ROMs that are greater than 32 MBit. Just use -ssize + to specify a larger part size for those. + */ + if (part_size < 4 * MBIT) + { + fprintf (stderr, + "ERROR: Split part size must be larger than or equal to 4 Mbit\n"); + return -1; + } + } + else + part_size = PARTSIZE; + + if (type == GD3) + // part_size is ignored for Game Doctor + { + if (size < 4 * MBIT && size != 2 * MBIT) + { // "&& size != 2 * MBIT" is a fix for BS Chrono Trigger - Jet Bike Special (J) + printf ("NOTE: ROM size is smaller than 4 Mbit -- won't be split\n"); + return -1; + } + } + else if (type == UFO && snes_hirom) + { + if (size < 2 * MBIT) + { + printf ("NOTE: ROM size is smaller than 2 Mbit -- won't be split\n"); + return -1; + } + } + else if (size <= part_size) + { + printf ("NOTE: ROM size is smaller than or equal to %d Mbit -- won't be split\n", + part_size / MBIT); + return -1; + } + + if (!rominfo->buheader_len || type == GD3) // GD3 format + { + if (UCON64_ISSET (ucon64.part_size)) + printf ("WARNING: ROM will be split as Game Doctor SF3 ROM, ignoring switch "OPTION_LONG_S"ssize\n"); + snes_split_gd3 (rominfo, size); + } + else if (type == UFO) + snes_split_ufo (rominfo, size, part_size); + else + snes_split_smc (rominfo, size, part_size); + + return 0; +} + + +int +snes_j (st_rominfo_t *rominfo) +{ + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX], *p = NULL; + int block_size, total_size = 0, header_len = rominfo->buheader_len; + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + set_suffix (dest_name, ".tmp"); + + ucon64_file_handler (dest_name, NULL, 0); + fcopy (src_name, 0, rominfo->buheader_len, dest_name, "wb"); // copy header (if any) + + p = strrchr (src_name, '.'); + if (p == NULL) // filename doesn't contain a period + p = src_name + strlen (src_name) - 1; + else + (type == GD3 || type == MGD_SNES) ? p-- : p++; + + // Split GD3 files don't have a header _except_ the first one + block_size = fsizeof (src_name) - header_len; + while (fcopy (src_name, header_len, block_size, dest_name, "ab") != -1) + { + printf ("Joined: %s\n", src_name); + total_size += block_size; + (*p)++; + + if (type == GD3) + header_len = 0; + block_size = fsizeof (src_name) - header_len; + } + + if (rominfo->buheader_len && type != GD3) + { // fix header + ucon64_fputc (dest_name, 0, total_size / 8192, "r+b"); // # 8K blocks low byte + ucon64_fputc (dest_name, 1, total_size / 8192 >> 8, "r+b"); // # 8K blocks high byte + ucon64_fputc (dest_name, 2, ucon64_fgetc (dest_name, 2) & ~0x40, "r+b"); // last file -> clear bit 6 + } + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +snes_k (st_rominfo_t *rominfo) +/* +See the document "src/backup/SWC-compatibility.txt". +Don't touch this code if you don't know what you're doing! + +Some SNES games check to see how much SRAM is connected to the SNES as a form +of copy protection. As most copiers have 256 kbits standard, the game will +know it's running on a backup unit and stop to prevent people copying the +games. However, newer copiers like the SWC DX2 get around this detection by +limiting the SRAM size for the game to the size specified in the backup unit +header. + +(original uCON) + 8f/9f XX YY 70 cf/df XX YY 70 d0 +=> 8f/9f XX YY 70 cf/df XX YY 70 ea ea if snes_sramsize == 64 kbits +=> 8f/9f XX YY 70 cf/df XX YY 70 80 + + sta $70YYXX/sta $70YYXX,x; cmp $70YYXX/cmp $70YYXX,x; bne + +TODO: The following three codes should be verified for many games. For example, +the first code replaces D0 (bne) with 80 (bra), but for some games (like Donkey +Kong Country (U|E)) it should do the opposite, i.e., writing EA EA (nop nop). + 8f/9f XX YY 30/31/32/33 cf/df XX YY 30/31/32/33 d0 +=> 8f/9f XX YY 30/31/32/33 cf/df XX YY 30/31/32/33 80 + + 8f/9f XX YY 30/31/32/33 cf/df XX YY 30/31/32/33 f0 beq +=> 8f/9f XX YY 30/31/32/33 cf/df XX YY 30/31/32/33 ea ea nop; nop + + 8f/9f XX YY 30/31/32/33 af XX YY 30/31/32/33 c9 XX YY d0 bne +=> 8f/9f XX YY 30/31/32/33 af XX YY 30/31/32/33 c9 XX YY 80 bra + +(uCON64) +- Mega Man X + 8f/9f XX YY 70 cf/df XX YY 70 f0 +=> 8f/9f XX YY 70 cf/df XX YY 70 ea ea +The code above could be combined with the first original uCON code. However, we +don't want to copy (or remove) the SRAM size determined behaviour, without +knowing when that is necessary. + +- Mega Man X + af/bf XX 80 00 cf/df XX 80 40 f0 +=> af/bf XX 80 00 cf/df XX 80 40 80 + + lda $0080XX/lda $0080XX,x; cmp $408000/cmp $408000,x; beq ... + +- Demon's Crest (af, 80, cf) / Breath of Fire II (bf, c0, df) + af/bf XX ff 80/c0 cf/df XX ff 40 f0 +=> af/bf XX ff 80/c0 cf/df XX ff 40 80 + +- Breath of Fire II (bf, 30, df, 31) + af/bf XX YY 30/31/32/33 cf/df XX YY 30/31/32/33 f0 +=> af/bf XX YY 30/31/32/33 cf/df XX YY 30/31/32/33 80 + +- Super Metroid + a9 00 00 a2 fe 1f df 00 00 70 d0 lda #$0000; ldx #$1ffe; cmp $700000,x; bne ... +=> a9 00 00 a2 fe 1f df 00 00 70 ea ea lda #$0000; ldx #$1ffe; cmp $700000,x; nop; nop + +- Tetris Attack +(generic) + 8f XX YY 70 af XX YY 70 c9 XX YY d0 +=> 8f XX YY 70 af XX YY 70 c9 XX YY 80 +(game specific) + c2 30 ad fc 1f c9 50 44 d0 +=> c2 30 4c d1 80 c9 50 44 d0 + +- Uniracers/Unirally + 8f XX YY 77 e2 XX af XX YY 77 c9 XX f0 +=> 8f XX YY 77 e2 XX af XX YY 77 c9 XX 80 + +- Mario no Super Picross + 8f/af XX YY b0 cf XX YY b1 d0 +=> 8f/af XX YY b0 cf XX YY b1 ea ea + +- most probably only Killer Instinct + 5c 7f d0 83 18 fb 78 c2 30 jmp $83d07f; clc; xce; sei; rep #$30 +=> ea ea ea ea ea ea ea ea ea nop; nop; nop; nop; nop; nop; nop; nop; nop + +- most probably only Donkey Kong Country (8f, 30, cf, 30) +Note that this code must be searched for before the less specific uCON code. + 8f/9f 57/59 60/68 30/31/32/33 cf/df 57/59 60 30/31/32/33 d0 +=> 8f/9f 57/59 60/68 30/31/32/33 cf/df 57/59 60 30/31/32/33 ea ea + +- most probably only Diddy's Kong Quest + 26 38 e9 48 12 c9 af 71 f0 +=> 26 38 e9 48 12 c9 af 71 80 + +- most probably only Diddy's Kong Quest + a0 5c 2f 77 32 e9 c7 04 f0 +=> a0 5c 2f 77 32 e9 c7 04 80 + +- most probably only Diddy's Kong Quest + 'K' 'O' 'N' 'G' 00 f8 f7 +=> 'K' 'O' 'N' 'G' 00 f8 f8 +TODO: make sense of Diddy's Kong Quest codes + +- most probably only BS The Legend of Zelda Remix + 22 08 5c 10 b0 28 jsl $105c08; bcs ... +=> ea ea ea ea ea ea nop; nop; nop; nop; nop; nop + +- most probably only BS The Legend of Zelda Remix (enables music) + da e2 30 c9 01 f0 18 c9 02 +=> da e2 30 c9 09 f0 18 c9 07 + +- most probably only BS The Legend of Zelda Remix (enables music) + 29 ff 00 c9 07 00 90 16 +=> 29 ff 00 c9 00 00 90 16 + +- most probably only Kirby's Dream Course + ca 10 f8 38 ef 1a 80 81 8d +=> ca 10 f8 38 ef 1a 80 81 9c + +- most probably only Kirby's Dream Course + 81 ca 10 f8 cf 39 80 87 f0 +=> 81 ca 10 f8 cf 39 80 87 80 + +- probably only Earthbound + 84 26 ad 39 b5 d0 1a +=> 84 26 ad 39 b5 ea ea +I don't know what this does, but it isn't necessary to start the game. + +- probably only Earthbound + 10 f8 38 ef ef ff c1 +=> 10 f8 38 ea a9 00 00 + +- probably only Earthbound + 10 f8 38 ef f2 fd c3 f0 +=> 10 f8 38 ea a9 00 00 80 +Same here. + +- Dixie Kong's Double Trouble E. U version looks like it already has been "patched" + a9 c3 80 dd ff ff f0 6c +=> a9 c3 f0 cc ff ff 80 7d + +- Front Mission - Gun Hazard + d0 f4 ab cf ae ff 00 d0 01 +=> d0 f4 ab cf ae ff 00 d0 00 +Modification protection. Not needed to play the game on an NTSC SNES. Needed +when it has been patched with -f. +*/ +{ + char header[512], src_name[FILENAME_MAX], dest_name[FILENAME_MAX], + buffer[32 * 1024]; + FILE *srcfile, *destfile; + int bytesread, n = 0, n_extra_patterns, n2; + st_cm_pattern_t *patterns = NULL; + + strcpy (src_name, "snescopy.txt"); + // First try the current directory, then the configuration directory + if (access (src_name, F_OK | R_OK) == -1) + sprintf (src_name, "%s" FILE_SEPARATOR_S "snescopy.txt", ucon64.configdir); + n_extra_patterns = build_cm_patterns (&patterns, src_name, ucon64.quiet == -1 ? 1 : 0); + if (n_extra_patterns >= 0) + printf ("Found %d additional code%s in %s\n", + n_extra_patterns, n_extra_patterns != 1 ? "s" : "", src_name); + + puts ("Attempting crack..."); + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, src_name, 0); + if ((srcfile = fopen (src_name, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], src_name); + return -1; + } + if ((destfile = fopen (dest_name, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], dest_name); + return -1; + } + if (rominfo->buheader_len) // copy header (if present) + { + fread (header, 1, SWC_HEADER_LEN, srcfile); + fseek (srcfile, rominfo->buheader_len, SEEK_SET); + fwrite (header, 1, SWC_HEADER_LEN, destfile); + } + + while ((bytesread = fread (buffer, 1, 32 * 1024, srcfile))) + { // '!' == ASCII 33 (\x21), '*' == 42 (\x2a) + // First use the extra patterns, so that their precedence is higher than + // the built-in patterns + for (n2 = 0; n2 < n_extra_patterns; n2++) + n += change_mem2 (buffer, bytesread, + patterns[n2].search, + patterns[n2].search_size, + patterns[n2].wildcard, + patterns[n2].escape, + patterns[n2].replace, + patterns[n2].replace_size, + patterns[n2].offset, + patterns[n2].sets); + + // SRAM + if (snes_sramsize == 8 * 1024) // 8 kB == 64 kb + { + n += change_mem (buffer, bytesread, "!**\x70!**\x70\xd0", 9, '*', '!', "\xea\xea", 2, 0, + "\x8f\x9f", 2, "\xcf\xdf", 2); + // actually Kirby's Dream Course, Lufia II - Rise of the Sinistrals + n += change_mem (buffer, bytesread, "!**\x70!**\x70\xf0", 9, '*', '!', "\x80", 1, 0, + "\x8f\x9f", 2, "\xcf\xdf", 2); +#if 1 // TODO: check which games really need this + n += change_mem (buffer, bytesread, "!**!!**!\xf0", 9, '*', '!', "\x80", 1, 0, + "\x8f\x9f", 2, "\x30\x31\x32\x33", 4, "\xcf\xdf", 2, "\x30\x31\x32\x33", 4); +#endif + } + else + { + n += change_mem (buffer, bytesread, "!**\x70!**\x70\xd0", 9, '*', '!', "\x80", 1, 0, + "\x8f\x9f", 2, "\xcf\xdf", 2); + // Mega Man X + n += change_mem (buffer, bytesread, "!**\x70!**\x70\xf0", 9, '*', '!', "\xea\xea", 2, 0, + "\x8f\x9f", 2, "\xcf\xdf", 2); + n += change_mem (buffer, bytesread, "!**!!**!\xf0", 9, '*', '!', "\xea\xea", 2, 0, + "\x8f\x9f", 2, "\x30\x31\x32\x33", 4, "\xcf\xdf", 2, "\x30\x31\x32\x33", 4); + } + + n += change_mem (buffer, bytesread, "\x8f**\x77\xe2*\xaf**\x77\xc9*\xf0", 13, '*', '!', "\x80", 1, 0); + n += change_mem (buffer, bytesread, "!!!!!!\x60!\xd0", 9, '*', '!', "\xea\xea", 2, 0, + "\x8f\x9f", 2, "\x57\x59", 2, "\x60\x68", 2, "\x30\x31\x32\x33", 4, + "\xcf\xdf", 2, "\x57\x59", 2, "\x30\x31\x32\x33", 4); + + n += change_mem (buffer, bytesread, "!**!!**!\xd0", 9, '*', '!', "\x80", 1, 0, + "\x8f\x9f", 2, "\x30\x31\x32\x33", 4, "\xcf\xdf", 2, "\x30\x31\x32\x33", 4); + n += change_mem (buffer, bytesread, "!**\xb0\xcf**\xb1\xd0", 9, '*', '!', "\xea\xea", 2, 0, + "\x8f\xaf", 2); + n += change_mem (buffer, bytesread, "!**!\xaf**!\xc9**\xd0", 12, '*', '!', "\x80", 1, 0, + "\x8f\x9f", 2, "\x30\x31\x32\x33", 4, "\x30\x31\x32\x33", 4); + n += change_mem (buffer, bytesread, "\xa9\x00\x00\xa2\xfe\x1f\xdf\x00\x00\x70\xd0", 11, '*', '!', "\xea\xea", 2, 0); + n += change_mem (buffer, bytesread, "\x8f**\x70\xaf**\x70\xc9**\xd0", 12, '*', '!', "\x80", 1, 0); + n += change_mem (buffer, bytesread, "!**!!**!\xf0", 9, '*', '!', "\x80", 1, 0, + "\xaf\xbf", 2, "\x30\x31\x32\x33", 4, "\xcf\xdf", 2, "\x30\x31\x32\x33", 4); + + // mirroring + n += change_mem (buffer, bytesread, "!*\x80\x00!*\x80\x40\xf0", 9, '*', '!', "\x80", 1, 0, + "\xaf\xbf", 2, "\xcf\xdf", 2); + n += change_mem (buffer, bytesread, "!*\xff!!*\xff\x40\xf0", 9, '*', '!', "\x80", 1, 0, + "\xaf\xbf", 2, "\x80\xc0", 2, "\xcf\xdf", 2); + + // game specific + n += change_mem (buffer, bytesread, "\x5c\x7f\xd0\x83\x18\xfb\x78\xc2\x30", 9, '*', '!', + "\xea\xea\xea\xea\xea\xea\xea\xea\xea", 9, -8); + + n += change_mem (buffer, bytesread, "KONG\x00\xf8\xf7", 7, '*', '!', "\xf8", 1, 0); + n += change_mem (buffer, bytesread, "\x26\x38\xe9\x48\x12\xc9\xaf\x71\xf0", 9, '*', '!', "\x80", 1, 0); + n += change_mem (buffer, bytesread, "\xa0\x5c\x2f\x77\x32\xe9\xc7\x04\xf0", 9, '*', '!', "\x80", 1, 0); + + n += change_mem (buffer, bytesread, "\x22\x08\x5c\x10\xb0\x28", 6, '*', '!', + "\xea\xea\xea\xea\xea\xea", 6, -5); + n += change_mem (buffer, bytesread, "\xda\xe2\x30\xc9\x01\xf0\x18\xc9\x02", 9, '*', '!', + "\x09\xf0\x18\xc9\x07", 5, -4); + n += change_mem (buffer, bytesread, "\x29\xff\x00\xc9\x07\x00\x90\x16", 8, '*', '!', "\x00", 1, -3); + + n += change_mem (buffer, bytesread, "\xca\x10\xf8\x38\xef\x1a\x80\x81\x8d", 9, '*', '!', "\x9c", 1, 0); + n += change_mem (buffer, bytesread, "\x81\xca\x10\xf8\xcf\x39\x80\x87\xf0", 9, '*', '!', "\x80", 1, 0); + + n += change_mem (buffer, bytesread, "\x84\x26\xad\x39\xb5\xd0\x1a", 7, '*', '!', "\xea\xea", 2, -1); + n += change_mem (buffer, bytesread, "\x10\xf8\x38\xef\xef\xff\xc1", 7, '*', '!', + "\xea\xa9\x00\x00", 4, -3); + n += change_mem (buffer, bytesread, "\x10\xf8\x38\xef\xf2\xfd\xc3\xf0", 8, '*', '!', + "\xea\xa9\x00\x00\x80", 5, -4); + + n += change_mem (buffer, bytesread, "\xc2\x30\xad\xfc\x1f\xc9\x50\x44\xd0", 9, '*', '!', "\x4c\xd1\x80", 3, -6); + n += change_mem (buffer, bytesread, "\xa9\xc3\x80\xdd\xff\xff\xf0\x6c", 8, '*', '!', "\xf0\xcc\xff\xff\x80\x7d", 6, -5); + n += change_mem (buffer, bytesread, "\xd0\xf4\xab\xcf\xae\xff\x00\xd0\x01", 9, '*', '!', "\x00", 1, 0); + + fwrite (buffer, 1, bytesread, destfile); + } + fclose (srcfile); + fclose (destfile); + cleanup_cm_patterns (&patterns, n_extra_patterns); + + printf ("Found %d pattern%s\n", n, n != 1 ? "s" : ""); + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return n; +} + + +static int +snes_fix_pal_protection (st_rominfo_t *rominfo) +/* +This function searches for PAL protection codes. If it finds one it will +fix the code so that the game will run on an NTSC SNES. +Don't touch this code if you don't know what you're doing! + +Search for Replace with +ad 3f 21 89 10 d0 ad 3f 21 89 10 80 - Terranigma +ad 3f 21 29 10 00 d0 ad 3f 21 29 10 00 80 +ad 3f 21 89 10 00 d0 a9 10 00 89 10 00 d0 - Eric Cantona Football ? +ad 3f 21 29 10 cf bd ff XX f0 ad 3f 21 29 10 cf bd ff XX 80 - Pop'n Twinbee E +af 3f 21 00 29 10 d0 af 3f 21 00 29 10 80 +af 3f 21 00 29 10 00 d0 af 3f 21 00 29 10 00 ea ea +af 3f 21 00 29 XX c9 XX f0 af 3f 21 00 29 XX c9 XX 80 - Secret of Mana E +a2 18 01 bd 27 20 89 10 00 f0 01 a2 18 01 bd 27 20 89 10 00 ea ea - Donkey Kong Country E +*/ +{ + char header[512], src_name[FILENAME_MAX], dest_name[FILENAME_MAX], + buffer[32 * 1024]; + FILE *srcfile, *destfile; + int bytesread, n = 0, n_extra_patterns, n2; + st_cm_pattern_t *patterns = NULL; + + strcpy (src_name, "snespal.txt"); + // First try the current directory, then the configuration directory + if (access (src_name, F_OK | R_OK) == -1) + sprintf (src_name, "%s" FILE_SEPARATOR_S "snespal.txt", ucon64.configdir); + n_extra_patterns = build_cm_patterns (&patterns, src_name, ucon64.quiet == -1 ? 1 : 0); + if (n_extra_patterns >= 0) + printf ("Found %d additional code%s in %s\n", + n_extra_patterns, n_extra_patterns != 1 ? "s" : "", src_name); + + puts ("Attempting to fix PAL protection code..."); + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, src_name, 0); + if ((srcfile = fopen (src_name, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], src_name); + return -1; + } + if ((destfile = fopen (dest_name, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], dest_name); + return -1; + } + if (rominfo->buheader_len) // copy header (if present) + { + fread (header, 1, SWC_HEADER_LEN, srcfile); + fseek (srcfile, rominfo->buheader_len, SEEK_SET); + fwrite (header, 1, SWC_HEADER_LEN, destfile); + } + + while ((bytesread = fread (buffer, 1, 32 * 1024, srcfile))) + { + // First use the extra patterns, so that their precedence is higher than + // the built-in patterns + for (n2 = 0; n2 < n_extra_patterns; n2++) + n += change_mem2 (buffer, bytesread, + patterns[n2].search, + patterns[n2].search_size, + patterns[n2].wildcard, + patterns[n2].escape, + patterns[n2].replace, + patterns[n2].replace_size, + patterns[n2].offset, + patterns[n2].sets); + + n += change_mem (buffer, bytesread, "\xad\x3f\x21\x89\x10\xd0", 6, '\x01', '\x02', "\x80", 1, 0); + n += change_mem (buffer, bytesread, "\xad\x3f\x21\x29\x10\x00\xd0", 7, '\x01', '\x02', "\x80", 1, 0); + n += change_mem (buffer, bytesread, "\xad\x3f\x21\x89\x10\x00\xd0", 7, '\x01', '\x02', "\xa9\x10\x00", 3, -6); + n += change_mem (buffer, bytesread, "\xad\x3f\x21\x29\x10\xcf\xbd\xff\x01\xf0", 10, '\x01', '\x02', "\x80", 1, 0); + n += change_mem (buffer, bytesread, "\xaf\x3f\x21\x00\x29\x10\xd0", 7, '\x01', '\x02', "\x80", 1, 0); + n += change_mem (buffer, bytesread, "\xaf\x3f\x21\x00\x29\x10\x00\xd0", 8, '\x01', '\x02', "\xea\xea", 2, 0); + n += change_mem (buffer, bytesread, "\xaf\x3f\x21\x00\x29\x01\xc9\x01\xf0", 9, '\x01', '\x02', "\x80", 1, 0); + n += change_mem (buffer, bytesread, "\xa2\x18\x01\xbd\x27\x20\x89\x10\x00\xf0\x01", 11, '*', '!', "\xea\xea", 2, -1); + + fwrite (buffer, 1, bytesread, destfile); + } + fclose (srcfile); + fclose (destfile); + cleanup_cm_patterns (&patterns, n_extra_patterns); + + printf ("Found %d pattern%s\n", n, n != 1 ? "s" : ""); + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return n; +} + + +static int +snes_fix_ntsc_protection (st_rominfo_t *rominfo) +/* +This function searches for NTSC protection codes. If it finds one it will +fix the code so that the game will run on a PAL SNES. +Don't touch this code if you don't know what you're doing! + +Search for Replace with +3f 21 29/89 10 f0 3f 21 29/89 10 80 +ad 3f 21 29 10 d0 ad 3f 21 29 10 ea ea +ad 3f 21 89 10 d0 ad 3f 21 89 10 80/(ea ea) - Live-a-Live (ea ea) +3f 21 29/89 10 00 f0 3f 21 29/89 10 00 80 - Clock Tower (29) +3f 21 29/89 10 00 d0 3f 21 29/89 10 00 ea ea - Mario no Super Picross (89) + 3f 21 89 10 c2 XX f0 3f 21 89 10 c2 XX 80 - Front Mission - Gun Hazard + 3f 21 89 10 c2 XX d0 3f 21 89 10 c2 XX ea ea - Robotrek +3f 21 29/89 10 c9 10 f0 3f 21 29/89 10 c9 10 80 +ad 3f 21 29 10 c9 00 f0 ad 3f 21 29 10 c9 00 80/(ea ea) <= original uCON used 80 +ad 3f 21 29 10 c9 00 d0 ad 3f 21 29 10 c9 00 80 +ad 3f 21 29 10 c9 10 d0 ad 3f 21 29 10 c9 10 ea ea + 3f 21 29 10 cf XX YY 80 f0 3f 21 29 10 cf XX YY 80 80 - Gokujyou Parodius/Tokimeki Memorial +ad 3f 21 8d XX YY 29 10 8d ad 3f 21 8d XX YY 29 00 8d - Dragon Ball Z - Super Butoden 2 ? + 3f 21 00 29/89 10 f0 3f 21 00 29/89 10 80 - Kirby's Dream Course U (29) +af 3f 21 00 29/89 10 d0 af 3f 21 00 29/89 10 ea ea - Kirby No Kira Kizzu (29)/Final Fight Guy (89) +af 3f 21 00 29/89 10 00 f0 af 3f 21 00 29/89 10 00 80 +af 3f 21 00 29 XX c9 XX f0 af 3f 21 00 29 XX c9 XX 80 - Seiken Densetsu 3 +af 3f 21 00 29 10 80 2d 00 1b af 3f 21 00 29 00 80 2d 00 1b - Seiken Densetsu 2/Secret of Mana U + 3f 21 00 89 10 c2 XX f0 3f 21 00 89 10 c2 XX 80 - Dragon - The Bruce Lee Story U +af 3f 21 00 XX YY 29 10 00 d0 af 3f 21 00 XX YY 29 10 00 ea ea - Fatal Fury Special ? + 3f 21 c2 XX 29 10 00 f0 3f 21 c2 XX 29 10 00 80 - Metal Warriors + 3f 21 c2 XX 29 10 00 d0 3f 21 c2 XX 29 10 00 ea ea - Dual Orb 2 +af 3f 21 ea 89 10 00 d0 a9 00 00 ea 89 10 00 d0 - Super Famista 3 ? +a2 18 01 bd 27 20 89 10 00 d0 01 a2 18 01 bd 27 20 89 10 00 ea ea - Donkey Kong Country U +29 10 00 a2 00 00 c9 10 00 d0 29 10 00 a2 00 00 c9 10 00 80 - Wolfenstein 3D U +*/ +{ + char header[512], src_name[FILENAME_MAX], dest_name[FILENAME_MAX], + buffer[32 * 1024]; + FILE *srcfile, *destfile; + int bytesread, n = 0, n_extra_patterns, n2; + st_cm_pattern_t *patterns = NULL; + + strcpy (src_name, "snesntsc.txt"); + // First try the current directory, then the configuration directory + if (access (src_name, F_OK | R_OK) == -1) + sprintf (src_name, "%s" FILE_SEPARATOR_S "snesntsc.txt", ucon64.configdir); + n_extra_patterns = build_cm_patterns (&patterns, src_name, ucon64.quiet == -1 ? 1 : 0); + if (n_extra_patterns >= 0) + printf ("Found %d additional code%s in %s\n", + n_extra_patterns, n_extra_patterns != 1 ? "s" : "", src_name); + + puts ("Attempting to fix NTSC protection code..."); + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, src_name, 0); + if ((srcfile = fopen (src_name, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], src_name); + return -1; + } + if ((destfile = fopen (dest_name, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], dest_name); + return -1; + } + if (rominfo->buheader_len) // copy header (if present) + { + fread (header, 1, SWC_HEADER_LEN, srcfile); + fseek (srcfile, rominfo->buheader_len, SEEK_SET); + fwrite (header, 1, SWC_HEADER_LEN, destfile); + } + + while ((bytesread = fread (buffer, 1, 32 * 1024, srcfile))) + { + // First use the extra patterns, so that their precedence is higher than + // the built-in patterns + for (n2 = 0; n2 < n_extra_patterns; n2++) + n += change_mem2 (buffer, bytesread, + patterns[n2].search, + patterns[n2].search_size, + patterns[n2].wildcard, + patterns[n2].escape, + patterns[n2].replace, + patterns[n2].replace_size, + patterns[n2].offset, + patterns[n2].sets); + + n += change_mem (buffer, bytesread, "\x3f\x21\x02\x10\xf0", 5, '\x01', '\x02', "\x80", 1, 0, + "\x29\x89", 2); + n += change_mem (buffer, bytesread, "\xad\x3f\x21\x29\x10\xd0", 6, '\x01', '\x02', "\xea\xea", 2, 0); + n += change_mem (buffer, bytesread, "\xad\x3f\x21\x89\x10\xd0", 6, '\x01', '\x02', "\xea\xea", 2, 0); +// The next statement could be the alternative for the previous one. Leave it +// disabled until we find a game that needs it. +// n += change_mem (buffer, bytesread, "\xad\x3f\x21\x89\x10\xd0", 6, '\x01', '\x02', "\x80", 1, 0); + n += change_mem (buffer, bytesread, "\x3f\x21\x02\x10\x00\xf0", 6, '\x01', '\x02', "\x80", 1, 0, + "\x29\x89", 2); + n += change_mem (buffer, bytesread, "\x3f\x21\x02\x10\x00\xd0", 6, '\x01', '\x02', "\xea\xea", 2, 0, + "\x29\x89", 2); + n += change_mem (buffer, bytesread, "\x3f\x21\x89\x10\xc2\x01\xf0", 7, '\x01', '\x02', "\x80", 1, 0); + n += change_mem (buffer, bytesread, "\x3f\x21\x89\x10\xc2\x01\xd0", 7, '\x01', '\x02', "\xea\xea", 2, 0); + n += change_mem (buffer, bytesread, "\x3f\x21\x02\x10\xc9\x10\xf0", 7, '\x01', '\x02', "\x80", 1, 0, + "\x29\x89", 2); + n += change_mem (buffer, bytesread, "\xad\x3f\x21\x29\x10\xc9\x00\xf0", 8, '\x01', '\x02', "\xea\xea", 2, 0); + n += change_mem (buffer, bytesread, "\xad\x3f\x21\x29\x10\xc9\x00\xd0", 8, '\x01', '\x02', "\x80", 1, 0); + n += change_mem (buffer, bytesread, "\xad\x3f\x21\x29\x10\xc9\x10\xd0", 8, '\x01', '\x02', "\xea\xea", 2, 0); + n += change_mem (buffer, bytesread, "\x3f\x21\x29\x10\xcf\x01\x01\x80\xf0", 9, '\x01', '\x02', "\x80", 1, 0); + n += change_mem (buffer, bytesread, "\xad\x3f\x21\x8d\x01\x01\x29\x10\x8d", 9, '\x01', '\x02', "\x00", 1, -1); + n += change_mem (buffer, bytesread, "\x3f\x21\x00\x02\x10\xf0", 6, '\x01', '\x02', "\x80", 1, 0, + "\x29\x89", 2); + n += change_mem (buffer, bytesread, "\xaf\x3f\x21\x00\x02\x10\xd0", 7, '\x01', '\x02', "\xea\xea", 2, 0, + "\x29\x89", 2); + n += change_mem (buffer, bytesread, "\xaf\x3f\x21\x00\x02\x10\x00\xf0", 8, '\x01', '\x02', "\x80", 1, 0, + "\x29\x89", 2); + n += change_mem (buffer, bytesread, "\xaf\x3f\x21\x00\x29\x01\xc9\x01\xf0", 9, '\x01', '\x02', "\x80", 1, 0); + n += change_mem (buffer, bytesread, "\xaf\x3f\x21\x00\x29\x10\x80\x2d\x00\x1b", 10, '\x01', '\x02', "\x00", 1, -4); + n += change_mem (buffer, bytesread, "\x3f\x21\x00\x89\x10\xc2\x01\xf0", 8, '\x01', '\x02', "\x80", 1, 0); + n += change_mem (buffer, bytesread, "\xaf\x3f\x21\x00\x01\x01\x29\x10\x00\xd0", 10, '\x01', '\x02', "\xea\xea", 2, 0); + n += change_mem (buffer, bytesread, "\x3f\x21\xc2\x01\x29\x10\x00\xf0", 8, '\x01', '\x02', "\x80", 1, 0); + n += change_mem (buffer, bytesread, "\x3f\x21\xc2\x01\x29\x10\x00\xd0", 8, '\x01', '\x02', "\xea\xea", 2, 0); + n += change_mem (buffer, bytesread, "\xaf\x3f\x21\xea\x89\x10\x00\xd0", 8, '\x01', '\x02', "\xa9\x00\x00", 3, -7); + n += change_mem (buffer, bytesread, "\xa2\x18\x01\xbd\x27\x20\x89\x10\x00\xd0\x01", 11, '*', '!', "\xea\xea", 2, -1); + n += change_mem (buffer, bytesread, "\x29\x10\x00\xa2\x00\x00\xc9\x10\x00\xd0", 10, '\x01', '\x02', "\x80", 1, 0); + + fwrite (buffer, 1, bytesread, destfile); + } + fclose (srcfile); + fclose (destfile); + cleanup_cm_patterns (&patterns, n_extra_patterns); + + printf ("Found %d pattern%s\n", n, n != 1 ? "s" : ""); + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return n; +} + + +int +snes_f (st_rominfo_t *rominfo) +// See the document "src/backup/NTSC-PAL notes.txt". +{ + switch (snes_header.country) + { + // In the Philipines the television standard is NTSC, but do games made + // for the Philipines exist? + case 0: // Japan + case 1: // U.S.A. + return snes_fix_ntsc_protection (rominfo); + default: + return snes_fix_pal_protection (rominfo); + } +} + + +int +snes_l (st_rominfo_t *rominfo) +/* +The order is important. Don't touch this code if you don't know what you're doing! + +Search for Replace with +(uCON64) +8c/8d/8e/8f 0d 42 9c 0d 42 +01 0d 42 00 0d 42 +a9 01 85 0d a9 00 85 0d // special one (used by Konami and Jaleco? sometimes) +a2 01 86 0d a2 00 86 0d +a0 01 84 0d a0 00 84 0d + +(original uCON) +a9/a2 01 8d/8e 0d 42 a9/a2 00 8d/8e 0d 42 +a9 01 00 8d 0d 42 a9 00 00 8d 0d 42 +a9 01 8f 0d 42 00 a9 00 8f 0d 42 00 +*/ +{ + char header[512], src_name[FILENAME_MAX], dest_name[FILENAME_MAX], + buffer[32 * 1024]; + FILE *srcfile, *destfile; + int bytesread, n = 0, n_extra_patterns, n2; + st_cm_pattern_t *patterns = NULL; + + strcpy (src_name, "snesslow.txt"); + // First try the current directory, then the configuration directory + if (access (src_name, F_OK | R_OK) == -1) + sprintf (src_name, "%s" FILE_SEPARATOR_S "snesslow.txt", ucon64.configdir); + n_extra_patterns = build_cm_patterns (&patterns, src_name, ucon64.quiet == -1 ? 1 : 0); + if (n_extra_patterns >= 0) + printf ("Found %d additional code%s in %s\n", + n_extra_patterns, n_extra_patterns != 1 ? "s" : "", src_name); + + puts ("Attempting SlowROM fix..."); + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, src_name, 0); + if ((srcfile = fopen (src_name, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], src_name); + return -1; + } + if ((destfile = fopen (dest_name, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], dest_name); + return -1; + } + if (rominfo->buheader_len) // copy header (if present) + { + fread (header, 1, SWC_HEADER_LEN, srcfile); + fseek (srcfile, rominfo->buheader_len, SEEK_SET); + fwrite (header, 1, SWC_HEADER_LEN, destfile); + } + + while ((bytesread = fread (buffer, 1, 32 * 1024, srcfile))) + { // '!' == ASCII 33 (\x21), '*' == 42 (\x2a) + // First use the extra patterns, so that their precedence is higher than + // the built-in patterns + for (n2 = 0; n2 < n_extra_patterns; n2++) + n += change_mem2 (buffer, bytesread, + patterns[n2].search, + patterns[n2].search_size, + patterns[n2].wildcard, + patterns[n2].escape, + patterns[n2].replace, + patterns[n2].replace_size, + patterns[n2].offset, + patterns[n2].sets); + + n += change_mem (buffer, bytesread, "!\x0d\x42", 3, '*', '!', "\x9c", 1, -2, + "\x8c\x8d\x8e\x8f", 4); + n += change_mem (buffer, bytesread, "\x01\x0d\x42", 3, '*', '!', "\x00", 1, -2); + n += change_mem (buffer, bytesread, "\xa9\x01\x85\x0d", 4, '*', '!', "\x00", 1, -2); + n += change_mem (buffer, bytesread, "\xa2\x01\x86\x0d", 4, '*', '!', "\x00", 1, -2); + n += change_mem (buffer, bytesread, "\xa0\x01\x84\x0d", 4, '*', '!', "\x00", 1, -2); + + // original uCON + n += change_mem (buffer, bytesread, "!\x01!\x0d\x42", 5, '*', '!', "\x00", 1, -3, + "\xa9\xa2", 2, "\x8d\x8e", 2); + n += change_mem (buffer, bytesread, "\xa9\x01\x00\x8d\x0d\x42", 6, '*', '!', "\x00", 1, -4); + n += change_mem (buffer, bytesread, "\xa9\x01\x8f\x0d\x42\x00", 6, '*', '!', "\x00", 1, -4); + + fwrite (buffer, 1, bytesread, destfile); + } + fclose (srcfile); + fclose (destfile); + cleanup_cm_patterns (&patterns, n_extra_patterns); + + printf ("Found %d pattern%s\n", n, n != 1 ? "s" : ""); + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return n; +} + + +int +snes_n (st_rominfo_t *rominfo, const char *name) +{ + char buf[SNES_NAME_LEN], dest_name[FILENAME_MAX]; + int size = ucon64.file_size - rominfo->buheader_len, header_start, + name_len = (bs_dump || st_dump) ? 16 : SNES_NAME_LEN; + + memset (buf, ' ', name_len); + strncpy (buf, name, strlen (name) > (unsigned int) name_len ? + (unsigned int) name_len : strlen (name)); + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + + if (rominfo->interleaved) + header_start = SNES_HEADER_START + (snes_hirom ? 0 : size / 2); // (Ext.) HiROM : LoROM + else if (st_dump) // ignore interleaved ST dumps + header_start = 8 * MBIT; + else + header_start = rominfo->header_start; + ucon64_fwrite (buf, header_start + rominfo->buheader_len + 16, name_len, dest_name, "r+b"); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +int +snes_chk (st_rominfo_t *rominfo) +{ + char buf[4], dest_name[FILENAME_MAX]; + int size = ucon64.file_size - rominfo->buheader_len, header_start; + + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + + /* + The internal checksum bytes have been included in the checksum + calculation, but they will be changed after this function returns. We + account for that. Otherwise we could have to run uCON64 on the ROM twice. + */ + rominfo->current_internal_crc += (-snes_header.inverse_checksum_low - + snes_header.inverse_checksum_high - + snes_header.checksum_low - + snes_header.checksum_high) + + 2 * 0xff; // + 2 * 0; + // change inverse checksum + buf[0] = 0xffff - rominfo->current_internal_crc; // low byte + buf[1] = (0xffff - rominfo->current_internal_crc) >> 8; // high byte + // change checksum + buf[2] = rominfo->current_internal_crc; // low byte + buf[3] = rominfo->current_internal_crc >> 8; // high byte + if (rominfo->interleaved) + header_start = SNES_HEADER_START + (snes_hirom ? 0 : size / 2); // (Ext.) HiROM : LoROM + else + header_start = rominfo->header_start; + ucon64_fwrite (buf, header_start + rominfo->buheader_len + 44, 4, dest_name, "r+b"); + + dumper (stdout, buf, 4, header_start + rominfo->buheader_len + 44, DUMPER_HEX); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +static int +snes_testinterleaved (unsigned char *rom_buffer, int size, int banktype_score) +/* + The only way to determine whether a HiROM dump is interleaved or not seems to + be to check the value of the map type byte. Valid HiROM values (hexadecimal): + 21, 31, 35, 3a + Valid LoROM values: + 20, 23, 30, 32, 44 [, 41, 53] + 41 is the hexadecimal value of 'A' (WWF Super Wrestlemania (E)). 53 is the + hexadecimal value value of 'S' (Contra III - The Alien Wars (U)). + So, if a ROM dump seems LoROM, but the map type byte is that of a HiROM dump + we assume it is interleaved. Interleaved LoROM dumps are not produced by any + copier, but by incorrect ROM tools... +*/ +{ + int interleaved = 0, check_map_type = 1; + unsigned int crc; + + if (size < 64 * 1024) // snes_deinterleave() reads blocks of 32 kB + return 0; // file cannot be interleaved + + crc = crc32 (0, rom_buffer, 512); + /* + Special case hell + + 0x4a70ad38: Double Dragon, Return of (J), Super Double Dragon (E/U) {[!], [a1]} + 0x0b34ddad: Kakinoki Shogi (J) + 0x348b5357: King of Rally, The (J) + 0xc39b8d3a: Pro Kishi Simulation Kishi no Hanamichi (J) + 0xbd7bc39f: Shin Syogi Club (J) + 0x9b4638d0: Street Fighter Alpha 2 (E/U) {[b1]}, Street Fighter Zero 2 (J) + Only really necessary for (U). The other versions can be detected because + one of the two internal headers has checksum bytes ff ff 00 00. + 0x0085b742: Super Bowling (U) + 0x30cbf83c: Super Bowling (J) + These games have two headers. + + BUG ALERT: We don't check for 0xbd7bc39f. The first 512 bytes of what + uCON64 detects as the interleaved dump of Shin Syogi Club (J) are identical + to the first 512 bytes of what we detect as the uninterleaved dump of + Kakinoki Shogi (J). We prefer uninterleaved dumps. Besides, concluding a + dump is interleaved if the first 512 bytes have CRC32 0xbd7bc39f would mess + up the detection of some BS dumps. See below. + + 0x7039388a: Ys 3 - Wanderers from Ys (J) + This game has 31 internal headers... + + 0xd7470b37/0x9f1d6284: Dai Kaiju Monogatari 2 (J) (GD3/UFO) + 0xa2c5fd29/0xfe536fc9: Tales of Phantasia (J) (GD3/UFO) + These are Extended HiROM games. By "coincidence" ToP can be detected in + another way, but DKM2 (40 Mbit) can't. The CRC32's are checked for below. + + 0xdbc88ebf: BS Satella2 1 (J) + This game has a LoROM map type byte while it is a HiROM game. + + 0x29226b62: BS Busters - Digital Magazine 5-24-98 (J), + BS Do-Re-Mi No.2 5-10 (J), + BS Do-Re-Mi No.2 5-25 (J), + BS Furoito No Chousenjou {2, 3, 4, 5, 6} (J), + BS Nintendo HP 5-17 (J), + BS Nintendo HP 5-31 (J) + 0xbd7bc39f: BS Goods Press 6 Gatsu Gou (J), + BS NP Magazine 107 (J), + BS Tora no Maki 5-17 (J), + BS Tora no Maki 5-31 (J) + 0x4ef3d27b: BS Lord Monarke (J) + These games are *not* special cases. uCON64 detects them correctly, but the + tool that was used to create GoodSNES - 0.999.5 for RC 2.5.dat, does not. + This has been verified on a real SNES for the games with CRC 0x29226b62 and + 0x4ef3d27b. The games with CRC 0xbd7bc39f don't seem to run on a copier. + + 0xc3194ad7: Yu Yu No Quiz De Go! Go! (J) + 0x89d09a77: Infernal's Evil Demo! (PD) + 0xd3095af3: Legend - SNDS Info, Incredible Hulk Walkthru (PD) + 0x9b161d4d: Pop 'N Twinbee Sample (J) + 0x6910700a: Rock Fall (PD) + 0x447df9d5: SM Choukyousi Hitomi (PD) + 0x02f401df: SM Choukyousi Hitomi Vol 2 (PD) + 0xf423997a: World of Manga 2 (PD) + These games/dumps have a HiROM map type byte while they are LoROM. + + 0x0f802e41: Mortal Kombat 3 Final (Anthrox Beta Hack) + 0xbd8f1b20: Rise of the Robots (Beta) + 0x05926d17: Shaq Fu (E)/(J)(NG-Dump Known) + 0x3e2e5619: Super Adventure Island II (Beta) + 0x023e1298: Super Air Driver (E) [b] + These are also not special cases (not: HiROM map type byte + LoROM game). + GoodSNES - 0.999.5 for RC 2.5.dat simply contains errors. + + 0x2a4c6a9b: Super Noah's Ark 3D (U) + 0xfa83b519: Mortal Kombat (Beta) + 0xf3aa1eca: Power Piggs of the Dark Age (Pre-Release) {[h1]} + 0x65485afb: Super Aleste (J) {[t1]} <= header == trainer + 0xaad23842/0x5ee74558: Super Wild Card DX DOS ROM V1.122/interleaved + 0x422c95c4: Time Slip (Beta) + 0x7a44bd18: Total Football (E)(NG-Dump Known) + 0xf0bf8d7c/0x92180571: Utyu no Kishi Tekkaman Blade (Beta) {[h1]}/interleaved + 0x8e1933d0: Wesley Orangee Hotel (PD) + 0xe2b95725/0x9ca5ed58: Zool (Sample Cart)/interleaved + These games/dumps have garbage in their header. + */ + if (crc == 0xc3194ad7 +#ifdef DETECT_NOTGOOD_DUMPS + || + crc == 0x89d09a77 || crc == 0xd3095af3 || crc == 0x9b161d4d || + crc == 0x6910700a || crc == 0x447df9d5 || crc == 0x02f401df || + crc == 0xf423997a || crc == 0xfa83b519 || crc == 0xf3aa1eca || + crc == 0xaad23842 || crc == 0x422c95c4 || crc == 0x7a44bd18 || + crc == 0xf0bf8d7c || crc == 0x8e1933d0 || crc == 0xe2b95725 +#endif + ) + check_map_type = 0; // not interleaved + else if (crc == 0x4a70ad38 || crc == 0x0b34ddad || crc == 0x348b5357 || + crc == 0xc39b8d3a || crc == 0x9b4638d0 || crc == 0x0085b742 || + crc == 0x30cbf83c || crc == 0x7039388a || crc == 0xdbc88ebf || + crc == 0x2a4c6a9b +#ifdef DETECT_NOTGOOD_DUMPS + || + crc == 0x65485afb || crc == 0x5ee74558 || crc == 0x92180571 || + crc == 0x9ca5ed58 +#endif + ) + { + interleaved = 1; + snes_hirom = 0; + snes_hirom_ok = 1; + check_map_type = 0; // interleaved + } + // WARNING: st_dump won't be set if it's an interleaved dump + else if (st_dump) + check_map_type = 0; + else + { +#ifdef DETECT_SMC_COM_FUCKED_UP_LOROM + if (size > SNES_HEADER_START + SNES_HIROM + 0x4d) + if (check_banktype (rom_buffer, size / 2) > banktype_score) + { + interleaved = 1; + snes_hirom = 0; + snes_hirom_ok = 1; // keep snes_deinterleave() + check_map_type = 0; // from changing snes_hirom + } +#endif +#ifdef DETECT_INSNEST_FUCKED_UP_LOROM + /* + "the most advanced and researched Super Nintendo ROM utility available" + What a joke. They don't support their own "format"... + For some games we never reach this code, because the previous code + detects them (incorrectly). I (dbjh) don't think there are games in + this format available on the internet, so I won't add special-case code + (like CRC32 checks) to fix that -- it's a bug in inSNESt. Examples are: + Lufia II - Rise of the Sinistrals (H) + Super Mario All-Stars & World (E) [!] + */ + if (!interleaved && size == 24 * MBIT) + if (check_banktype (rom_buffer, 16 * MBIT) > banktype_score) + { + interleaved = 1; + snes_hirom = 0; + snes_hirom_ok = 2; // fix for snes_deinterleave() + check_map_type = 0; + } +#endif + } + if (check_map_type && !snes_hirom) + { + // first check if it's an interleaved Extended HiROM dump + if (ucon64.file_size >= (int) (SNES_HEADER_START + SNES_EROM + SNES_HEADER_LEN)) + { + // don't set snes_header_base to SNES_EROM for too small files (split files) + if (crc == 0xd7470b37 || crc == 0xa2c5fd29) // GD3 + snes_header_base = SNES_EROM; + else if (crc == 0x9f1d6284 || crc == 0xfe536fc9) // UFO + { + snes_header_base = SNES_EROM; + interleaved = 1; + } + } + if (snes_header.map_type == 0x21 || snes_header.map_type == 0x31 || + snes_header.map_type == 0x35 || snes_header.map_type == 0x3a || + snes_header.bs_map_type == 0x21 || snes_header.bs_map_type == 0x31) + interleaved = 1; + } + + return interleaved; +} + + +int +snes_deinterleave (st_rominfo_t *rominfo, unsigned char **rom_buffer, int rom_size) +{ + unsigned char blocks[256], *rom_buffer2; + int nblocks, i, j, org_hirom; + + org_hirom = snes_hirom; + nblocks = rom_size >> 16; // # 32 kB blocks / 2 + if (nblocks * 2 > 256) + return -1; // file > 8 MB + + if (rominfo->interleaved == 2) // SFX(2) games (Doom, Yoshi's Island) + { + for (i = 0; i < nblocks * 2; i++) + { + blocks[i] = (i & ~0x1e) | ((i & 2) << 2) | ((i & 4) << 2) | + ((i & 8) >> 2) | ((i & 16) >> 2); + if (blocks[i] * 0x8000 + 0x8000 > rom_size) + { + printf ("WARNING: This ROM cannot be handled as if it is in interleaved format 2\n"); + rominfo->interleaved = 0; + return -1; + } + } + } + else // rominfo->interleaved == 1 + { + int blocksset = 0; + + if (!snes_hirom_ok) + { + snes_hirom = SNES_HIROM; + snes_hirom_ok = 1; + } + + if (type == GD3) + { + // deinterleaving schemes specific for the Game Doctor + if ((snes_hirom || snes_hirom_ok == 2) && rom_size == 24 * MBIT) + { + for (i = 0; i < nblocks; i++) + { + blocks[i * 2] = i + ((i < (16 * MBIT >> 16) ? 16 : 4) * MBIT >> 15); + blocks[i * 2 + 1] = i; + } + blocksset = 1; + } + else if (snes_header_base == SNES_EROM) + { + int size2 = rom_size - 32 * MBIT; // size of second ROM + j = 32 * MBIT >> 16; + for (i = 0; i < j; i++) + { + blocks[i * 2] = i + j + (size2 >> 15); + blocks[i * 2 + 1] = i + (size2 >> 15); + } + j = size2 >> 16; + for (; i < j + (32 * MBIT >> 16); i++) + { + blocks[i * 2] = (unsigned char) (i + j - (32 * MBIT >> 16)); + blocks[i * 2 + 1] = (unsigned char) (i - (32 * MBIT >> 16)); + } + blocksset = 1; + } + } + if (!blocksset) + for (i = 0; i < nblocks; i++) + { + blocks[i * 2] = i + nblocks; + blocks[i * 2 + 1] = i; + } + } + + if (!(rom_buffer2 = (unsigned char *) malloc (rom_size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], rom_size); + exit (1); + } + for (i = 0; i < nblocks * 2; i++) + memcpy (rom_buffer2 + i * 0x8000, (*rom_buffer) + blocks[i] * 0x8000, 0x8000); + + free (*rom_buffer); + *rom_buffer = rom_buffer2; + return 0; +} + + +static const char * +matches_deviates (int equal) +{ + return +#ifdef USE_ANSI_COLOR + ucon64.ansi_color ? + (equal ? "\x1b[01;32mMatches\x1b[0m" : "\x1b[01;33mDeviates\x1b[0m") : + (equal ? "Matches" : "Deviates"); +#else + (equal ? "Matches" : "Deviates"); +#endif +} + + +int +snes_buheader_info (st_rominfo_t *rominfo) +// -dbuh +{ + unsigned char header[512]; + int x, y; + snes_file_t org_type = type; + + if (rominfo->buheader_len == 0) // type == MGD_SNES + { + printf ("This ROM has no backup unit header\n"); + return -1; + } + else + { + printf ("Backup unit header info (%s)\n\n", + type == SWC ? "SWC" : + type == FIG ? "FIG" : + type == GD3 ? "GD3" : + type == UFO ? "UFO" : + "unknown header type, but interpreted as SWC"); + if (type == SMC) + type = SWC; + } + + ucon64_fread (&header, 0, 512, ucon64.rom); + dumper (stdout, header, 48, 0, DUMPER_HEX); // show only the part that is + fputc ('\n', stdout); // interpreted by copier + + if (type == SWC || type == FIG || type == SMC || type == UFO) + { + x = ucon64.file_size - rominfo->buheader_len; + y = (header[0] + (header[1] << 8)) * 8 * 1024; + printf ("[0-1] File size: %d Bytes (%.4f Mb) => %s\n", + y, TOMBIT_F (y), matches_deviates (x == y)); + } + + if (type == SWC || type == SMC) + { + int z; + unsigned char sram_sizes[] = {0, 2, 8, 32}; + + if (header[2] & 0x80) // bit 7 has higher precedence + z = 0; // than bit 1 + else if (header[2] & 0x02) + z = 2; + else + z = 3; + + printf ("[2:7] Run program in mode: %d", z); + if (z == 2) + printf (" (bit 1=1)\n"); + else + fputc ('\n', stdout); + + y = header[2] & 0x40 ? 1 : 0; + printf ("[2:6] Split: %s => %s\n", + y ? "Yes" : "No", matches_deviates ((snes_split ? 1 : 0) == y)); + + x = snes_hirom ? 1 : 0; + y = header[2] & 0x20 ? 1 : 0; + printf ("[2:5] SRAM mapping mode: %s => %s\n", + y ? "HiROM" : "LoROM", matches_deviates (x == y)); + + y = header[2] & 0x10 ? 1 : 0; + printf ("[2:4] DRAM mapping mode: %s => %s\n", + y ? "HiROM" : "LoROM", matches_deviates (x == y)); + + y = sram_sizes[(~header[2] & 0x0c) >> 2]; // 32 => 12, 8 => 8, 2 => 4, 0 => 0 + printf ("[2:3-2] SRAM size: %d kB => %s\n", + y, matches_deviates (snes_sramsize == y * 1024)); + + printf ("[2:1] Run program in mode: %d", z); + if (z == 0) + printf (" (bit 7=1)\n"); + else + fputc ('\n', stdout); + + printf ("[2:0] External cartridge memory: %s\n", + header[2] & 0x01 ? "Enabled" : "Disabled"); + } + else if (type == FIG) + { + y = header[2] & 0x40 ? 1 : 0; + printf ("[2] Split: %s => %s\n", + y ? "Yes" : "No", matches_deviates ((snes_split ? 1 : 0) == y)); + + y = header[3] & 0x80 ? 1 : 0; + printf ("[3] Memory mapping mode: %s => %s\n", + y ? "HiROM" : "LoROM", matches_deviates ((snes_hirom ? 1 : 0) == y)); + + y = -1; + if (snes_hirom) + { + if ((header[4] == 0x77 && header[5] == 0x83) || + (header[4] == 0xf7 && header[5] == 0x83)) + y = 0; + else if ((header[4] == 0xfd && header[5] == 0x82) || + (header[4] == 0xdd && header[5] == 0x82)) + y = 8 * 1024; // or 2 * 1024 + else if (header[4] == 0xdd && header[5] == 0x02) + y = 32 * 1024; + } + else + { + if ((header[4] == 0x77 && header[5] == 0x83) || + (header[4] == 0x47 && header[5] == 0x83)) + y = 0; + else if (header[4] == 0x00 && header[5] == 0x80) + y = 8 * 1024; // or 2 * 1024 + else if ((header[4] == 0x00 && header[5] == 0x00) || + (header[4] == 0x11 && header[5] == 0x02)) + y = 32 * 1024; + } + + if (y == 8 * 1024) + printf ("[4-5] SRAM size: 2 kB / 8 kB => %s\n", + matches_deviates (snes_sramsize == 2 * 1024 || + snes_sramsize == 8 * 1024)); + else + printf ("[4-5] SRAM size: %d kB => %s\n", + y / 1024, matches_deviates (snes_sramsize == y)); + } + else if (type == UFO) + { + unsigned char sram_sizes[] = {0, 2, 8, 32}; + int z; + + y = header[2] ? 1 : 0; + printf ("[2] Split: %s => %s\n", + y ? "Yes" : "No", matches_deviates ((snes_split ? 1 : 0) == y)); + + x = ucon64.file_size - rominfo->buheader_len; + y = header[0x11] * MBIT; + printf ("[11] ROM size: %d Bytes (%d.0000 Mb) => %s\n", + y, y / MBIT, matches_deviates (x == y)); + + y = header[0x12] & 1; + printf ("[12] DRAM mapping mode: %s => %s\n", + y ? "LoROM" : "HiROM", matches_deviates ((snes_hirom ? 0 : 1) == y)); + + y = (header[0x13] <= 3 ? sram_sizes[header[0x13]] : 128) * 1024; + printf ("[13] SRAM size: %d kB => %s\n", + y / 1024, matches_deviates (snes_sramsize == y)); + + y = header[0x14]; + if (y) + printf ("[14] A15=%s selects SRAM\n", y == 1 ? "x" : y == 2 ? "0" : "1"); + else + printf ("[14] A15 not used for SRAM control\n"); + for (x = 0; x < 4; x++) + { + if (x & 1) + { + y = header[0x15 + x / 2] >> 2; + z = 2; + } + else + { + y = header[0x15 + x / 2] & 0x3; + z = 0; + } + if (y != 1) + printf ("[%x:%d-%d] A%d=%s selects SRAM\n", + 0x15 + x / 2, z + 1, z, 20 + x, y == 0 ? "x" : y == 2 ? "0" : "1"); + } + + y = header[0x17]; + printf ("[17] SRAM mapping mode: %s => %s\n", + y == 3 ? "LoROM" : y == 0 ? "HiROM" : "unknown", + matches_deviates ((snes_hirom ? 0 : 3) == y)); + } + else if (type == GD3) + { + y = -1; + if (header[0x10] == 0x81) + y = 8 * 1024; + else if (header[0x10] == 0x82) + y = 2 * 1024; + else if (header[0x10] == 0x80) + y = 32 * 1024; // or 0 + if (y == 32 * 1024) + printf ("[10] SRAM size: 0 kB / 32 kB => %s\n", + matches_deviates (snes_sramsize == 0 || snes_sramsize == 32 * 1024)); + else + printf ("[10] SRAM size: %d kB => %s\n", + y / 1024, matches_deviates (snes_sramsize == y)); + } + + type = org_type; + + return 0; +} + + +unsigned short int +get_internal_sums (st_rominfo_t *rominfo) +/* + Returns the sum of the internal checksum and the internal inverse checksum + if the values for snes_hirom and rominfo->buheader_len are correct. If the + values are correct the sum will be 0xffff. Note that the sum for bad ROM + dumps can also be 0xffff, because this function adds the internal checksum + bytes and doesn't do anything with the real, i.e. calculated, checksum. +*/ +{ + int image = SNES_HEADER_START + snes_header_base + snes_hirom + + rominfo->buheader_len; + // don't use rominfo->header_start here! + unsigned char buf[4]; + + ucon64_fread (buf, image + 44, 4, ucon64.rom); + return buf[0] + (buf[1] << 8) + buf[2] + (buf[3] << 8); +} + + +static void +snes_handle_buheader (st_rominfo_t *rominfo, st_unknown_header_t *header) +/* + Determine the size of a possible backup unit header. This function also tries + to determine the bank type in the process. However, snes_set_hirom() has the + final word about that. +*/ +{ + int x = 0, y; + /* + Check for "Extended" ROM dumps first, because at least one of them + (Tales of Phantasia (J)) has two headers; an incorrect one at the normal + location and a correct one at the Extended HiROM location. + */ + if (ucon64.file_size >= (int) (SNES_HEADER_START + SNES_EROM + SNES_HEADER_LEN)) + { + snes_header_base = SNES_EROM; + snes_hirom = SNES_HIROM; + rominfo->buheader_len = 0; + if ((x = get_internal_sums (rominfo)) != 0xffff) + { + rominfo->buheader_len = SWC_HEADER_LEN; + if ((x = get_internal_sums (rominfo)) != 0xffff) + { + snes_hirom = 0; + if ((x = get_internal_sums (rominfo)) != 0xffff) + { + rominfo->buheader_len = 0; + x = get_internal_sums (rominfo); + } + } + } + } + if (x != 0xffff) + { + snes_header_base = 0; + snes_hirom = 0; + rominfo->buheader_len = 0; + if ((x = get_internal_sums (rominfo)) != 0xffff) + { + rominfo->buheader_len = SWC_HEADER_LEN; + if ((x = get_internal_sums (rominfo)) != 0xffff) + { + snes_hirom = SNES_HIROM; + if ((x = get_internal_sums (rominfo)) != 0xffff) + { + rominfo->buheader_len = 0; + x = get_internal_sums (rominfo); + } + } + } + } + + if (header->id1 == 0xaa && header->id2 == 0xbb && header->type == 4) + type = SWC; + else if (!strncmp ((char *) header, "GAME DOCTOR SF 3", 16)) + type = GD3; + else if (!strncmp ((char *) header + 8, "SUPERUFO", 8)) + type = UFO; + else if ((header->hirom == 0x80 && // HiROM + ((header->emulation1 == 0x77 && header->emulation2 == 0x83) || + (header->emulation1 == 0xdd && header->emulation2 == 0x82) || + (header->emulation1 == 0xdd && header->emulation2 == 0x02) || + (header->emulation1 == 0xf7 && header->emulation2 == 0x83) || + (header->emulation1 == 0xfd && header->emulation2 == 0x82))) + || + (header->hirom == 0x00 && // LoROM + ((header->emulation1 == 0x77 && header->emulation2 == 0x83) || + (header->emulation1 == 0x00 && header->emulation2 == 0x80) || +#if 1 + // This makes NES FFE ROMs & Game Boy ROMs be detected as SNES + // ROMs, see src/console/nes.c & src/console/gb.c + (header->emulation1 == 0x00 && header->emulation2 == 0x00) || +#endif + (header->emulation1 == 0x47 && header->emulation2 == 0x83) || + (header->emulation1 == 0x11 && header->emulation2 == 0x02))) + ) + type = FIG; + else if (rominfo->buheader_len == 0 && x == 0xffff) + type = MGD_SNES; + + /* + x can be better trusted than type == FIG, but x being 0xffff is definitely + not a guarantee that rominfo->buheader_len already has the right value + (e.g. Earthworm Jim (U), Alfred Chicken (U|E), Soldiers of Fortune (U)). + */ +#if 0 + if (type != MGD_SNES) // don't do "&& type != SMC" or we'll miss a lot of PD ROMs +#endif + { + y = ((header->size_high << 8) + header->size_low) * 8 * 1024; + y += SWC_HEADER_LEN; // if SWC-like header -> hdr[1] high byte, + if (y == ucon64.file_size) // hdr[0] low byte of # 8 kB blocks in ROM + rominfo->buheader_len = SWC_HEADER_LEN; + else + { + int surplus = ucon64.file_size % 32768; + if (surplus == 0) + // most likely we guessed the copier type wrong + { + rominfo->buheader_len = 0; + type = MGD_SNES; + } + /* + Check for surplus being smaller than 31232 instead of MAXBUFSIZE + (32768) to detect "Joystick Sampler with Still Picture (PD)" (64000 + bytes, including SWC header). + "Super Wild Card V2.255 DOS ROM (BIOS)" is 16384 bytes (without + header), so check for surplus being smaller than 16384. + Shadow, The (Beta) [b3] has a surplus of 7680 bytes (15 * 512). So, + accept a surplus of up to 7680 bytes as a header... + */ + else if (surplus % SWC_HEADER_LEN == 0 && + surplus < (int) (15 * SWC_HEADER_LEN) && + ucon64.file_size > surplus) + rominfo->buheader_len = surplus; + // special case for Infinity Demo (PD)... (has odd size, but SWC + // header). Don't add "|| type == FIG" as it is too unreliable + else if (type == SWC || type == GD3 || type == UFO) + rominfo->buheader_len = SWC_HEADER_LEN; + } + } + if (UCON64_ISSET (ucon64.buheader_len)) // -hd, -nhd or -hdn switch was specified + { + rominfo->buheader_len = ucon64.buheader_len; + if (type == MGD_SNES && rominfo->buheader_len) + type = SMC; + } + + if (rominfo->buheader_len && !memcmp ((unsigned char *) header + 0x1e8, "NSRT", 4)) + nsrt_header = 1; + else + nsrt_header = 0; +} + + +static int +snes_set_hirom (unsigned char *rom_buffer, int size) +/* + This function tries to determine if the ROM dump is LoROM or HiROM. It returns + the highest value that check_banktype() returns. A higher value means a higher + chance the bank type is correct. +*/ +{ + int x, score_hi = 0, score_lo = 0; + + if (size >= (int) (8 * MBIT + SNES_HEADER_START + SNES_HIROM + SNES_HEADER_LEN) && + !strncmp ((char *) rom_buffer + SNES_HEADER_START + 16, "ADD-ON BASE CASSETE", 19)) + { // A Sufami Turbo dump contains 4 copies of the ST BIOS, which is 2 Mbit. + // After the BIOS comes the game data. + st_dump = 1; + snes_header_base = 8 * MBIT; + x = 8 * MBIT + SNES_HIROM; + } + else if (snes_header_base == SNES_EROM) + x = SNES_EROM + SNES_HIROM; + else + { + snes_header_base = 0; + x = SNES_HIROM; + } + + if (size > SNES_HEADER_START + SNES_HIROM + 0x4d) + { + score_hi = check_banktype (rom_buffer, x); + score_lo = check_banktype (rom_buffer, snes_header_base); + } + if (score_hi > score_lo) // yes, a preference for LoROM + { // (">" vs. ">=") + snes_hirom = SNES_HIROM; + x = score_hi; + } + else + { + snes_hirom = 0; + x = score_lo; + } + /* + It would be nice if snes_header.map_type & 1 could be used to verify that + snes_hirom has the correct value, but it doesn't help much. For games like + Batman Revenge of the Joker (U) it matches what check_banktype() finds. + snes_hirom must be 0x8000 for that game in order to display correct + information. However it should be 0 when writing a copier header. + So, snes_header.map_type can't be used to recognize such cases. + */ + + // step 3. + if (UCON64_ISSET (ucon64.snes_hirom)) // -hi or -nhi switch was specified + { + snes_hirom = ucon64.snes_hirom; + // keep snes_deinterleave() from changing snes_hirom + snes_hirom_ok = 1; + if (size < (int) (SNES_HEADER_START + SNES_HIROM + SNES_HEADER_LEN)) + snes_hirom = 0; + } + + if (UCON64_ISSET (ucon64.snes_header_base)) // -erom switch was specified + { + snes_header_base = ucon64.snes_header_base; + if (snes_header_base && + size < (int) (snes_header_base + SNES_HEADER_START + snes_hirom + SNES_HEADER_LEN)) + snes_header_base = 0; // Don't let -erom crash on a too small ROM + } + + return x; +} + + +static void +snes_set_bs_dump (st_rominfo_t *rominfo, unsigned char *rom_buffer, int size) +{ + bs_dump = snes_check_bs (); + /* + Do the following check before checking for ucon64.bs_dump. Then it's + possible to specify both -erom and -bs with effect, for what it's worth ;-) + The main reason to test this case is to display correct info for "SD Gundam + G-NEXT + Rom Pack Collection (J) [!]". Note that testing for SNES_EROM + causes the code to be skipped for Sufami Turbo dumps. + */ + if (bs_dump && + snes_header_base == SNES_EROM && !UCON64_ISSET (ucon64.snes_header_base)) + { + bs_dump = 0; + snes_header_base = 0; + snes_set_hirom (rom_buffer, size); + rominfo->header_start = snes_header_base + SNES_HEADER_START + snes_hirom; + memcpy (&snes_header, rom_buffer + rominfo->header_start, rominfo->header_len); + } + if (UCON64_ISSET (ucon64.bs_dump)) // -bs or -nbs switch was specified + { + bs_dump = ucon64.bs_dump; + if (bs_dump && snes_header_base == SNES_EROM) + bs_dump = 2; // Extended ROM => must be add-on cart + } +} + + +int +snes_init (st_rominfo_t *rominfo) +{ + int x, y, size, calc_checksums, result = -1; // it's no SNES ROM dump until detected otherwise + unsigned char *rom_buffer; + st_unknown_header_t header = { 0, 0, 0, 0, 0, 0, { 0 }, 0, 0, 0, { 0 } }; + char buf[MAXBUFSIZE], *str; +#define SNES_COUNTRY_MAX 0xe + static const char *snes_country[SNES_COUNTRY_MAX] = + { + "Japan", + "U.S.A.", + "Europe, Oceania and Asia", // Australia is part of Oceania + "Sweden", + "Finland", + "Denmark", + "France", + "The Netherlands", // Holland is an incorrect name for The Netherlands + "Spain", + "Germany, Austria and Switzerland", + "Italy", + "Hong Kong and China", + "Indonesia", + "South Korea" + }, + *snes_rom_type[3] = + { + "ROM", // NOT ROM only, ROM + other chip is possible + "ROM + SRAM", + "ROM + SRAM + Battery" + }, + *snes_bs_type[4] = + { + "Full size + Sound link", + "Full size", + "Part size + Sound link", + "Part size" + }; + + snes_hirom_ok = 0; // init these vars here, for -lsv + snes_sramsize = 0; // idem + type = SMC; // idem, SMC indicates unknown copier type + bs_dump = 0; // for -lsv, but also just to init it + st_dump = 0; // idem + + x = 0; + ucon64_fread (&header, UNKNOWN_HEADER_START, UNKNOWN_HEADER_LEN, ucon64.rom); + if (header.id1 == 0xaa && header.id2 == 0xbb) + x = SWC; + else if (!strncmp ((char *) &header + 8, "SUPERUFO", 8)) + x = UFO; + if ((x == SWC && (header.type == 5 || header.type == 8)) || + (x == UFO && (OFFSET (header, 0x10) == 0))) + { + rominfo->buheader_len = SWC_HEADER_LEN; + strcpy (rominfo->name, "Name: N/A"); + rominfo->console_usage = NULL; + rominfo->maker = "Publisher: You?"; + rominfo->country = "Country: Your country?"; + rominfo->has_internal_crc = 0; + ucon64.split = 0; // SRAM & RTS files are never split + if (x == SWC) + { + rominfo->copier_usage = swc_usage[0].help; + type = SWC; + if (header.type == 5) + strcat (rominfo->misc, "Type: Super Wild Card SRAM file\n"); + else if (header.type == 8) + strcat (rominfo->misc, "Type: Super Wild Card RTS file\n"); + } + else if (x == UFO) + { + rominfo->copier_usage = ufo_usage[0].help; + type = UFO; + strcat (rominfo->misc, "Type: Super UFO SRAM file\n"); + } + return 0; // rest is nonsense for SRAM/RTS file + } + + /* + snes_testinterleaved() needs the correct value for snes_hirom and + rominfo->header_start. snes_hirom may be used only after the check for + -hi/-nhi has been done. However, rominfo->buheader_len must have the + correct value in order to determine the value for snes_hirom. This can only + be known after the backup unit header length detection (including the check + for -hd/-nhd/-hdn). So, the order must be + 1. - rominfo->buheader_len + 2. - snes_hirom + 3. - check for -hi/-nhi + 4. - snes_testinterleaved() + */ + + snes_handle_buheader (rominfo, &header); // step 1. & first part of step 2. + + if (UCON64_ISSET (ucon64.split)) + snes_split = ucon64.split; + else + { + if (type == SWC || type == FIG || type == UFO) + { + // TODO?: fix this code for last split file + snes_split = 0; + if (header.emulation & 0x40 || (type == UFO && header.emulation & 0x10)) + snes_split = ucon64_testsplit (ucon64.rom); + ucon64.split = snes_split; // force displayed info to be correct + } // if not split (see ucon64.c) + else + snes_split = ucon64_testsplit (ucon64.rom); + } + + size = ucon64.file_size - rominfo->buheader_len; + if (size < (int) (SNES_HEADER_START + SNES_HEADER_LEN)) + { + snes_hirom = 0; + if (UCON64_ISSET (ucon64.snes_hirom)) // see snes_set_hirom() + snes_hirom = ucon64.snes_hirom; + snes_hirom_ok = 1; + + rominfo->interleaved = 0; + if (UCON64_ISSET (ucon64.interleaved)) + rominfo->interleaved = ucon64.interleaved; + return -1; // don't continue (seg faults!) + } + if (ucon64.console == UCON64_SNES || (type != SMC && size <= 16 * 1024 * 1024)) + result = 0; // it seems to be a SNES ROM dump + + if (!(rom_buffer = (unsigned char *) malloc (size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size); + return -1; // don't exit(), we might've been + } // called with -lsv + ucon64_fread (rom_buffer, rominfo->buheader_len, size, ucon64.rom); + + x = snes_set_hirom (rom_buffer, size); // second part of step 2. & step 3. + + rominfo->header_start = snes_header_base + SNES_HEADER_START + snes_hirom; + rominfo->header_len = SNES_HEADER_LEN; + // set snes_header before calling snes_testinterleaved() + memcpy (&snes_header, rom_buffer + rominfo->header_start, rominfo->header_len); + rominfo->header = &snes_header; + + // step 4. + rominfo->interleaved = UCON64_ISSET (ucon64.interleaved) ? + ucon64.interleaved : snes_testinterleaved (rom_buffer, size, x); + + calc_checksums = !UCON64_ISSET (ucon64.do_not_calc_crc) && result == 0; + // we want the CRC32 of the "raw" data (too) + if (calc_checksums) + ucon64.fcrc32 = crc32 (0, rom_buffer, size); + + // bs_dump has to be set before calling snes_chksum(), but snes_check_bs() + // needs snes_header to be filled with the correct data + if (rominfo->interleaved) + { + snes_deinterleave (rominfo, &rom_buffer, size); + snes_set_hirom (rom_buffer, size); + rominfo->header_start = snes_header_base + SNES_HEADER_START + snes_hirom; + memcpy (&snes_header, rom_buffer + rominfo->header_start, rominfo->header_len); + } + + snes_set_bs_dump (rominfo, rom_buffer, size); + + // internal ROM name + if (!bs_dump && st_dump) + memcpy (rominfo->name, rom_buffer + 8 * MBIT + 16, SNES_NAME_LEN); + else + { + memcpy (rominfo->name, snes_header.name, SNES_NAME_LEN); + for (x = 0; x < SNES_NAME_LEN; x++) + if (!isprint ((int) rominfo->name[x])) // we can't use mkprint(), because it skips \n + rominfo->name[x] = '.'; + } + rominfo->name[(bs_dump || st_dump) ? 16 : SNES_NAME_LEN] = 0; + // terminate string (at 1st byte _after_ string) + + if (calc_checksums) + { + // internal ROM crc + rominfo->has_internal_crc = 1; + rominfo->internal_crc_len = 2; + rominfo->current_internal_crc = snes_chksum (rominfo, &rom_buffer, size); + rominfo->internal_crc = snes_header.checksum_low; + rominfo->internal_crc += snes_header.checksum_high << 8; + x = snes_header.inverse_checksum_low; + x += snes_header.inverse_checksum_high << 8; + y = ~rominfo->current_internal_crc & 0xffff; + sprintf (rominfo->internal_crc2, + "Inverse checksum: %s, 0x%04x (calculated) %c= 0x%04x (internal)", +#ifdef USE_ANSI_COLOR + ucon64.ansi_color ? + ((y == x) ? "\x1b[01;32mOk\x1b[0m" : "\x1b[01;31mBad\x1b[0m") + : + ((y == x) ? "Ok" : "Bad"), +#else + (y == x) ? "Ok" : "Bad", +#endif + y, (y == x) ? '=' : '!', x); + if (bs_dump == 1) // bs_dump == 2 for BS add-on dumps + { + unsigned short int *bs_date_ptr = (unsigned short int *) + (rom_buffer + snes_header_base + SNES_HEADER_START + snes_hirom + 38); + /* + We follow the "uCONSRT standard" for calculating the CRC32 of BS + dumps. At the time of this writing (20 June 2003) the uCONSRT + standard defines that the date of BS dumps has to be "skipped" + (overwritten with a constant number), because the date is variable. + When a BS dump is made the BSX fills in the date. Otherwise two + dumps of the same memory card would have a different CRC32. + For BS add-on cartridge dumps we don't do anything special as they + come from cartridges (with a constant date). + Why 42? It's the answer to life, the universe and everything :-) + */ + *bs_date_ptr = me2le_16 (0x0042); + get_nsrt_info (rom_buffer, rominfo->header_start, (unsigned char *) &header); + ucon64.crc32 = crc32 (0, rom_buffer, size); + } + else if (rominfo->interleaved || nsrt_header) + { + get_nsrt_info (rom_buffer, rominfo->header_start, (unsigned char *) &header); + ucon64.crc32 = crc32 (0, rom_buffer, size); + } + else + { + ucon64.crc32 = ucon64.fcrc32; + ucon64.fcrc32 = 0; + } + } + + rominfo->console_usage = snes_usage[0].help; + if (!rominfo->buheader_len) + rominfo->copier_usage = mgd_usage[0].help; + else + { + switch (type) + { + case GD3: + rominfo->copier_usage = gd_usage[0].help; + break; + case UFO: + rominfo->copier_usage = ufo_usage[0].help; + break; + case FIG: + rominfo->copier_usage = fig_usage[0].help; + break; + // just assume it's in SWC format... (there are _many_ ROMs on the + // internet with incorrect headers) + default: + rominfo->copier_usage = swc_usage[0].help; + } + } + + // ROM maker + if (snes_header.maker == 0x33 || bs_dump) + { + int ih = snes_header.maker_high <= '9' ? + snes_header.maker_high - '0' : snes_header.maker_high - 'A' + 10, + il = snes_header.maker_low <= '9' ? + snes_header.maker_low - '0' : snes_header.maker_low - 'A' + 10; + x = ih * 36 + il; + } + else if (snes_header.maker != 0) + x = (snes_header.maker >> 4) * 36 + (snes_header.maker & 0x0f); + else + x = 0; // warning remover + + if (x < 0 || x >= NINTENDO_MAKER_LEN) + x = 0; + rominfo->maker = snes_header.maker == 0 ? "Demo or Beta ROM?" : + NULL_TO_UNKNOWN_S (nintendo_maker[x]); + + if (!bs_dump) + { + // ROM country + rominfo->country = NULL_TO_UNKNOWN_S (snes_country[MIN (snes_header.country, SNES_COUNTRY_MAX - 1)]); + + // misc stuff + sprintf (buf, "HiROM: %s\n", snes_hirom ? "Yes" : "No"); + strcat (rominfo->misc, buf); + + sprintf (buf, "Internal size: %d Mb\n", 1 << (snes_header.rom_size - 7)); + strcat (rominfo->misc, buf); +/* + sprintf (buf, "Map type: %x\n", snes_header.map_type); + strcat (rominfo->misc, buf); +*/ + sprintf (buf, "ROM type: (%x) %s", snes_header.rom_type, + snes_rom_type[(snes_header.rom_type & 7) % 3]); + strcat (rominfo->misc, buf); + if ((snes_header.rom_type & 0xf) >= 3) + { + if (snes_header.rom_type == 3 || snes_header.rom_type == 5) + str = "DSP"; + else if (snes_header.rom_type == 0x13) + str = "SRAM + Super FX (Mario Chip 1)"; + else if (snes_header.rom_type == 0x1a) + str = "Super FX"; + else if (snes_header.rom_type == 0x14 || snes_header.rom_type == 0x15) + { + if (snes_header.rom_size > 10) + str = "Super FX 2"; // larger than 8 Mbit + else + str = "Super FX"; + } + else if (snes_header.rom_type == 0x25) + str = "OBC1"; + else if (snes_header.rom_type == 0x34 || snes_header.rom_type == 0x35) + str = "SA-1"; + else if (snes_header.rom_type == 0x43 || snes_header.rom_type == 0x45) + str = "S-DD1"; + else if (snes_header.rom_type == 0x55) + str = "S-RTC"; + else if (snes_header.rom_type == 0xe3) + str = "Game Boy data"; + else if (snes_header.rom_type == 0xf3) + str = "C4"; + else if (snes_header.rom_type == 0xf5) + { + if (snes_header.map_type == 0x30) + str = "Seta RISC"; + else + str = "SPC7110"; + } + else if (snes_header.rom_type == 0xf6) + str = "Seta DSP"; + else if (snes_header.rom_type == 0xf9) + str = "SPC7110 + RTC"; + else + str = "Unknown"; + + sprintf (buf, " + %s", str); + strcat (rominfo->misc, buf); + } + strcat (rominfo->misc, "\n"); + + sprintf (buf, "ROM speed: %s\n", + snes_header.map_type & 0x10 ? "120 ns (FastROM)" : "200 ns (SlowROM)"); + strcat (rominfo->misc, buf); + + if (snes_header.rom_type == 0x13 || snes_header.rom_type == 0x1a || + snes_header.rom_type == 0x14 || snes_header.rom_type == 0x15) + { + snes_sramsize = 32 * 1024; + if (snes_header.maker == 0x33) + snes_sfx_sramsize = snes_header.sfx_sram_size ? 1 << (snes_header.sfx_sram_size + 10) : 0; + else + snes_sfx_sramsize = 32 * 1024; + } + else + { + snes_sramsize = snes_header.sram_size ? 1 << (snes_header.sram_size + 10) : 0; + snes_sfx_sramsize = 0; + } + + if (!snes_sramsize && !snes_sfx_sramsize) + sprintf (buf, "SRAM: No\n"); + else + sprintf (buf, "SRAM: Yes, %d kBytes\n", (snes_sfx_sramsize ? snes_sfx_sramsize : snes_sramsize) / 1024); + strcat (rominfo->misc, buf); + } + else // BS info + { + // ROM country + rominfo->country = "Japan"; + // misc stuff + if (bs_dump == 2) + sprintf (buf, "\nBroadcast Satellaview add-on cartridge dump\n"); + else + sprintf (buf, "\nBroadcast Satellaview dump\n"); // new line is intentional + strcat (rominfo->misc, buf); + + x = snes_header.bs_day & 0x0f; + if (x <= 3) + y = (snes_header.bs_day >> 4) * 2; + else if (x >= 8 && x <= 0xb) + y = (snes_header.bs_day >> 4) * 2 + 1; + else // incorrect data + y = 0; + sprintf (buf, "Dumping date: %d/%d\n", y, snes_header.bs_month >> 4); + strcat (rominfo->misc, buf); + + sprintf (buf, "HiROM: %s\n", snes_hirom ? "Yes" : "No"); + strcat (rominfo->misc, buf); + + // misc stuff + sprintf (buf, "Internal size: %d Mb\n", 8 - (snes_header.bs_type >> (4 + 1)) * 4); + strcat (rominfo->misc, buf); +/* + sprintf (buf, "Map type: %x\n", snes_header.bs_map_type); + strcat (rominfo->misc, buf); +*/ + x = snes_header.bs_type >> 4; + sprintf (buf, "ROM type: (%x) %s\n", snes_header.bs_type, + x > 3 ? "Unknown" : snes_bs_type[x]); + strcat (rominfo->misc, buf); + + /* + It seems logical that the same condition as for regular cartridge dumps + tells whether it's a FastROM or a SlowROM. The original condition was + "(snes_header.bs_map_type >> 4) > 2". + */ + sprintf (buf, "ROM speed: %s\n", + snes_header.bs_map_type & 0x10 ? "120 ns (FastROM)" : "200 ns (SlowROM)"); + strcat (rominfo->misc, buf); + } + + sprintf (buf, "Version: 1.%d", snes_header.version); + strcat (rominfo->misc, buf); + + if (nsrt_header) + handle_nsrt_header (rominfo, (unsigned char *) &header, snes_country); + + free (rom_buffer); + return result; +} + + +#if 1 +int +snes_check_bs (void) +{ + if ((snes_header.maker == 0x33 || snes_header.maker == 0xff) && + (snes_header.map_type == 0 || (snes_header.map_type & 0x83) == 0x80)) + { + int date = (snes_header.bs_day << 8) | snes_header.bs_month; + if (date == 0) + return 2; // BS add-on cartridge dump + else if (date == 0xffff || + ((snes_header.bs_month & 0xf) == 0 && + ((unsigned int) ((snes_header.bs_month >> 4) - 1)) < 12)) + return 1; // BS dump (via BSX) + } + return 0; +} +#else +static int +check_char (unsigned char c) +{ + if ((c & 0x80) == 0) + return 0; + + if ((c - 0x20) & 0x40) + return 1; + else + return 0; +} + + +static int +snes_bs_name (void) +{ + unsigned int value; + int n, n_valid = 0; + + for (n = 0; n < 16; n++) + { + value = snes_header.name[n]; + if (check_char ((unsigned char) value) != 0) + { + value = snes_header.name[n + 1]; + if (value < 0x20) + if ((n_valid != 11) || (value != 0)) // Dr. Mario Hack + break; + + n_valid++; + n++; + } + else + { + if (value == 0) + { + if (n_valid == 0) + break; + continue; + } + + if (value < 0x20) + break; + + if (value >= 0x80) + if (value < 0xa0 || value >= 0xf0) + break; + n_valid++; + } + } + + return n == 16 && n_valid > 0 ? 1 : 0; +} + + +int +snes_check_bs (void) +{ + unsigned int value; + + if (snes_header.bs_type & 0x4f) + return 0; + + if (snes_header.maker != 0x33 && snes_header.maker != 0xff) + return 0; + + value = (snes_header.bs_day << 8) | snes_header.bs_month; + if (value != 0x0000 && value != 0xffff) + { + if ((value & 0x040f) != 0) + return 0; + if ((value & 0xff) > 0xc0) + return 0; + } + + if (snes_header.bs_map_type & 0xce || ((snes_header.bs_map_type & 0x30) == 0)) + return 0; + + if ((snes_header.map_type & 0x03) != 0) + return 0; + + value = ((unsigned char *) &snes_header)[35]; + if (value != 0x00 && value != 0xff) + return 0; + + if (((unsigned char *) &snes_header)[36] != 0x00) + return 0; + + return snes_bs_name (); +} +#endif + + +#if 1 +int +snes_chksum (st_rominfo_t *rominfo, unsigned char **rom_buffer, int rom_size) +/* + Calculate the checksum of a SNES ROM. This version of snes_chksum() has one + advantage over the one below in that it is a bit more sensitive to overdumps. +*/ +{ + int i, internal_rom_size, half_internal_rom_size, remainder; + unsigned short int sum1, sum2; + + if (!bs_dump && snes_header.rom_size <= 13) // largest known cart size is 64 Mbit + internal_rom_size = 1 << (snes_header.rom_size + 10); + else + internal_rom_size = st_dump ? rom_size - 8 * MBIT : rom_size; + + half_internal_rom_size = internal_rom_size >> 1; + + sum1 = 0; + if ((snes_header.rom_type == 0xf5 && snes_header.map_type != 0x30) + || snes_header.rom_type == 0xf9 || bs_dump) + { + for (i = 0; i < rom_size; i++) + sum1 += (*rom_buffer)[i]; // Far East of Eden Zero (J) + if (rom_size == 24 * MBIT) + sum1 *= 2; // Momotaro Dentetsu Happy (J) + + if (bs_dump) // Broadcast Satellaview "ROM" + for (i = rominfo->header_start; + i < (int) (rominfo->header_start + SNES_HEADER_LEN); i++) + sum1 -= (*rom_buffer)[i]; + } + else + { + // Handle split files. Don't make this dependent of ucon64.split as + // the last file doesn't get detected as being split. Besides, we don't + // want to crash on *any* input data. + int i_start = st_dump ? 8 * MBIT : 0, + i_end = i_start + + (half_internal_rom_size > rom_size ? rom_size : half_internal_rom_size); + + for (i = i_start; i < i_end; i++) // normal ROM + sum1 += (*rom_buffer)[i]; + + remainder = rom_size - i_start - half_internal_rom_size; + if (!remainder) // don't divide by zero below + remainder = half_internal_rom_size; + + sum2 = 0; + for (i = i_start + half_internal_rom_size; i < rom_size; i++) + sum2 += (*rom_buffer)[i]; + sum1 += sum2 * (half_internal_rom_size / remainder); +// printf ("DEBUG internal_rom_size: %d; half_internal_rom_size: %d; remainder: %d\n", +// internal_rom_size, half_internal_rom_size, remainder); + } + + return sum1; +} +#else +int +snes_chksum (st_rominfo_t *rominfo, unsigned char **rom_buffer, int rom_size) +// Calculate the checksum of a SNES ROM +{ + int i, internal_rom_size; + unsigned short int sum; + + if (!bs_dump) + { + internal_rom_size = 1 << (snes_header.rom_size + 10); + if (internal_rom_size < rom_size) + internal_rom_size = rom_size; + if (internal_rom_size > 16 * 1024 *1024) + internal_rom_size = 16 * 1024 *1024; + } + else + internal_rom_size = rom_size; + +// printf ("DEBUG internal_rom_size: %d; rom_size: %d\n", internal_rom_size, rom_size); + if (internal_rom_size > rom_size) + { + int blocksize; + unsigned char *ptr; + + if (!(*rom_buffer = (unsigned char *) realloc (*rom_buffer, internal_rom_size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], internal_rom_size); + return -1; // don't exit(), we might've been + } // called with -lsv + blocksize = internal_rom_size - rom_size; + ptr = *rom_buffer + rom_size; + if (blocksize % (3 * MBIT) == 0) // 6 (16 - 10), 12 (32 - 20), 24 (64 - 40) + { + blocksize /= 3; + for (i = 0; i < 3; i++) + memcpy (ptr + i * blocksize, ptr - blocksize, blocksize); + } + else + memcpy (ptr, ptr - blocksize, blocksize); + } + + sum = 0; + if ((snes_header.rom_type == 0xf5 && snes_header.map_type != 0x30) + || snes_header.rom_type == 0xf9 || bs_dump) + { + for (i = 0; i < rom_size; i++) + sum += (*rom_buffer)[i]; // Far East of Eden Zero (J) + if (rom_size == 24 * MBIT) + sum *= 2; // Momotaro Dentetsu Happy (J) + + if (bs_dump) + for (i = rominfo->header_start; + i < (int) (rominfo->header_start + SNES_HEADER_LEN); i++) + sum -= (*rom_buffer)[i]; + } + else + { + int i_start = st_dump ? 8 * MBIT : 0; + for (i = i_start; i < internal_rom_size; i++) + sum += (*rom_buffer)[i]; + } + + return sum; +} +#endif + + +int +snes_isprint (char *s, int len) +{ + unsigned char *p = (unsigned char *) s; + + for (; len >= 0; p++, len--) + // we don't use isprint(), because we don't want to get different results + // of check_banktype() for different locale settings + if (*p < 0x20 || *p > 0x7e) + return FALSE; + + return TRUE; +} + + +int +check_banktype (unsigned char *rom_buffer, int header_offset) +/* + This function is used to check if the value of header_offset is a good guess + for the location of the internal SNES header (and thus of the bank type + (LoROM, HiROM or Extended HiROM)). The higher the returned value, the higher + the chance the guess was correct. +*/ +{ + int score = 0, x, y; + +// dumper (stdout, (char *) rom_buffer + SNES_HEADER_START + header_offset, +// SNES_HEADER_LEN, SNES_HEADER_START + header_offset, DUMPER_HEX); + + // game ID info (many games don't have useful info here) + if (snes_isprint ((char *) rom_buffer + SNES_HEADER_START + header_offset + 2, 4)) + score += 1; + + if (!bs_dump) + { + if (snes_isprint ((char *) rom_buffer + SNES_HEADER_START + header_offset + 16, + SNES_NAME_LEN)) + score += 1; + + // map type + x = rom_buffer[SNES_HEADER_START + header_offset + 37]; + if ((x & 0xf) < 4) + score += 2; + y = rom_buffer[SNES_HEADER_START + header_offset + 38]; + if (snes_hirom_ok && !(y == 0x34 || y == 0x35)) // ROM type for SA-1 + // map type, HiROM flag (only if we're sure about value of snes_hirom) + if ((x & 1) == ((header_offset >= snes_header_base + SNES_HIROM) ? 1 : 0)) + score += 1; + + // ROM size + if (1 << (rom_buffer[SNES_HEADER_START + header_offset + 39] - 7) <= 64) + score += 1; + + // SRAM size + if (1 << rom_buffer[SNES_HEADER_START + header_offset + 40] <= 256) + score += 1; + + // country + if (rom_buffer[SNES_HEADER_START + header_offset + 41] <= 13) + score += 1; + } + else + { + if (snes_hirom_ok) + // map type, HiROM flag + if ((rom_buffer[SNES_HEADER_START + header_offset + 40] & 1) == + ((header_offset >= snes_header_base + SNES_HIROM) ? 1 : 0)) + score += 1; + } + + // publisher "escape code" + if (rom_buffer[SNES_HEADER_START + header_offset + 42] == 0x33) + score += 2; + else // publisher code + if (snes_isprint ((char *) rom_buffer + SNES_HEADER_START + header_offset, 2)) + score += 2; + + // version + if (rom_buffer[SNES_HEADER_START + header_offset + 43] <= 2) + score += 2; + + // checksum bytes + x = rom_buffer[SNES_HEADER_START + header_offset + 44] + + (rom_buffer[SNES_HEADER_START + header_offset + 45] << 8); + y = rom_buffer[SNES_HEADER_START + header_offset + 46] + + (rom_buffer[SNES_HEADER_START + header_offset + 47] << 8); + if (x + y == 0xffff) + { + if (x == 0xffff || y == 0xffff) + score += 3; + else + score += 4; + } + + // reset vector + if (rom_buffer[SNES_HEADER_START + header_offset + 0x4d] & 0x80) + score += 3; + + return score; +} + + +int +snes_demirror (st_rominfo_t *rominfo) // nice verb :-) +{ + int fixed = 0, size = ucon64.file_size - rominfo->buheader_len, mirror_size = 0; + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + unsigned char *buffer; + + if (!(buffer = (unsigned char *) malloc (size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size); + exit (1); + } + ucon64_fread (buffer, rominfo->buheader_len, size, ucon64.rom); + if (rominfo->interleaved) + { + printf ("NOTE: ROM is interleaved -- deinterleaving\n"); + snes_deinterleave (rominfo, &buffer, size); + } + + if (size % (12 * MBIT) == 0 && size != 36 * MBIT) // 12, 24 or 48 Mbit dumps can be mirrored + mirror_size = size / 12 * 2; + else if (size == 16 * MBIT) // ...and some C4 dumps too + mirror_size = 4 * MBIT; + else if (size == 32 * MBIT) // ...and some SA-1 dumps too + mirror_size = 8 * MBIT; + + if (mirror_size) + { + if (memcmp (buffer + size - mirror_size, buffer + size - 2 * mirror_size, + mirror_size) == 0) + { + if (ucon64.quiet == -1) + printf ("Mirrored: %d - %d == %d - %d\n", + (size - 2 * mirror_size) / MBIT, (size - mirror_size) / MBIT, + (size - mirror_size) / MBIT, size / MBIT); + size -= mirror_size; + fixed = 1; + } + } + + if (!fixed) + { + if (ucon64.quiet < 1) + printf ("NOTE: Did not detect a mirrored block -- no file has been written\n"); + free (buffer); + return 1; + } + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, src_name, 0); + fcopy (src_name, 0, rominfo->buheader_len, dest_name, "wb"); + ucon64_fwrite (buffer, rominfo->buheader_len, size, dest_name, "ab"); + printf (ucon64_msg[WROTE], dest_name); + + remove_temp_file (); + free (buffer); + return 0; +} + + +static void +write_game_table_entry (FILE *destfile, int file_no, st_rominfo_t *rominfo, int size) +{ + int n, uses_DSP; + unsigned char name[0x1c], flags1, flags2; + static int slot = 0; + + uses_DSP = snes_header.rom_type == 3 || snes_header.rom_type == 5 || + snes_header.rom_type == 0xf6; + + fseek (destfile, 0x4000 + (file_no - 1) * 0x20, SEEK_SET); + fputc (0xff, destfile); // 0x0 = 0xff + memcpy (name, rominfo->name, 0x1c); + for (n = 0; n < 0x1c; n++) + { + if (!isprint ((int) name[n])) + name[n] = '.'; + else + name[n] = toupper (name[n]); // The Super Flash loader (SFBOTX2.GS) + } // only supports upper case characters + fwrite (name, 1, 0x1c, destfile); // 0x1 - 0x1c = name + + if (snes_sramsize) + { + if (snes_sramsize == 2 * 1024) + flags2 = 0x00; + else if (snes_sramsize == 8 * 1024) + flags2 = 0x10; + else if (snes_sramsize == 32 * 1024) + flags2 = 0x20; + else // if (snes_sramsize == 128 * 1024) // Default to 1024 kbit SRAM + flags2 = 0x30; + } + else + flags2 = 0x40; + + if (snes_header_base == SNES_EROM) // Enable Extended Map for >32 Mbit ROMs + flags2 |= 0x80; + + flags1 = snes_hirom ? 0x10 : 0x00; + + if (!snes_hirom && uses_DSP) // Set LoROM DSP flag if necessary + flags1 |= 0x01; + + if (slot == 0) + flags1 |= 0x00; + else if (slot == 0x200000) + flags1 |= 0x40; + else if (slot == 0x400000) + flags1 |= 0x20; + else if (slot == 0x600000) + flags1 |= 0x60; + + slot += (size + 16 * MBIT - 1) & ~(16 * MBIT - 1); + + fputc (flags1, destfile); // 0x1d = mapping flags + fputc (flags2, destfile); // 0x1e = SRAM flags + fputc (size / 0x8000, destfile); // 0x1f = ROM size (not used by loader) +} + + +int +snes_multi (int truncate_size, char *fname) +{ +#define BUFSIZE (32 * 1024) + int n, n_files, file_no, bytestowrite, byteswritten, done, truncated = 0, + totalsize_disk = 0, totalsize_card = 0, org_do_not_calc_crc = ucon64.do_not_calc_crc; + struct stat fstate; + FILE *srcfile, *destfile; + char destname[FILENAME_MAX]; + unsigned char buffer[BUFSIZE]; + + if (truncate_size == 0) + { + fprintf (stderr, "ERROR: Can't make multi-game file of 0 bytes\n"); + return -1; + } + + if (fname != NULL) + { + strcpy (destname, fname); + n_files = ucon64.argc; + } + else + { + strcpy (destname, ucon64.argv[ucon64.argc - 1]); + n_files = ucon64.argc - 1; + } + + ucon64_file_handler (destname, NULL, OF_FORCE_BASENAME); + if ((destfile = fopen (destname, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], destname); + return -1; + } + + printf ("Creating multi-game file for Super Flash: %s\n", destname); + + file_no = 0; + for (n = 1; n < n_files; n++) + { + if (access (ucon64.argv[n], F_OK)) + continue; // "file" does not exist (option) + stat (ucon64.argv[n], &fstate); + if (!S_ISREG (fstate.st_mode)) + continue; + if (file_no == 5) // loader + 4 games + { + puts ("WARNING: A multi-game file can contain a maximum of 4 games. The other files\n" + " are ignored."); + break; + } + + ucon64.console = UCON64_UNKNOWN; + ucon64.rom = ucon64.argv[n]; + ucon64.file_size = fsizeof (ucon64.rom); + // DON'T use fstate.st_size, because file could be compressed + ucon64.rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? + ucon64.buheader_len : 0; + ucon64.rominfo->interleaved = UCON64_ISSET (ucon64.interleaved) ? + ucon64.interleaved : 0; + ucon64.do_not_calc_crc = 1; + if (snes_init (ucon64.rominfo) != 0) + printf ("WARNING: %s does not appear to be a SNES ROM\n", ucon64.rom); + else if (ucon64.rominfo->interleaved) + printf ("WARNING: %s appears to be interleaved\n", ucon64.rom); + + if ((srcfile = fopen (ucon64.rom, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], ucon64.rom); + continue; + } + if (ucon64.rominfo->buheader_len) + fseek (srcfile, ucon64.rominfo->buheader_len, SEEK_SET); + + if (file_no == 0) + { + printf ("Loader: %s\n", ucon64.rom); + if (ucon64.file_size - ucon64.rominfo->buheader_len != 32 * 1024) + printf ("WARNING: Are you sure %s is a loader binary?\n", ucon64.rom); + } + else + { + printf ("ROM%d: %s\n", file_no, ucon64.rom); + write_game_table_entry (destfile, file_no, ucon64.rominfo, + ucon64.file_size - ucon64.rominfo->buheader_len); + fseek (destfile, totalsize_disk, SEEK_SET); // restore file pointer + } + + done = 0; + byteswritten = 0; // # of bytes written per file + while (!done) + { + bytestowrite = fread (buffer, 1, BUFSIZE, srcfile); + if (totalsize_disk + bytestowrite > truncate_size) + { + bytestowrite = truncate_size - totalsize_disk; + done = 1; + truncated = 1; + printf ("Output file is %d Mbit, truncating %s, skipping %d bytes\n", + truncate_size / MBIT, ucon64.rom, ucon64.file_size - + ucon64.rominfo->buheader_len - (byteswritten + bytestowrite)); + } + else if (totalsize_card + bytestowrite > 64 * MBIT - 32 * 1024) + { + /* + Note that it is correct to check for any size larger than 64 + Mbit - 32 kB, as we always overwrite the last 32 kB of the flash + card. Note also that this means it's useless to write a smaller + loader. + */ + bytestowrite = 64 * MBIT - 32 * 1024 - totalsize_card; + done = 1; + truncated = 1; + printf ("Output file needs 64 Mbit on flash card, truncating %s, skipping %d bytes\n", + ucon64.rom, ucon64.file_size - + ucon64.rominfo->buheader_len - (byteswritten + bytestowrite)); + } + totalsize_disk += bytestowrite; + if (file_no > 0) + totalsize_card += bytestowrite; + if (bytestowrite == 0) + done = 1; + fwrite (buffer, 1, bytestowrite, destfile); + byteswritten += bytestowrite; + } + + file_no++; + + // We don't need padding for Super Flash as sf_write_rom() will care + // about alignment. Games have to be aligned to a 16 Mbit boundary. + totalsize_card = (totalsize_card + 16 * MBIT - 1) & ~(16 * MBIT - 1); + fclose (srcfile); + if (truncated) + break; + } + // fill the next game table entry + fseek (destfile, 0x4000 + (file_no - 1) * 0x20, SEEK_SET); + fputc (0, destfile); // indicate no next game + fclose (destfile); + ucon64.console = UCON64_SNES; + ucon64.do_not_calc_crc = org_do_not_calc_crc; + + return 0; +} + + +int +snes_densrt (st_rominfo_t *rominfo) +{ + int size = ucon64.file_size - rominfo->buheader_len, header_start; + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; + unsigned char buheader[512], *buffer; + + if (!nsrt_header) + { + if (ucon64.quiet < 1) + printf ("NOTE: ROM has no NSRT header -- no file has been written\n"); + return 1; + } + + ucon64_fread (buheader, 0, 512, ucon64.rom); + if (!(buffer = (unsigned char *) malloc (size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size); + exit (1); + } + ucon64_fread (buffer, rominfo->buheader_len, size, ucon64.rom); + + if (rominfo->interleaved) + header_start = SNES_HEADER_START + (snes_hirom ? 0 : size / 2); // (Ext.) HiROM : LoROM + else + header_start = rominfo->header_start; + get_nsrt_info (buffer, header_start, buheader); + memset (buheader + 0x1d0, 0, 32); // remove NSRT header + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, src_name, 0); + + ucon64_fwrite (buheader, 0, 512, dest_name, "wb"); + if (rominfo->buheader_len > 512) + fcopy (src_name, 512, rominfo->buheader_len - 512, dest_name, "ab"); + ucon64_fwrite (buffer, rominfo->buheader_len, size, dest_name, "ab"); + + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + free (buffer); + return 0; +} + + +static void +set_nsrt_checksum (unsigned char *header) +{ + int n; + char checksum = -1; + + for (n = 0x1d0; n <= 0x1ed; n++) + checksum += header[n]; + header[0x1ee] = checksum; + header[0x1ef] = ~checksum; +} + + +static void +set_nsrt_info (st_rominfo_t *rominfo, unsigned char *header) +/* + This function will write an NSRT header if the user specified a controller + type, but only if the checksum is correct. We write a complete NSRT header + only to be 100% compatible with NSRT. We are only interested in the + controller type feature, though. + NSRT is a SNES ROM tool. See developers.html. + + NSRT header format (0x1d0 - 0x1ef, offsets in _copier header_): + 0x1d0 low nibble = original country value + high nibble = bank type + 1 = LoROM + 2 = HiROM + 3 = "Extended" HiROM + 0x1d1 - 0x1e5 original game name + 0x1e6 low byte of original SNES checksum + 0x1e7 high byte of original SNES checksum + 0x1e8 - 0x1eb "NSRT" + 0x1ec header version; a value of for example 15 should be + interpreted as 1.5 + 0x1ed low nibble = port 2 controller type + high nibble = port 1 controller type + 0 = gamepad + 1 = mouse + 2 = mouse / gamepad + 3 = super scope + 4 = super scope / gamepad + 5 = Konami's justifier + 6 = multitap + 7 = mouse / super scope / gamepad + 0x1ee NSRT header checksum + the checksum is calculated by adding all bytes of the + NSRT header (except the checksum bytes themselves) + and then subtracting 1 + 0x1ef inverse NSRT header checksum +*/ +{ + int x; + + if ((UCON64_ISSET (ucon64.controller) || UCON64_ISSET (ucon64.controller2)) + && !nsrt_header) // don't overwrite these values + { + if (rominfo->current_internal_crc != rominfo->internal_crc) + { + printf ("WARNING: The controller type info will be discarded (checksum is bad)\n"); + return; + } + + header[0x1d0] = bs_dump ? 0 : snes_header.country; + if (rominfo->header_start == SNES_EROM + SNES_HEADER_START + SNES_HIROM) + header[0x1d0] |= 0x30; // Note: Extended LoROM is not supported + else + header[0x1d0] |= snes_hirom ? 0x20 : 0x10; + + if (!bs_dump && st_dump) + memcpy (header + 0x1d1, rominfo->name, 16); + else // for ST dumps, rominfo->name may be used + memcpy (header + 0x1d1, &snes_header.name, SNES_NAME_LEN); + header[0x1e6] = snes_header.checksum_low; + header[0x1e7] = snes_header.checksum_high; + memcpy (header + 0x1e8, "NSRT", 4); + header[0x1ec] = NSRT_HEADER_VERSION; + } + + if (UCON64_ISSET (ucon64.controller)) + { + for (x = 0; x < 8; x++) + if ((ucon64.controller >> x) & 1) + break; + if (x != 0 && x != 1 && x != 2 && x != 6) + { + printf ("WARNING: Invalid value for controller in port 1, using \"0\"\n"); + x = 0; + } + header[0x1ed] = x << 4; + } + if (UCON64_ISSET (ucon64.controller2)) + { + for (x = 0; x < 8; x++) + if ((ucon64.controller2 >> x) & 1) + break; + if (x >= 8) + { + printf ("WARNING: Invalid value for controller in port 2, using \"0\"\n"); + x = 0; + } + header[0x1ed] |= x; + } + + // set the checksum bytes + if (UCON64_ISSET (ucon64.controller) || UCON64_ISSET (ucon64.controller2)) + set_nsrt_checksum (header); +} + + +static void +get_nsrt_info (unsigned char *rom_buffer, int header_start, unsigned char *buheader) +{ + if (nsrt_header) + { + memcpy (rom_buffer + header_start + 16 - (st_dump ? SNES_HEADER_START : 0), + buheader + 0x1d1, (bs_dump || st_dump) ? 16 : SNES_NAME_LEN); // name + // we ignore interleaved ST dumps + if (!bs_dump) + { + // According to the NSRT specification, the region byte should be set + // to 0 for BS dumps. + rom_buffer[header_start + 41] = buheader[0x1d0] & 0x0f; // region + // NSRT only modifies the internal header. For BS dumps the internal + // checksum does not include the header. So, we don't have to + // overwrite the checksum. + rom_buffer[header_start + 44] = ~buheader[0x1e6]; // inverse checksum low + rom_buffer[header_start + 45] = ~buheader[0x1e7]; // inverse checksum high + rom_buffer[header_start + 46] = buheader[0x1e6]; // checksum low + rom_buffer[header_start + 47] = buheader[0x1e7]; // checksum high + } + } +} + + +static void +reset_header (void *header) +{ + // preserve possible NSRT header + if (nsrt_header) + { + memset (header, 0, 0x1d0); + memset ((unsigned char *) header + 0x1f0, 0, 16); + ((unsigned char *) header)[0x1ec] = NSRT_HEADER_VERSION; + set_nsrt_checksum ((unsigned char *) header); + } + else + memset (header, 0, SWC_HEADER_LEN); +} + + +static void +handle_nsrt_header (st_rominfo_t *rominfo, unsigned char *header, + const char **snes_country) +{ + char buf[800], name[SNES_NAME_LEN + 1], *str_list[9] = + { + "Gamepad", "Mouse", "Mouse / Gamepad", "Super Scope", + "Super Scope / Gamepad", "Konami's Justifier", "Multitap", + "Mouse / Super Scope / Gamepad", "Unknown" + }; + int x = header[0x1ed], ctrl1 = x >> 4, ctrl2 = x & 0xf, + name_len = (bs_dump || st_dump) ? 16 : SNES_NAME_LEN; + + memcpy (name, header + 0x1d1, name_len); + name[name_len] = 0; + for (x = 0; x < name_len; x++) + if (!isprint ((int) name[x])) + name[x] = '.'; + + if (ctrl1 > 8) + ctrl1 = 8; + if (ctrl2 > 8) + ctrl2 = 8; + sprintf (buf, "\nNSRT info:\n" + " Original country: %s\n" + " Original game name: \"%s\"\n" + " Original checksum: 0x%04x\n" + " Port 1 controller type: %s\n" + " Port 2 controller type: %s\n" + " Header version: %.1f", + NULL_TO_UNKNOWN_S (snes_country[MIN (header[0x1d0] & 0xf, SNES_COUNTRY_MAX - 1)]), + name, + header[0x1e6] + (header[0x1e7] << 8), + str_list[ctrl1], + str_list[ctrl2], + header[0x1ec] / 10.f); + strcat (rominfo->misc, buf); +} diff --git a/ucon64/2.0/src/console/snes.h b/ucon64/2.0/src/console/snes.h new file mode 100644 index 0000000..b676ae1 --- /dev/null +++ b/ucon64/2.0/src/console/snes.h @@ -0,0 +1,65 @@ +/* +snes.h - Super NES support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2001 - 2004 dbjh +Copyright (c) 2002 - 2003 John Weidman + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef SNES_H +#define SNES_H + +#include "backup/fig.h" // for st_fig_header_t + +#define SNES_HEADER_START 0x7fb0 +#define SNES_HIROM 0x8000 +#define SNES_EROM 0x400000 // "Extended" ROM, Hi or Lo + +typedef enum { SWC = 1, GD3, UFO, FIG, MGD_SNES, SMC } snes_file_t; + +extern const st_getopt2_t snes_usage[]; + +extern snes_file_t snes_get_file_type (void); +extern int snes_buheader_info (st_rominfo_t *rominfo); +extern int snes_chk (st_rominfo_t *rominfo); +extern int snes_col (const char *color); +extern int snes_demirror (st_rominfo_t *rominfo); +extern int snes_densrt (st_rominfo_t *rominfo); +extern int snes_dint (st_rominfo_t *rominfo); +extern int snes_f (st_rominfo_t *rominfo); +extern int snes_fig (st_rominfo_t *rominfo); +extern int snes_figs (st_rominfo_t *rominfo); +extern int snes_gd3 (st_rominfo_t *rominfo); +extern int snes_gd3s (st_rominfo_t *rominfo); +extern int snes_get_snes_hirom (void); +extern int snes_init (st_rominfo_t *rominfo); +extern int snes_j (st_rominfo_t *rominfo); +extern int snes_k (st_rominfo_t *rominfo); +extern int snes_l (st_rominfo_t *rominfo); +extern int snes_make_gd_names (const char *filename, st_rominfo_t *rominfo, char **names); +extern int snes_mgd (st_rominfo_t *rominfo); +extern int snes_mgh (st_rominfo_t *rominfo); +extern int snes_multi (int truncate_size, char *fname); +extern int snes_n (st_rominfo_t *rominfo, const char *name); +extern int snes_s (st_rominfo_t *rominfo); +extern void snes_set_fig_header (st_rominfo_t *rominfo, st_fig_header_t *header); +extern int snes_smc (st_rominfo_t *rominfo); +extern int snes_swc (st_rominfo_t *rominfo); +extern int snes_swcs (st_rominfo_t *rominfo); +extern int snes_ufo (st_rominfo_t *rominfo); +extern int snes_ufos (st_rominfo_t *rominfo); +#endif // SNES_H diff --git a/ucon64/2.0/src/console/swan.c b/ucon64/2.0/src/console/swan.c new file mode 100644 index 0000000..4c9dc7d --- /dev/null +++ b/ucon64/2.0/src/console/swan.c @@ -0,0 +1,206 @@ +/* +swan.c - WonderSwan support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include "misc/misc.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "swan.h" + + +static int swan_chksum (unsigned char *rom_buffer); + +const st_getopt2_t swan_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "WonderSwan/WonderSwan Color/SwanCrystal"/*"19XX/19XX/2002 Bandai"*/, + NULL + }, + { + "swan", 0, 0, UCON64_SWAN, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_SWAN_SWITCH] + }, + { + "chk", 0, 0, UCON64_CHK, + NULL, "fix ROM checksum", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} +}; + + +typedef struct st_swan_header +{ + char pad[10]; +} st_swan_header_t; +#define SWAN_HEADER_START (ucon64.file_size - 10) +#define SWAN_HEADER_LEN (sizeof (st_swan_header_t)) + +st_swan_header_t swan_header; + + +int +swan_chk (st_rominfo_t *rominfo) +{ + char buf[3], dest_name[FILENAME_MAX]; + + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); + + ucon64_fputc (dest_name, SWAN_HEADER_START + 8, rominfo->current_internal_crc, "r+b"); // low byte + ucon64_fputc (dest_name, SWAN_HEADER_START + 9, rominfo->current_internal_crc >> 8, "r+b"); // high byte + + ucon64_fread (buf, SWAN_HEADER_START + 8, 2, dest_name); + dumper (stdout, buf, 2, SWAN_HEADER_START + 8, DUMPER_HEX); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} + + +/* + Byte2 - Cartridge ID number for this developer + + Byte3 - ?Unknown? + 00 - most roms + 01 - Dig2/BAN032 & soro/KGT007 + 02 - Chocobo/BAN002 + 03 - sdej/BAN006 + 04 - srv2/BPR006 + + Byte4 - ROM Size: + 01 - ? + 02 - 4Mbit + 03 - 8Mbit + 04 - 16Mbit + 05 - ? + 06 - 32Mbit + 07 - ? + 08 - ? + 09 - 128Mbit + + Byte5 - SRAM/EEPROM Size: + 00 - 0k + 01 - 64k SRAM + 02 - 256k SRAM + 10 - 1k EEPROM (MGH001/NAP001) + 20 - 16k EEPROM + + Byte6 - Additional capabilities(?) + 04 - ?? game played in "horizontal" position (most roms) + 05 - ?? game played in "vertical" position + 10 - ?? (SUN003) +*/ +int +swan_init (st_rominfo_t *rominfo) +{ + int result = -1; + unsigned char *rom_buffer, buf[MAXBUFSIZE]; +#define SWAN_MAKER_MAX 0x30 + const char *swan_maker[SWAN_MAKER_MAX] = + { + "BAN", "BAN", NULL, NULL, NULL, + "DTE", NULL, NULL, NULL, NULL, + NULL, "SUM", "SUM", NULL, "BPR", + NULL, NULL, NULL, "KNM", NULL, + NULL, NULL, "KGT", NULL, NULL, + NULL, NULL, "MGH", NULL, "BEC", + "NAP", "BVL", NULL, NULL, NULL, + NULL, NULL, NULL, "KDK", NULL, + "SQR", NULL, NULL, NULL, NULL, + "NMC", NULL, NULL + }; + + rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? ucon64.buheader_len : 0; + + ucon64_fread (&swan_header, SWAN_HEADER_START + rominfo->buheader_len, + SWAN_HEADER_LEN, ucon64.rom); + + rominfo->header = &swan_header; + rominfo->header_start = SWAN_HEADER_START; + rominfo->header_len = SWAN_HEADER_LEN; + + // ROM maker + rominfo->maker = NULL_TO_UNKNOWN_S (swan_maker[MIN (OFFSET (swan_header, 0), + SWAN_MAKER_MAX - 1)]); + + // misc stuff + sprintf ((char *) buf, "Minimum supported system: %s", + (!OFFSET (swan_header, 1) ? "WS Monochrome" : "WS Color")); + strcat (rominfo->misc, (const char *) buf); + + if (!(rom_buffer = (unsigned char *) malloc (ucon64.file_size))) + { + fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], ucon64.file_size); + return -1; + } + ucon64_fread (rom_buffer, 0, ucon64.file_size, ucon64.rom); + + rominfo->has_internal_crc = 1; + rominfo->internal_crc_len = 2; + + if (ucon64.file_size > 10) // header itself is already 10 bytes + { + rominfo->current_internal_crc = swan_chksum (rom_buffer); + rominfo->internal_crc = OFFSET (swan_header, 8); // low byte of checksum + rominfo->internal_crc += OFFSET (swan_header, 9) << 8; // high byte of checksum + if (rominfo->current_internal_crc == rominfo->internal_crc) + result = 0; + else + result = -1; + } + if (ucon64.console == UCON64_SWAN) + result = 0; + + rominfo->console_usage = swan_usage[0].help; + rominfo->copier_usage = unknown_usage[0].help; + + free (rom_buffer); + return result; +} + + +int +swan_chksum (unsigned char *ptr) +{ + unsigned int csum = 0, t; + + if (ucon64.file_size % 4) + return -1; + + t = ucon64.file_size - 2; + while (t-- > 0) + csum += *ptr++; + + return csum & 0xffff; +} diff --git a/ucon64/2.0/src/console/swan.h b/ucon64/2.0/src/console/swan.h new file mode 100644 index 0000000..badc1e7 --- /dev/null +++ b/ucon64/2.0/src/console/swan.h @@ -0,0 +1,27 @@ +/* +swan.h - WonderSwan support for uCON64 + +Copyright (c) 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef WSWAN_H +#define WSWAN_H + +extern int swan_init (st_rominfo_t *rominfo); +extern const st_getopt2_t swan_usage[]; +extern int swan_chk (st_rominfo_t *rominfo); +#endif diff --git a/ucon64/2.0/src/genpal.txt b/ucon64/2.0/src/genpal.txt new file mode 100644 index 0000000..e843d30 --- /dev/null +++ b/ucon64/2.0/src/genpal.txt @@ -0,0 +1 @@ +# Put your PAL protection search-and-defeat "codes" (patterns) here \ No newline at end of file diff --git a/ucon64/2.0/src/libdiscmage/Makefile.in b/ucon64/2.0/src/libdiscmage/Makefile.in new file mode 100644 index 0000000..5ce17d7 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/Makefile.in @@ -0,0 +1,267 @@ +.PHONY: all clean install + +@DEFINE_DLOPEN_MAKE@ +@DEFINE_ZLIB_MAKE@ + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +includedir=@includedir@ +libdir=@libdir@ + +INSTALL=@INSTALL@ +INSTALL_PROGRAM=@INSTALL_PROGRAM@ +INSTALL_DATA=@INSTALL_DATA@ +#RANLIB=@RANLIB@ + +CC=@CC@ +CFLAGS0=-I. -Wall -W -O3 @DEFS@ +CFLAGS=$(CFLAGS0) -DDLL +ifdef DLOPEN +CFLAGS+=-DDLOPEN +endif +ifeq ($(findstring darwin,$(OSTYPE)),darwin) # for example "darwin7.0" +# On Mac OS X using -s gives the following warning: +# /usr/bin/libtool: -static not specified, -s invalid +LDFLAGS= +else +LDFLAGS=-s +endif + +# The test for Cygwin should be done before the test for DJGPP, because the +# environment variable DJGPP can be set under Bash for people who have +# installed both GCC (and friends) ports. + +GCC_WIN=0 +ifeq ($(TERM),cygwin) # test cygwin before DJGPP +GCC_WIN=1 +endif +ifeq ($(OSTYPE),msys) # test msys (MinGW's POSIX build env.) before DJGPP +GCC_WIN=1 +endif + +LIBNAME=discmage +OBJECTS=libdm_misc.o dllinit.o misc.o misc_wav.o format/format.o format/cdi.o \ + format/nero.o format/cue.o format/toc.o format/other.o +ifneq ($(OSTYPE),beos) +LIBS=-lm +else +LIBS= +endif +ifdef USE_ZLIB +LIBS+=-lz +OBJECTS+=unzip.o map.o misc_z.o +else +ifeq ($(GCC_WIN),1) +else +ifdef DJGPP # DJGPP code in dllinit needs map code +OBJECTS+=map.o +endif # DJGPP +endif # GCC_WIN +endif # USE_ZLIB + + +ifeq ($(TERM),cygwin) # test cygwin before DJGPP + +GCCA_DIR=/lib/gcc-lib/i686-pc-cygwin/3.2/ +ifeq ($(CC),g++) +LIBS+=-lstdc++ +endif +LIBS+=-L$(GCCA_DIR) -lgcc -lcygwin -lkernel32 +# kernel32 for DLOPEN and DisableThreadLibraryCalls() + +ifdef DLOPEN +ENTRY=__cygwin_dll_entry@12 +else +ENTRY=_DllMain@12 +endif + +FULLLIBNAME=$(LIBNAME).dll +DLLFLAGS=$(LDFLAGS) --dll $(OBJECTS) $(LIBS) -e $(ENTRY) -o $(LIBNAME).dll +DLLTOOLFLAGS=-d $(LIBNAME).def -b tmp.base -e tmp.exp -D $(LIBNAME).dll +ifndef DLOPEN +DLLTOOLFLAGS+=-l $(LIBNAME).a +endif + +else +ifeq ($(OSTYPE),msys) # test msys before DJGPP + +GCCA_DIR=/mingw/lib/gcc-lib/mingw32/3.2.3/ +LIBS+=-L/mingw/lib -lkernel32 -lmsvcrt -L$(GCCA_DIR) -lgcc +# MSYS problem: Specifying the library directory is necessary when compiling on +# a different filesystem than the filesystem that MinGW is installed on. + +FULLLIBNAME=$(LIBNAME).dll +DLLFLAGS=$(LDFLAGS) --dll $(OBJECTS) $(LIBS) -e _DllMain@12 -o $(LIBNAME).dll +DLLTOOLFLAGS=-d $(LIBNAME).def -b tmp.base -e tmp.exp -D $(LIBNAME).dll +ifndef DLOPEN +DLLTOOLFLAGS+=-l $(LIBNAME).a +endif + +else +ifdef DJGPP + +OBJECTS+=dxe_misc.o + +GCCA_DIR=c:/djgpp/lib/gcc-lib/djgpp/3.31 +LIBS+=-L$(GCCA_DIR) -lgcc + +DLLFLAGS=$(OBJECTS) $(LIBS) $(LDFLAGS) +# $(LDFLAGS) must come after $(OBJECTS) +FULLLIBNAME=$(LIBNAME).dxe + +else # Unix, BeOS or Mac OS X (Darwin) + +CFLAGS+=-fPIC + +ifeq ($(findstring darwin,$(OSTYPE)),darwin) +FULLLIBNAME=$(LIBNAME).dylib +else +FULLLIBNAME=$(LIBNAME).so +endif + +ifndef DLOPEN # GNU specific: "simply expanded variable" +FULLLIBNAME:=$(addprefix lib,$(FULLLIBNAME)) +endif + +ifeq ($(OSTYPE),beos) +LDFLAGS+=-nostart +else +ifeq ($(findstring darwin,$(OSTYPE)),darwin) +LDFLAGS+=-dynamiclib +else +LDFLAGS+=-shared +endif # darwin +endif # beos + +DLLFLAGS+=$(LDFLAGS) $(OBJECTS) $(LIBS) -o $(FULLLIBNAME) + +endif # DJGPP +endif # msys +endif # cygwin + + +all: $(FULLLIBNAME) + + +clean: +ifeq ($(GCC_WIN),1) + rm -f $(FULLLIBNAME) $(LIBNAME).a $(OBJECTS) *.core *.stackdump *.o \ + tmp.* +else +ifdef DJGPP + del $(FULLLIBNAME) +ifndef DLOPEN + del $(LIBNAME).a +endif + del *.o + del format\*.o +else # Unix, BeOS or Mac OS X (Darwin) + rm -f $(FULLLIBNAME) $(OBJECTS) *.core *.stackdump *.o +endif # DJGPP +endif # GCC_WIN + + +distclean: clean +ifeq ($(GCC_WIN),1) + rm -f Makefile config.log config.status config.cache config.h +else +ifdef DJGPP + del Makefile + del config.log + del config.status + del config.cache + del config.h +else + rm -f Makefile config.log config.status config.cache config.h +endif # DJGPP +endif # GCC_WIN + + +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ + + +ifeq ($(GCC_WIN),1) + +ifndef DLOPEN +$(LIBNAME).dll $(LIBNAME).a: $(OBJECTS) +else +$(LIBNAME).dll: $(OBJECTS) +endif +# echo EXPORTS > tmp.def +## nm $(OBJECTS) | grep ' T _' | sed 's/.* T _//' >> tmp.def +# nm $(OBJECTS) | grep '^........ [T] _' | sed 's/[^_]*_//' >> tmp.def +# We use nm instead of dlltool --output-def, so that we don't have to +# specify explicitly (in the source code) which symbols should be exported. +# We don't create the .def file automatically anymore. Using nm resulted in a +# (too large?) .def file which resulted in a non-working DLL when using Cygwin. + + ld --base-file tmp.base $(DLLFLAGS) + dlltool $(DLLTOOLFLAGS) + ld tmp.exp $(DLLFLAGS) + +else +ifdef DJGPP + +ifndef DLOPEN +$(LIBNAME).dxe $(LIBNAME).a: $(OBJECTS) djimport.o dlopen.o +else +$(LIBNAME).dxe: $(OBJECTS) +endif + dxegen $(LIBNAME).dxe _import_export $(DLLFLAGS) +ifndef DLOPEN +# Recompile map.c, because it has to be a normal object file for the import +# library (no references to import_export) + $(CC) $(CFLAGS0) -c map.c -o map.o + ar rs $(LIBNAME).a djimport.o map.o dlopen.o +endif + +else # Unix, BeOS or Mac OS X (Darwin) +# Unix uses LD_LIBRARY_PATH for dynamic linking, BeOS uses LIBRARY_PATH, Darwin +# uses LD_LIBRARY_PATH, DYLD_LIBRARY_PATH and DYLD_FALLBACK_LIBRARY_PATH + +$(FULLLIBNAME): $(OBJECTS) + $(CC) $(DLLFLAGS) + +endif # DJGPP +endif # GCC_WIN + + +install: +ifndef DLOPEN + [ -d $(DESTDIR)$(libdir) ] || \ + (mkdir -p $(DESTDIR)$(libdir); chmod 755 $(DESTDIR)$(libdir)) + $(INSTALL_DATA) $(FULLLIBNAME) $(DESTDIR)$(libdir)/$(FULLLIBNAME) +# $(RANLIB) $(DESTDIR)$(libdir)/$(FULLLIBNAME) +# $(LIBTOOL) --mode=install $(INSTALL) $(FULLLIBNAME) $(libdir)/$(FULLLIBNAME) + [ -d $(DESTDIR)$(includedir) ] || \ + (mkdir -p $(DESTDIR)$(includedir); chmod 755 $(DESTDIR)$(includedir)) + $(INSTALL_DATA) lib$(LIBNAME).h $(DESTDIR)$(includedir) +endif + +uninstall: +ifndef DLOPEN + rm -f $(DESTDIR)$(libdir)/$(FULLLIBNAME) + rm -f $(DESTDIR)$(includedir)/lib$(LIBNAME).h +endif + + +# Dependencies +LIBDM_STD_H=libdiscmage.h misc.h dxedll_priv.h format/format.h config.h + +libdm_misc.o: $(LIBDM_STD_H) +format/format.o: $(LIBDM_STD_H) +format/ccd.o: format/ccd.h $(LIBDM_STD_H) +format/cdi.o: format/cdi.h $(LIBDM_STD_H) +format/nero.o: format/nero.h $(LIBDM_STD_H) +format/cue.o: format/cue.h $(LIBDM_STD_H) +format/toc.o: format/toc.h $(LIBDM_STD_H) +format/other.o: format/other.h $(LIBDM_STD_H) +misc_wav.o: $(LIBDM_STD_H) +dllinit.o: $(LIBDM_STD_H) +misc.o: misc.h dxedll_priv.h +misc_z.o: misc_z.h misc.h dxedll_priv.h +dxe_misc.o: dxedll_pub.h +djimport.o: libdiscmage.h dlopen.h dxedll_pub.h +dlopen.o: dlopen.h dxedll_pub.h +map.o: map.h diff --git a/ucon64/2.0/src/libdiscmage/Makefile.orig b/ucon64/2.0/src/libdiscmage/Makefile.orig new file mode 100644 index 0000000..e3100f2 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/Makefile.orig @@ -0,0 +1,267 @@ +.PHONY: all clean install + +DLOPEN=1 +#USE_ZLIB=1 + +prefix=/usr/local +exec_prefix=${prefix} +includedir=${prefix}/include +libdir=${exec_prefix}/lib + +INSTALL=/usr/bin/install -c +INSTALL_PROGRAM=${INSTALL} +INSTALL_DATA=${INSTALL} -m 644 +#RANLIB=ranlib + +CC=gcc +CFLAGS0=-I. -Wall -W -O3 -DHAVE_CONFIG_H +CFLAGS=$(CFLAGS0) -DDLL +ifdef DLOPEN +CFLAGS+=-DDLOPEN +endif +ifeq ($(findstring darwin,$(OSTYPE)),darwin) # for example "darwin7.0" +# On Mac OS X using -s gives the following warning: +# /usr/bin/libtool: -static not specified, -s invalid +LDFLAGS= +else +LDFLAGS=-s +endif + +# The test for Cygwin should be done before the test for DJGPP, because the +# environment variable DJGPP can be set under Bash for people who have +# installed both GCC (and friends) ports. + +GCC_WIN=0 +ifeq ($(TERM),cygwin) # test cygwin before DJGPP +GCC_WIN=1 +endif +ifeq ($(OSTYPE),msys) # test msys (MinGW's POSIX build env.) before DJGPP +GCC_WIN=1 +endif + +LIBNAME=discmage +OBJECTS=libdm_misc.o dllinit.o misc.o misc_wav.o format/format.o format/cdi.o \ + format/nero.o format/cue.o format/toc.o format/other.o +ifneq ($(OSTYPE),beos) +LIBS=-lm +else +LIBS= +endif +ifdef USE_ZLIB +LIBS+=-lz +OBJECTS+=unzip.o map.o misc_z.o +else +ifeq ($(GCC_WIN),1) +else +ifdef DJGPP # DJGPP code in dllinit needs map code +OBJECTS+=map.o +endif # DJGPP +endif # GCC_WIN +endif # USE_ZLIB + + +ifeq ($(TERM),cygwin) # test cygwin before DJGPP + +GCCA_DIR=/lib/gcc-lib/i686-pc-cygwin/3.2/ +ifeq ($(CC),g++) +LIBS+=-lstdc++ +endif +LIBS+=-L$(GCCA_DIR) -lgcc -lcygwin -lkernel32 +# kernel32 for DLOPEN and DisableThreadLibraryCalls() + +ifdef DLOPEN +ENTRY=__cygwin_dll_entry@12 +else +ENTRY=_DllMain@12 +endif + +FULLLIBNAME=$(LIBNAME).dll +DLLFLAGS=$(LDFLAGS) --dll $(OBJECTS) $(LIBS) -e $(ENTRY) -o $(LIBNAME).dll +DLLTOOLFLAGS=-d $(LIBNAME).def -b tmp.base -e tmp.exp -D $(LIBNAME).dll +ifndef DLOPEN +DLLTOOLFLAGS+=-l $(LIBNAME).a +endif + +else +ifeq ($(OSTYPE),msys) # test msys before DJGPP + +GCCA_DIR=/mingw/lib/gcc-lib/mingw32/3.2.3/ +LIBS+=-L/mingw/lib -lkernel32 -lmsvcrt -L$(GCCA_DIR) -lgcc +# MSYS problem: Specifying the library directory is necessary when compiling on +# a different filesystem than the filesystem that MinGW is installed on. + +FULLLIBNAME=$(LIBNAME).dll +DLLFLAGS=$(LDFLAGS) --dll $(OBJECTS) $(LIBS) -e _DllMain@12 -o $(LIBNAME).dll +DLLTOOLFLAGS=-d $(LIBNAME).def -b tmp.base -e tmp.exp -D $(LIBNAME).dll +ifndef DLOPEN +DLLTOOLFLAGS+=-l $(LIBNAME).a +endif + +else +ifdef DJGPP + +OBJECTS+=dxe_misc.o + +GCCA_DIR=c:/djgpp/lib/gcc-lib/djgpp/3.31 +LIBS+=-L$(GCCA_DIR) -lgcc + +DLLFLAGS=$(OBJECTS) $(LIBS) $(LDFLAGS) +# $(LDFLAGS) must come after $(OBJECTS) +FULLLIBNAME=$(LIBNAME).dxe + +else # Unix, BeOS or Mac OS X (Darwin) + +CFLAGS+=-fPIC + +ifeq ($(findstring darwin,$(OSTYPE)),darwin) +FULLLIBNAME=$(LIBNAME).dylib +else +FULLLIBNAME=$(LIBNAME).so +endif + +ifndef DLOPEN # GNU specific: "simply expanded variable" +FULLLIBNAME:=$(addprefix lib,$(FULLLIBNAME)) +endif + +ifeq ($(OSTYPE),beos) +LDFLAGS+=-nostart +else +ifeq ($(findstring darwin,$(OSTYPE)),darwin) +LDFLAGS+=-dynamiclib +else +LDFLAGS+=-shared +endif # darwin +endif # beos + +DLLFLAGS+=$(LDFLAGS) $(OBJECTS) $(LIBS) -o $(FULLLIBNAME) + +endif # DJGPP +endif # msys +endif # cygwin + + +all: $(FULLLIBNAME) + + +clean: +ifeq ($(GCC_WIN),1) + rm -f $(FULLLIBNAME) $(LIBNAME).a $(OBJECTS) *.core *.stackdump *.o \ + tmp.* +else +ifdef DJGPP + del $(FULLLIBNAME) +ifndef DLOPEN + del $(LIBNAME).a +endif + del *.o + del format\*.o +else # Unix, BeOS or Mac OS X (Darwin) + rm -f $(FULLLIBNAME) $(OBJECTS) *.core *.stackdump *.o +endif # DJGPP +endif # GCC_WIN + + +distclean: clean +ifeq ($(GCC_WIN),1) + rm -f Makefile config.log config.status config.cache config.h +else +ifdef DJGPP + del Makefile + del config.log + del config.status + del config.cache + del config.h +else + rm -f Makefile config.log config.status config.cache config.h +endif # DJGPP +endif # GCC_WIN + + +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ + + +ifeq ($(GCC_WIN),1) + +ifndef DLOPEN +$(LIBNAME).dll $(LIBNAME).a: $(OBJECTS) +else +$(LIBNAME).dll: $(OBJECTS) +endif +# echo EXPORTS > tmp.def +## nm $(OBJECTS) | grep ' T _' | sed 's/.* T _//' >> tmp.def +# nm $(OBJECTS) | grep '^........ [T] _' | sed 's/[^_]*_//' >> tmp.def +# We use nm instead of dlltool --output-def, so that we don't have to +# specify explicitly (in the source code) which symbols should be exported. +# We don't create the .def file automatically anymore. Using nm resulted in a +# (too large?) .def file which resulted in a non-working DLL when using Cygwin. + + ld --base-file tmp.base $(DLLFLAGS) + dlltool $(DLLTOOLFLAGS) + ld tmp.exp $(DLLFLAGS) + +else +ifdef DJGPP + +ifndef DLOPEN +$(LIBNAME).dxe $(LIBNAME).a: $(OBJECTS) djimport.o dlopen.o +else +$(LIBNAME).dxe: $(OBJECTS) +endif + dxegen $(LIBNAME).dxe _import_export $(DLLFLAGS) +ifndef DLOPEN +# Recompile map.c, because it has to be a normal object file for the import +# library (no references to import_export) + $(CC) $(CFLAGS0) -c map.c -o map.o + ar rs $(LIBNAME).a djimport.o map.o dlopen.o +endif + +else # Unix, BeOS or Mac OS X (Darwin) +# Unix uses LD_LIBRARY_PATH for dynamic linking, BeOS uses LIBRARY_PATH, Darwin +# uses LD_LIBRARY_PATH, DYLD_LIBRARY_PATH and DYLD_FALLBACK_LIBRARY_PATH + +$(FULLLIBNAME): $(OBJECTS) + $(CC) $(DLLFLAGS) + +endif # DJGPP +endif # GCC_WIN + + +install: +ifndef DLOPEN + [ -d $(DESTDIR)$(libdir) ] || \ + (mkdir -p $(DESTDIR)$(libdir); chmod 755 $(DESTDIR)$(libdir)) + $(INSTALL_DATA) $(FULLLIBNAME) $(DESTDIR)$(libdir)/$(FULLLIBNAME) +# $(RANLIB) $(DESTDIR)$(libdir)/$(FULLLIBNAME) +# $(LIBTOOL) --mode=install $(INSTALL) $(FULLLIBNAME) $(libdir)/$(FULLLIBNAME) + [ -d $(DESTDIR)$(includedir) ] || \ + (mkdir -p $(DESTDIR)$(includedir); chmod 755 $(DESTDIR)$(includedir)) + $(INSTALL_DATA) lib$(LIBNAME).h $(DESTDIR)$(includedir) +endif + +uninstall: +ifndef DLOPEN + rm -f $(DESTDIR)$(libdir)/$(FULLLIBNAME) + rm -f $(DESTDIR)$(includedir)/lib$(LIBNAME).h +endif + + +# Dependencies +LIBDM_STD_H=libdiscmage.h misc.h dxedll_priv.h format/format.h config.h + +libdm_misc.o: $(LIBDM_STD_H) +format/format.o: $(LIBDM_STD_H) +format/ccd.o: format/ccd.h $(LIBDM_STD_H) +format/cdi.o: format/cdi.h $(LIBDM_STD_H) +format/nero.o: format/nero.h $(LIBDM_STD_H) +format/cue.o: format/cue.h $(LIBDM_STD_H) +format/toc.o: format/toc.h $(LIBDM_STD_H) +format/other.o: format/other.h $(LIBDM_STD_H) +misc_wav.o: $(LIBDM_STD_H) +dllinit.o: $(LIBDM_STD_H) +misc.o: misc.h dxedll_priv.h +misc_z.o: misc_z.h misc.h dxedll_priv.h +dxe_misc.o: dxedll_pub.h +djimport.o: libdiscmage.h dlopen.h dxedll_pub.h +dlopen.o: dlopen.h dxedll_pub.h +map.o: map.h diff --git a/ucon64/2.0/src/libdiscmage/Makefile.vc6 b/ucon64/2.0/src/libdiscmage/Makefile.vc6 new file mode 100644 index 0000000..8fd7eca --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/Makefile.vc6 @@ -0,0 +1,73 @@ +DLOPEN=1 +#USE_ZLIB=1 + +CC=cl.exe +CFLAGS=/nologo /I. /W3 /O2 /DHAVE_CONFIG_H /DDLL +!ifdef DLOPEN +CFLAGS=$(CFLAGS) /DDLOPEN +!endif + +LIBNAME=discmage +OBJECTS=libdm_misc.obj dllinit.obj misc.obj misc_wav.obj format/format.obj \ + format/cdi.obj format/nero.obj format/cue.obj format/toc.obj format/other.obj +!ifdef USE_ZLIB +LIBS=zlib.lib +OBJECTS=$(OBJECTS) unzip.obj map.obj misc_z.obj +!else +LIBS= +!endif + + +FULLLIBNAME=$(LIBNAME).dll +DLLFLAGS=/NOLOGO /DLL $(OBJECTS) $(LIBS) /DEF:$(LIBNAME).def /OUT:$(LIBNAME).dll + + +all: $(FULLLIBNAME) + + +clean: + del $(LIBNAME).dll + del $(LIBNAME).lib + del *.obj + del format\*.obj + del $(LIBNAME).exp + + +distclean: clean + del config.h + + +.c.obj: + $(CC) $(CFLAGS) /c $< /Fo$@ + + +$(LIBNAME).dll: $(OBJECTS) + link.exe $(DLLFLAGS) +# link.exe automatically creates the import library. "/IMPLIB:filename.lib" +# could be used to give the import library another name +# lib.exe $(DLLFLAGS) /OUT:$(LIBNAME).lib + +install: + +uninstall: + + +# Dependencies +LIBDM_STD_H=libdiscmage.h misc.h dxedll_priv.h format/format.h config.h + +libdm_misc.obj: $(LIBDM_STD_H) +format/format.obj: $(LIBDM_STD_H) +format/ccd.obj: format/ccd.h $(LIBDM_STD_H) +format/cdi.obj: format/cdi.h $(LIBDM_STD_H) +format/nero.obj: format/nero.h $(LIBDM_STD_H) +format/cue.obj: format/cue.h $(LIBDM_STD_H) +format/toc.obj: format/toc.h $(LIBDM_STD_H) +format/other.obj: format/other.h $(LIBDM_STD_H) +misc_wav.obj: $(LIBDM_STD_H) +dllinit.obj: $(LIBDM_STD_H) +misc.obj: misc.h dxedll_priv.h +misc_z.obj: misc_z.h misc.h dxedll_priv.h +dxe_misc.obj: dxedll_pub.h +djimport.obj: libdiscmage.h dlopen.h dxedll_pub.h +dlopen.obj: dlopen.h dxedll_pub.h +map.obj: map.h diff --git a/ucon64/2.0/src/libdiscmage/config.h.in b/ucon64/2.0/src/libdiscmage/config.h.in new file mode 100644 index 0000000..61c3d2f --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/config.h.in @@ -0,0 +1,109 @@ +/* src/config.h.in. Generated from configure.in by autoheader. */ + +/* enable debug output (default: no) */ +#undef DEBUG + +/* enable dynamic loading of library (default: no) */ +#undef DLOPEN + +/* Define to 1 if you have the header file. */ +#undef HAVE_BYTESWAP_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_DIRENT_H + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#undef HAVE_DOPRNT + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +#undef HAVE_NDIR_H + +/* Define to 1 if you have the `realpath' function. */ +#undef HAVE_REALPATH + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strftime' function. */ +#undef HAVE_STRFTIME + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_SYS_DIR_H + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_SYS_NDIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vprintf' function. */ +#undef HAVE_VPRINTF + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* enable usage of color (default: yes) */ +#undef USE_ANSI_COLOR + +/* build with gzip and zip support (default: yes) */ +#undef USE_ZLIB + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `int' if doesn't define. */ +#undef gid_t + +/* Define as `__inline' if that's what the C compiler calls it, or to nothing + if it is not supported. */ +#undef inline + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to `unsigned' if does not define. */ +#undef size_t + +/* Define to `int' if doesn't define. */ +#undef uid_t diff --git a/ucon64/2.0/src/libdiscmage/discmage.def b/ucon64/2.0/src/libdiscmage/discmage.def new file mode 100644 index 0000000..4853ab2 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/discmage.def @@ -0,0 +1,18 @@ +EXPORTS +dm_close +dm_disc_read +dm_disc_write +dm_get_version +dm_get_version_s +dm_open +dm_reopen +dm_fdopen +dm_nfo +dm_read +dm_write +dm_set_gauge +dm_cue_read +dm_cue_write +dm_toc_read +dm_toc_write +dm_rip diff --git a/ucon64/2.0/src/libdiscmage/djimport.c b/ucon64/2.0/src/libdiscmage/djimport.c new file mode 100644 index 0000000..82cfd3a --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/djimport.c @@ -0,0 +1,226 @@ +/* +djimport.c - discmage import library for DJGPP + +Copyright (c) 2002 - 2004 dbjh + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include "dlopen.h" +#include "dxedll_pub.h" +#include "libdiscmage.h" + +#define CHECK \ + if (!dxe_loaded) \ + { \ + load_dxe (); \ + dxe_loaded = 1; \ + } + +static int dxe_loaded = 0; +char djimport_path[FILENAME_MAX] = "discmage.dxe"; // default value + +static void *libdm; +static uint32_t (*dm_get_version_ptr) (void); +static const char *(*dm_get_version_s_ptr) (void); +static void (*dm_set_gauge_ptr) (void (*) (int, int)); + +static FILE *(*dm_fdopen_ptr) (dm_image_t *, int, const char *); +static dm_image_t *(*dm_open_ptr) (const char *, uint32_t); +static dm_image_t *(*dm_reopen_ptr) (const char *, uint32_t, dm_image_t *); +static int (*dm_close_ptr) (dm_image_t *); +static void (*dm_nfo_ptr) (const dm_image_t *, int, int); + +static int (*dm_disc_read_ptr) (const dm_image_t *); +static int (*dm_disc_write_ptr) (const dm_image_t *); + +static int (*dm_read_ptr) (char *, int, int, const dm_image_t *); +static int (*dm_write_ptr) (const char *, int, int, const dm_image_t *); + +static dm_image_t *(*dm_toc_read_ptr) (dm_image_t *, const char *); +static int (*dm_toc_write_ptr) (const dm_image_t *); + +static dm_image_t *(*dm_cue_read_ptr) (dm_image_t *, const char *); +static int (*dm_cue_write_ptr) (const dm_image_t *); + +static int (*dm_rip_ptr) (const dm_image_t *, int, uint32_t); + + +static void +load_dxe (void) +{ + libdm = open_module (djimport_path); + + dm_get_version_ptr = get_symbol (libdm, "dm_get_version"); + dm_get_version_s_ptr = get_symbol (libdm, "dm_get_version_s"); + dm_set_gauge_ptr = get_symbol (libdm, "dm_set_gauge"); + + dm_open_ptr = get_symbol (libdm, "dm_open"); + dm_fdopen_ptr = get_symbol (libdm, "dm_fdopen"); + dm_reopen_ptr = get_symbol (libdm, "dm_reopen"); + dm_close_ptr = get_symbol (libdm, "dm_close"); + dm_nfo_ptr = get_symbol (libdm, "dm_nfo"); + + dm_disc_read_ptr = get_symbol (libdm, "dm_disc_read"); + dm_disc_write_ptr = get_symbol (libdm, "dm_disc_write"); + + dm_read_ptr = get_symbol (libdm, "dm_read"); + dm_write_ptr = get_symbol (libdm, "dm_write"); + + dm_toc_read_ptr = get_symbol (libdm, "dm_toc_read"); + dm_toc_write_ptr = get_symbol (libdm, "dm_toc_write"); + + dm_cue_read_ptr = get_symbol (libdm, "dm_cue_read"); + dm_cue_write_ptr = get_symbol (libdm, "dm_cue_write"); + + dm_rip_ptr = get_symbol (libdm, "dm_rip"); +} + + +uint32_t +dm_get_version (void) +{ + CHECK + return dm_get_version_ptr (); +} + + +const char * +dm_get_version_s (void) +{ + CHECK + return dm_get_version_s_ptr (); +} + + +void +dm_set_gauge (void (*a) (int, int)) +{ + CHECK + dm_set_gauge_ptr (a); +} + + +FILE * +dm_fdopen (dm_image_t *a, int b, const char *c) +{ + CHECK + return dm_fdopen_ptr (a, b, c); +} + + +dm_image_t * +dm_open (const char *a, uint32_t b) +{ + CHECK + return dm_open_ptr (a, b); +} + + +dm_image_t * +dm_reopen (const char *a, uint32_t b, dm_image_t *c) +{ + CHECK + return dm_reopen_ptr (a, b, c); +} + + +int +dm_close (dm_image_t *a) +{ + CHECK + return dm_close_ptr (a); +} + + +void +dm_nfo (const dm_image_t *a, int b, int c) +{ + CHECK + dm_nfo_ptr (a, b, c); +} + + +int +dm_disc_read (const dm_image_t *a) +{ + CHECK + return dm_disc_read_ptr (a); +} + + +int +dm_disc_write (const dm_image_t *a) +{ + CHECK + return dm_disc_write_ptr (a); +} + + +int +dm_read (char *a, int b, int c, const dm_image_t *d) +{ + CHECK + return dm_read_ptr (a, b, c, d); +} + + +int +dm_write (const char *a, int b, int c, const dm_image_t *d) +{ + CHECK + return dm_write_ptr (a, b, c, d); +} + + +dm_image_t * +dm_toc_read (dm_image_t *a, const char *b) +{ + CHECK + return dm_toc_read_ptr (a, b); +} + + +int +dm_toc_write (const dm_image_t *a) +{ + CHECK + return dm_toc_write_ptr (a); +} + + +dm_image_t * +dm_cue_read (dm_image_t *a, const char *b) +{ + CHECK + return dm_cue_read_ptr (a, b); +} + + +int +dm_cue_write (const dm_image_t *a) +{ + CHECK + return dm_cue_write_ptr (a); +} + + +int +dm_rip (const dm_image_t *a, int b, uint32_t c) +{ + CHECK + return dm_rip_ptr (a, b, c); +} diff --git a/ucon64/2.0/src/libdiscmage/dllinit.c b/ucon64/2.0/src/libdiscmage/dllinit.c new file mode 100644 index 0000000..70fb7e0 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/dllinit.c @@ -0,0 +1,162 @@ +/* +dllinit.c - DLL initialization code + +Copyright (c) 2002 - 2004 dbjh + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#if defined __CYGWIN__ && defined DLOPEN +#include + +DECLARE_CYGWIN_DLL(DllMain); +#endif + + +#include +#include +#include "libdiscmage.h" + + +#if defined __CYGWIN__ || defined _WIN32 +#include + +#ifdef __cplusplus +extern "C" BOOL WINAPI DllMain (HINSTANCE h, DWORD reason, LPVOID ptr); +#endif + +/* + When this function is explicitly specified as entry point (for example by + using the option /ENTRY of link.exe) printf() does not produce output when + called from this function. +*/ +BOOL WINAPI +DllMain (HINSTANCE h, DWORD reason, LPVOID ptr) +{ + (void) ptr; // warning remover + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls ((HMODULE) h); + break; + case DLL_PROCESS_DETACH: + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + } + return TRUE; +} + +#elif defined DJGPP +#include "dxedll_pub.h" // for st_symbol_t +#include "dxedll_priv.h" // must be included after headers +#include "map.h" // of external libraries! + + +int dxe_init (void); +void *dxe_symbol (char *symbol_name); + +st_symbol_t import_export = +{ + dxe_init, dxe_symbol, sizeof (st_symbol_t), + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + {0, NULL, NULL, 0, 0, 0, NULL, 0}, {0, NULL, NULL, 0, 0, 0, NULL, 0}, + {0, NULL, NULL, 0, 0, 0, NULL, 0}, NULL, NULL, NULL, 0 +}; +st_map_t *symbol; + + +int +dxe_init (void) +{ + symbol = map_create (17); + symbol->cmp_key = (int (*) (void *, void *)) strcmp; // How beautiful! ;-) + + symbol = map_put (symbol, "dm_get_version", dm_get_version); + symbol = map_put (symbol, "dm_get_version_s", dm_get_version_s); + symbol = map_put (symbol, "dm_set_gauge", dm_set_gauge); + + symbol = map_put (symbol, "dm_open", dm_open); + symbol = map_put (symbol, "dm_reopen", dm_reopen); + symbol = map_put (symbol, "dm_fdopen", dm_fdopen); + symbol = map_put (symbol, "dm_close", dm_close); + symbol = map_put (symbol, "dm_nfo", dm_nfo); + + symbol = map_put (symbol, "dm_read", dm_read); + symbol = map_put (symbol, "dm_write", dm_write); + + symbol = map_put (symbol, "dm_disc_read", dm_disc_read); + symbol = map_put (symbol, "dm_disc_write", dm_disc_write); + + symbol = map_put (symbol, "dm_toc_read", dm_toc_read); + symbol = map_put (symbol, "dm_toc_write", dm_toc_write); + + symbol = map_put (symbol, "dm_cue_read", dm_cue_read); + symbol = map_put (symbol, "dm_cue_write", dm_cue_write); + + symbol = map_put (symbol, "dm_rip", dm_rip); + + return 0; +} + + +/* + Normally, the code that uses a dynamic library knows what it wants, i.e., it + searches for specific symbol names. So, for a program that uses a DXE the + code could look something like: + void *handle; + int (*function1) (int); + + handle = open_module (MODULE_NAME); + function1 = ((st_symbol_t *) handle)->function1; + + However, by adding a symbol loading function, st_symbol_t doesn't have to be + updated if the DXE should export more or other symbols, which makes using a + DXE less error prone. Changing st_symbol_t would also require a recompile of + the code that uses the DXE (which is a *bad* thing). + A symbol loading function also makes an "extension API" a bit more elegant, + because the extension functions needn't be hardcoded in st_symbol_t; +*/ +void * +dxe_symbol (char *symbol_name) +{ + return map_get (symbol, symbol_name); +} + +#elif defined __unix__ + +// The functions _init() and _fini() seem to be reserved on GNU/Linux +#if 0 +void _init (void) +{ +} + + +void _fini (void) +{ +} +#endif + +#endif diff --git a/ucon64/2.0/src/libdiscmage/dlopen.c b/ucon64/2.0/src/libdiscmage/dlopen.c new file mode 100644 index 0000000..1a6f65e --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/dlopen.c @@ -0,0 +1,294 @@ +/* +dlopen.c - DLL support code + +Copyright (c) 2002 - 2004 dbjh + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef DJGPP +#include +#elif defined __unix__ || defined __APPLE__ // Mac OS X actually; __unix__ is also +#include // defined under Cygwin (and DJGPP) +#elif defined _WIN32 +#include +#elif defined __BEOS__ +#include +#include +#endif + +#include +#include +#include + + +#ifdef DJGPP +#include "dxedll_pub.h" +#include "map.h" + + +#define INITIAL_HANDLE 1 +static st_map_t *dxe_map; +extern int errno; + + +void +uninit_func (void) +{ + fprintf (stderr, "An uninitialized member of the import/export structure was called!\n" + "Update dlopen.c/open_module()\n"); + exit (1); +} +#endif + + +void * +open_module (char *module_name) +{ + void *handle; +#ifdef DJGPP + static int new_handle = INITIAL_HANDLE; + int n, m; + st_symbol_t *sym = _dxe_load (module_name); + /* + _dxe_load() doesn't really return a handle. It returns a pointer to the one + symbol a DXE module can export. + */ + if (sym == 0) + { + fprintf (stderr, "Error while loading DXE module: %s\n", module_name); + exit (1); + } + + if (sym->size != sizeof (st_symbol_t)) + { + fprintf (stderr, "Incompatible DXE module: %s\n", module_name); + exit (1); + } + + // initialize the import/export structure + + /* + Catch calls to uninitialized members in case a new function was added to + st_symbol_t, but forgotten to initialize here. + */ + m = sizeof (st_symbol_t) / sizeof (void (*) (void)); + for (n = 0; n < m && ((void (**) (void)) sym)[n] != 0; n++) // Don't overwrite values initialized by DXE + ; + for (; n < m; n++) + ((void (**) (void)) sym)[n] = uninit_func; + + // initialize functions + sym->printf = printf; + sym->fprintf = fprintf; + sym->vfprintf = vfprintf; + sym->sprintf = sprintf; + sym->vsprintf = vsprintf; + sym->puts = puts; + sym->fputs = fputs; + sym->sscanf = sscanf; + sym->vsscanf = vsscanf; + sym->fopen = fopen; + sym->fdopen = fdopen; + sym->popen = popen; + sym->fclose = fclose; + sym->pclose = pclose; + sym->fseek = fseek; + sym->ftell = ftell; + sym->rewind = rewind; + sym->fread = fread; + sym->fwrite = fwrite; + sym->fgetc = fgetc; + sym->fgets = fgets; + sym->feof = feof; + sym->fputc = fputc; + sym->fflush = fflush; + sym->ferror = ferror; + sym->rename = rename; + sym->remove = remove; + + sym->free = free; + sym->malloc = malloc; + sym->calloc = calloc; + sym->realloc = realloc; + sym->exit = exit; + sym->strtol = strtol; + sym->getenv = getenv; + sym->srand = srand; + sym->rand = rand; + sym->atoi = atoi; + + sym->memcpy = memcpy; + sym->memset = memset; + sym->strcmp = strcmp; + sym->strcpy = strcpy; + sym->strncpy = strncpy; + sym->strcat = strcat; + sym->strncat = strncat; + sym->strcasecmp = strcasecmp; + sym->strncasecmp = strncasecmp; + sym->strchr = strchr; + sym->strrchr = strrchr; + sym->strpbrk = strpbrk; + sym->strspn = strspn; + sym->strcspn = strcspn; + sym->strlen = strlen; + sym->strstr = strstr; + sym->strdup = strdup; + sym->strtok = strtok; + + sym->tolower = tolower; + sym->toupper = toupper; + sym->isupper = isupper; + + sym->opendir = opendir; + sym->readdir = readdir; + sym->closedir = closedir; + + sym->access = access; + sym->rmdir = rmdir; + sym->isatty = isatty; + sym->chdir = chdir; + sym->getcwd = getcwd; + sym->getuid = getuid; + sym->sync = sync; + sym->truncate = truncate; + + sym->stat = stat; + sym->chmod = chmod; + sym->mkdir = mkdir; + sym->time = time; + sym->delay = delay; + sym->__dpmi_int = __dpmi_int; + + // initialize variables + sym->__dj_stdin = __dj_stdin; + sym->__dj_stdout = __dj_stdout; + sym->__dj_stderr = __dj_stderr; + sym->__dj_ctype_flags = __dj_ctype_flags; + sym->__dj_ctype_tolower = __dj_ctype_tolower; + sym->__dj_ctype_toupper = __dj_ctype_toupper; + sym->errno = errno; + + // initialize the DXE module + sym->dxe_init (); + + if (new_handle == INITIAL_HANDLE) + dxe_map = map_create (10); + dxe_map = map_put (dxe_map, (void *) new_handle, sym); + handle = (void *) new_handle++; +#elif defined __unix__ || defined __APPLE__ // Mac OS X actually + /* + We use dlcompat under Mac OS X simply because it's there. I (dbjh) don't + want to add extra code only because "using the native api's is the supported + method of loading dynamically on Mac OS X" (Peter O'Gorman, maintainer of + dlcompat). Besides, dlcompat has been tested while any new code we add, not. + RTLD_NOW is ignored by dlcompat (7-12-2003). + */ + if ((handle = dlopen (module_name, RTLD_LAZY)) == NULL) + { + fputs (dlerror (), stderr); + fputc ('\n', stderr); + exit (1); + } +#elif defined _WIN32 + if ((handle = LoadLibrary (module_name)) == NULL) + { + LPTSTR strptr; + + FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError (), + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &strptr, 0, NULL); + // Note the construct with strptr. You wouldn't believe what a bunch of + // fucking morons those guys at Microsoft are! + fputs (strptr, stderr); + LocalFree (strptr); + exit (1); + } +#elif defined __BEOS__ + if ((int) (handle = (void *) load_add_on (module_name)) < B_NO_ERROR) + { + fprintf (stderr, "Error while loading add-on image: %s\n", module_name); + exit (1); + } +#endif + + return handle; +} + + +void * +get_symbol (void *handle, char *symbol_name) +{ + void *symptr; +#ifdef DJGPP + st_symbol_t *sym = map_get (dxe_map, handle); + if (sym == NULL) + { + fprintf (stderr, "Invalid handle: %x\n", (int) handle); + exit (1); + } + + symptr = sym->dxe_symbol (symbol_name); + if (symptr == NULL) + { + fprintf (stderr, "Could not find symbol: %s\n", symbol_name); + exit (1); + } +#elif defined __unix__ || defined __APPLE__ // Mac OS X actually, see + char *strptr; // comment in open_module() + + symptr = dlsym (handle, symbol_name); + if ((strptr = (char *) dlerror ()) != NULL) // this is "the correct way" + { // according to the info page + fputs (strptr, stderr); + fputc ('\n', stderr); + exit (1); + } +#elif defined _WIN32 + symptr = (void *) GetProcAddress ((HINSTANCE) handle, symbol_name); + if (symptr == NULL) + { + LPTSTR strptr; + + FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError (), + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &strptr, 0, NULL); + fputs (strptr, stderr); + LocalFree (strptr); + exit (1); + } +#elif defined __BEOS__ + int status = get_image_symbol ((int) handle, symbol_name, + B_SYMBOL_TYPE_TEXT, &symptr); // B_SYMBOL_TYPE_DATA/B_SYMBOL_TYPE_ANY + if (status != B_OK) + { + fprintf (stderr, "Could not find symbol: %s\n", symbol_name); + exit (1); + } +#endif + + return symptr; +} diff --git a/ucon64/2.0/src/libdiscmage/dlopen.h b/ucon64/2.0/src/libdiscmage/dlopen.h new file mode 100644 index 0000000..7dbcb0a --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/dlopen.h @@ -0,0 +1,27 @@ +/* +dlopen.h - DLL support code + +Copyright (c) 2002 dbjh + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef DLOPEN_H +#define DLOPEN_H + +void *open_module (char *module_name); +void *get_symbol (void *handle, char *symbol_name); + +#endif // DLOPEN_H diff --git a/ucon64/2.0/src/libdiscmage/dxe_misc.c b/ucon64/2.0/src/libdiscmage/dxe_misc.c new file mode 100644 index 0000000..5a819eb --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/dxe_misc.c @@ -0,0 +1,590 @@ +/* +dxe_misc.c - miscellaneous functions for the grand libdiscmage DXE hack + +Copyright 2003 - 2004 dbjh + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + The original reason this file was created was because we can do the + (re)definition of the names of the buffered file I/O functions only once. + For a DXE they would have to be (re)defined twice if we want to be able to + use the zlib & unzip code in miscz.c; once to substitute the names to make + code use the import/export "table" and once to make code use the f*2() + functions in miscz.c. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include "dxedll_pub.h" + +extern st_symbol_t import_export; +int errno = 0; // TODO: verify how dangerous this is (is it?) + + +int +printf (const char *format, ...) +{ + va_list argptr; + int n_chars; + + va_start (argptr, format); + n_chars = import_export.vfprintf (&import_export.__dj_stdout, format, argptr); + va_end (argptr); + return n_chars; +} + + +int +fprintf (FILE *file, const char *format, ...) +{ + va_list argptr; + int n_chars; + + va_start (argptr, format); + n_chars = import_export.vfprintf (file, format, argptr); + va_end (argptr); + return n_chars; +} + + +int +vfprintf (FILE *file, const char *format, va_list argptr) +{ + return import_export.vfprintf (file, format, argptr); +} + + +int +sprintf (char *buffer, const char *format, ...) +{ + va_list argptr; + int n_chars; + + va_start (argptr, format); + n_chars = import_export.vsprintf (buffer, format, argptr); + va_end (argptr); + return n_chars; +} + + +int +vsprintf (char *buffer, const char *format, va_list argptr) +{ + return import_export.vsprintf (buffer, format, argptr); +} + + +int +puts (const char *str) +{ + return import_export.puts (str); +} + + +int +fputs (const char *str, FILE *file) +{ + return import_export.fputs (str, file); +} + + +int +sscanf (const char *buffer, const char *format, ...) +{ + va_list argptr; + int n_chars; + + va_start (argptr, format); + n_chars = import_export.vsscanf (buffer, format, argptr); + va_end (argptr); + return n_chars; +} + + +int +vsscanf (const char *buffer, const char *format, va_list argptr) +{ + return import_export.vsscanf (buffer, format, argptr); +} + + +FILE * +fopen (const char *filename, const char *mode) +{ + return import_export.fopen (filename, mode); +} + + +FILE * +fdopen (int fd, const char *mode) +{ + return import_export.fdopen (fd, mode); +} + + +FILE * +popen (const char *command, const char *mode) +{ + return import_export.popen (command, mode); +} + + +int +fclose (FILE *file) +{ + return import_export.fclose (file); +} + + +int +pclose (FILE *stream) +{ + return import_export.pclose (stream); +} + + +int +fseek (FILE *file, long offset, int mode) +{ + return import_export.fseek (file, offset, mode); +} + + +long +ftell (FILE *file) +{ + return import_export.ftell (file); +} + + +void +rewind (FILE *file) +{ + import_export.rewind (file); +} + + +size_t +fread (void *buffer, size_t size, size_t number, FILE *file) +{ + return import_export.fread (buffer, size, number, file); +} + + +size_t +fwrite (const void *buffer, size_t size, size_t number, FILE *file) +{ + return import_export.fwrite (buffer, size, number, file); +} + + +int +fgetc (FILE *file) +{ + return import_export.fgetc (file); +} + + +char * +fgets (char *buffer, int maxlength, FILE *file) +{ + return import_export.fgets (buffer, maxlength, file); +} + + +int +feof (FILE *file) +{ + return import_export.feof (file); +} + + +int +fputc (int character, FILE *file) +{ + return import_export.fputc (character, file); +} + + +int +fflush (FILE *file) +{ + return import_export.fflush (file); +} + + +int +ferror (FILE *file) +{ + return import_export.ferror (file); +} + + +int +rename (const char *oldname, const char *newname) +{ + return import_export.rename (oldname, newname); +} + + +int +remove (const char *filename) +{ + return import_export.remove (filename); +} + + +void +free (void *mem) +{ + import_export.free (mem); +} + + +void * +malloc (size_t size) +{ + return import_export.malloc (size); +} + + +void * +calloc (size_t n_elements, size_t size) +{ + return import_export.calloc (n_elements, size); +} + + +void * +realloc (void *mem, size_t size) +{ + return import_export.realloc (mem, size); +} + + +void +exit (int code) +{ + import_export.exit (code); +} + + +long +strtol (const char *str, char **endptr, int base) +{ + return import_export.strtol (str, endptr, base); +} + + +char * +getenv (const char *name) +{ + return import_export.getenv (name); +} + + +void +srand (unsigned seed) +{ + import_export.srand (seed); +} + + +int +rand (void) +{ + return import_export.rand (); +} + + +int +atoi (const char *str) +{ + return import_export.atoi (str); +} + + +void * +memcpy (void *dest, const void *src, size_t size) +{ + return import_export.memcpy (dest, src, size); +} + + +void * +memset (void *mem, int value, size_t size) +{ + return import_export.memset (mem, value, size); +} + + +int +strcmp (const char *s1, const char *s2) +{ + return import_export.strcmp (s1, s2); +} + + +char * +strcpy (char *dest, const char *src) +{ + return import_export.strcpy (dest, src); +} + + +char * +strncpy (char *dest, const char *src, size_t n) +{ + return import_export.strncpy (dest, src, n); +} + + +char * +strcat (char *s1, const char *s2) +{ + return import_export.strcat (s1, s2); +} + + +char * +strncat (char *s1, const char *s2, size_t n) +{ + return import_export.strncat (s1, s2, n); +} + + +int +strcasecmp (const char *s1, const char *s2) +{ + return import_export.strcasecmp (s1, s2); +} + + +int +strncasecmp (const char *s1, const char *s2, size_t n) +{ + return import_export.strncasecmp (s1, s2, n); +} + + +char * +strchr (const char *str, int c) +{ + return import_export.strchr (str, c); +} + + +char * +strrchr (const char *str, int c) +{ + return import_export.strrchr (str, c); +} + + +char * +strpbrk (const char *str, const char *set) +{ + return import_export.strpbrk (str, set); +} + + +size_t +strspn (const char *str, const char *set) +{ + return import_export.strspn (str, set); +} + + +size_t +strcspn (const char *str, const char *set) +{ + return import_export.strcspn (str, set); +} + + +size_t +strlen (const char *str) +{ + return import_export.strlen (str); +} + + +char * +strstr (const char *s1, const char *s2) +{ + return import_export.strstr (s1, s2); +} + + +char * +strdup (const char *str) +{ + return import_export.strdup (str); +} + + +char * +strtok (char *s1, const char *s2) +{ + return import_export.strtok (s1, s2); +} + + +#undef tolower +int +tolower (int c) +{ + return import_export.tolower (c); +} + + +#undef toupper +int +toupper (int c) +{ + return import_export.toupper (c); +} + + +#undef isupper +int +isupper (int c) +{ + return import_export.isupper (c); +} + + +DIR * +opendir (const char *dirname) +{ + return import_export.opendir (dirname); +} + + +struct dirent * +readdir (DIR *dir) +{ + return import_export.readdir (dir); +} + + +int +closedir (DIR *dir) +{ + return import_export.closedir (dir); +} + + +int +access (const char *filename, int mode) +{ + return import_export.access (filename, mode); +} + + +int +rmdir (const char *dirname) +{ + return import_export.rmdir (dirname); +} + + +int +isatty (int fd) +{ + return import_export.isatty (fd); +} + + +int +chdir (const char *dirname) +{ + return import_export.chdir (dirname); +} + + +char * +getcwd (char *buffer, size_t size) +{ + return import_export.getcwd (buffer, size); +} + + +int +getuid (void) +{ + return import_export.getuid (); +} + + +int +sync (void) +{ + return import_export.sync (); +} + + +int +truncate (const char *filename, off_t size) +{ + return import_export.truncate (filename, size); +} + + +int +stat (const char *filename, struct stat *statbuf) +{ + return import_export.stat (filename, statbuf); +} + + +int +chmod (const char *filename, mode_t mode) +{ + return import_export.chmod (filename, mode); +} + + +int +mkdir (const char *path, mode_t mode) +{ + return import_export.mkdir (path, mode); +} + + +time_t +time (time_t *t) +{ + return import_export.time (t); +} + + +void +delay (unsigned nmillis) +{ + import_export.delay (nmillis); +} + + +int +__dpmi_int (int vector, __dpmi_regs *regs) +{ + return import_export.__dpmi_int (vector, regs); +} diff --git a/ucon64/2.0/src/libdiscmage/dxedll_priv.h b/ucon64/2.0/src/libdiscmage/dxedll_priv.h new file mode 100644 index 0000000..3945d89 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/dxedll_priv.h @@ -0,0 +1,74 @@ +/* +dxedll_priv.h - DXE support (code/data private to DXE) + +Copyright (c) 2002 - 2003 dbjh + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef DXEDLL_PRIV_H +#define DXEDLL_PRIV_H + +#include "dxedll_pub.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern st_symbol_t import_export; + +// zlib functions +#if 0 +#define gzopen import_export.gzopen +#define gzclose import_export.gzclose +#define gzwrite import_export.gzwrite +#define gzgets import_export.gzgets +#define gzeof import_export.gzeof +#define gzseek import_export.gzseek +#define gzputc import_export.gzputc +#define gzread import_export.gzread +#define gzgetc import_export.gzgetc +#define gzrewind import_export.gzrewind +#define gztell import_export.gztell + +// unzip functions +#define unzOpen import_export.unzOpen +#define unzOpenCurrentFile import_export.unzOpenCurrentFile +#define unzGoToFirstFile import_export.unzGoToFirstFile +#define unzClose import_export.unzClose +#define unzGetGlobalInfo import_export.unzGetGlobalInfo +#define unzGoToNextFile import_export.unzGoToNextFile +#define unzCloseCurrentFile import_export.unzCloseCurrentFile +#define unzeof import_export.unzeof +#define unzReadCurrentFile import_export.unzReadCurrentFile +#define unztell import_export.unztell +#define unzGetCurrentFileInfo import_export.unzGetCurrentFileInfo +#endif + +// variables +#define __dj_stdin import_export.__dj_stdin +#define __dj_stdout import_export.__dj_stdout +#define __dj_stderr import_export.__dj_stderr +#define __dj_ctype_flags import_export.__dj_ctype_flags +#define __dj_ctype_tolower import_export.__dj_ctype_tolower +#define __dj_ctype_toupper import_export.__dj_ctype_toupper +//#define errno import_export.errno + +#ifdef __cplusplus +} +#endif + +#endif // DXEDLL_PRIV_H diff --git a/ucon64/2.0/src/libdiscmage/dxedll_pub.h b/ucon64/2.0/src/libdiscmage/dxedll_pub.h new file mode 100644 index 0000000..e23baae --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/dxedll_pub.h @@ -0,0 +1,159 @@ +/* +dxedll_pub.h - DXE client support code + +Copyright (c) 2002 - 2004 dbjh + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef DXEDLL_PUB_H +#define DXEDLL_PUB_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_DIRENT_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct st_symbol +{ + // functions exported by the DXE module + int (*dxe_init) (void); + void *(*dxe_symbol) (char *symbol_name); + + // variables exported by the DXE module + int size; // yes, this var needs to be here + // (or it could be a function) + /* + functions imported by the DXE module + Note that most functions used by the DXE module and not defined in it + should be listed here. That includes standard C library functions and + variables. + */ + int (*printf) (const char *, ...); + int (*fprintf) (FILE *, const char *, ...); + int (*vfprintf) (FILE *, const char *, va_list); + int (*sprintf) (char *, const char *, ...); + int (*vsprintf) (char *, const char *, va_list); + int (*puts) (const char *); + int (*fputs) (const char *, FILE *); + int (*sscanf) (const char *, const char *, ...); + int (*vsscanf) (const char *, const char *, va_list); + FILE *(*fopen) (const char *, const char *); + FILE *(*fdopen) (int, const char *); + FILE *(*popen) (const char *, const char *); + int (*fclose) (FILE *); + int (*pclose) (FILE *); + int (*fseek) (FILE *, long, int); + long (*ftell) (FILE *); + void (*rewind) (FILE *); + size_t (*fread) (void *, size_t, size_t, FILE *); + size_t (*fwrite) (const void *, size_t, size_t, FILE *); + int (*fgetc) (FILE *); + char *(*fgets) (char *, int, FILE *); + int (*feof) (FILE *); + int (*fputc) (int, FILE *); + int (*fflush) (FILE *); + int (*ferror) (FILE *); + int (*rename) (const char *, const char *); + int (*remove) (const char *); + + void (*free) (void *); + void *(*malloc) (size_t); + void *(*calloc) (size_t, size_t); + void *(*realloc) (void *, size_t); + void (*exit) (int) __attribute__ ((noreturn)); + long (*strtol) (const char *, char **, int); + char *(*getenv) (const char *); + void (*srand) (unsigned); + int (*rand) (void); + int (*atoi) (const char *); + + void *(*memcpy) (void *, const void *, size_t); + void *(*memset) (void *, int, size_t); + int (*strcmp) (const char *, const char *); + char *(*strcpy) (char *, const char *); + char *(*strncpy) (char *, const char *, size_t); + char *(*strcat) (char *, const char *); + char *(*strncat) (char *, const char *, size_t); + int (*strcasecmp) (const char *, const char *); + int (*strncasecmp) (const char *, const char *, size_t); + char *(*strchr) (const char *, int); + char *(*strrchr) (const char *, int); + char *(*strpbrk) (const char *, const char *); + size_t (*strspn) (const char *, const char *); + size_t (*strcspn) (const char *, const char *); + size_t (*strlen) (const char *); + char *(*strstr) (const char *, const char *); + char *(*strdup) (const char *); + char *(*strtok) (char *, const char *); + + int (*tolower) (int); + int (*toupper) (int); + int (*isupper) (int); + + DIR *(*opendir) (const char *); + struct dirent *(*readdir) (DIR *); + int (*closedir) (DIR *); + + // va_start(), va_arg() and va_end() are macros + + int (*access) (const char *, int); + int (*rmdir) (const char *); + int (*isatty) (int); + int (*chdir) (const char *); + char *(*getcwd) (char *, size_t); + int (*getuid) (void); + int (*sync) (void); + int (*truncate) (const char *, off_t); + + int (*stat) (const char *, struct stat *); + int (*chmod) (const char *, mode_t); + int (*mkdir) (const char *, mode_t); + time_t (*time) (time_t *); + void (*delay) (unsigned); + int (*__dpmi_int) (int, __dpmi_regs *); + + // Put all variables AFTER the functions. This makes it easy to catch + // uninitialized function pointers. + FILE __dj_stdin, __dj_stdout, __dj_stderr; + // WARNING: actually the __dj_ctype_X variables are arrays + unsigned short *__dj_ctype_flags; + unsigned char *__dj_ctype_tolower, *__dj_ctype_toupper; + int errno; +} st_symbol_t; + +#ifdef __cplusplus +} +#endif + +#endif // DXEDLL_PUB_H diff --git a/ucon64/2.0/src/libdiscmage/format/ccd.c b/ucon64/2.0/src/libdiscmage/format/ccd.c new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/format/ccd.c @@ -0,0 +1 @@ + diff --git a/ucon64/2.0/src/libdiscmage/format/ccd.h b/ucon64/2.0/src/libdiscmage/format/ccd.h new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/format/ccd.h @@ -0,0 +1 @@ + diff --git a/ucon64/2.0/src/libdiscmage/format/cdi.c b/ucon64/2.0/src/libdiscmage/format/cdi.c new file mode 100644 index 0000000..c060f7a --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/format/cdi.c @@ -0,0 +1,309 @@ +/* +cdi.c - DiscJuggler/CDI image support for libdiscmage + +Copyright (c) 2002 NoisyB (noisyb@gmx.net) +based on specs and code by Dext + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif +#include +#include +#include +#include "../misc.h" +#include "../libdiscmage.h" +#include "../libdm_misc.h" +#include "format.h" +#ifdef DJGPP +#include "../dxedll_priv.h" +#endif + +// cdi images are little-endian + +// header magic +#define CDI_V2 0x80000004 +#define CDI_V3 0x80000005 +#define CDI_V35 0x80000006 +#define CDI_V4 (CDI_V35) + +static uint32_t header_start = 0, version = 0, position = 0; + +int +cdi_track_init (dm_track_t *track, FILE *fh) +// based on the DiscJuggler 4.0 header specs by Dext (see comments below) +{ + int cdi_track_modes[] = {2048, 2336, 2352, 0}, x = 0; + uint32_t value32; +// uint16_t value16; + uint8_t value8; + char value_s[300]; + const char track_header_magic[] = { 0, 0, 0x01, 0, 0, 0, (const char) 0xFF, + (const char) 0xFF, (const char) 0xFF, (const char) 0xFF}; + +#ifdef DEBUG + printf ("%lx\n", ftell(fh)); + fflush (stdout); +#endif + +#if 0 + fread (&value32, 4, 1, fh); // [ 8 unkown data (not NULL) 3.00.780 only (may not be present)] + if (le2me_32 (value32)) + fseek (fh, 2, SEEK_CUR); // skip +#else +// callibrate + fseek (fh, -9, SEEK_CUR); + for (x = 0; x < 64; x++) + { + if (fread (&value_s, 1, 10, fh) != 10) + return -1; + fseek (fh, -10, SEEK_CUR); + if (!memcmp (track_header_magic, value_s, 10)) + break; + fseek (fh, 1, SEEK_CUR); + } +#endif +#ifdef DEBUG + printf ("new offset: %lx\n", ftell (fh)); + fflush (stdout); +#endif + + for (x = 0; x < 2; x++) // 20 00 00 01 00 00 00 FF FF FF FF + { // 00 00 01 00 00 00 FF FF FF FF track start mark? + fread (&value_s, 1, 10, fh); + if (memcmp (track_header_magic, value_s, 10)) + { + fprintf (stderr, "ERROR: could not locate the track start mark (pos: %08lx)\n", ftell (fh)); + return -1; + } + } + + fseek (fh, 4, SEEK_CUR); // 4 discjuggler settings no idea of internal bit fields + fread (&value8, 1, 1, fh); // 1 filename_lenght + fread (&value_s, 1, value8, fh); // [fl] [filename] +#ifdef DEBUG + if (value8 < sizeof (value_s)) + { + value_s[value8] = 0; + puts (value_s); + fflush (stdout); + } +#endif + fseek (fh, 19, SEEK_CUR); // 1 NULL + // 10 NULL (ISRC?) + // 4 2 (always?) + // 4 NULL + fread (&value32, 4, 1, fh); // [ 4 0x80000000 (4.x only)] + +// fseek (fh, 4, SEEK_CUR); // 4 max_cd_length = 0x514C8 (333000 dec) or 0x57E40 (360000 dec) + if (le2me_32 (value32) == 0x80000000) + fseek (fh, 4, SEEK_CUR); // [ 4 0x980000 (4.x only)] + fseek (fh, 2, SEEK_CUR); // 2 2 (always?) + fread (&value32, 4, 1, fh); // 4 track->pregap_len = 0x96 (150 dec) in sectors + track->pregap_len = le2me_32 (value32); +#ifdef DEBUG + printf ("pregap: %x\n", track->pregap_len); + fflush (stdout); +#endif + fread (&value32, 4, 1, fh); // 4 track_length (in sectors) + track->track_len = le2me_32 (value32); +#ifdef DEBUG + printf ("track len: %d\n", track->track_len); + fflush (stdout); +#endif + fseek (fh, 6, SEEK_CUR); // NULL + fread (&value32, 4, 1, fh); // 4 track_mode (0 = audio, 1 = mode1, 2 = mode2) + track->mode = le2me_32 (value32); +#ifdef DEBUG + printf ("track mode: %d\n", track->mode); + fflush (stdout); +#endif + fseek (fh, 12, SEEK_CUR); // 4 NULL + // 4 session_number (starting at 0) + // 4 track_number (in current session, starting at 0) + fread (&value32, 4, 1, fh); // 4 start_lba + track->start_lba = le2me_32 (value32); + fread (&value32, 4, 1, fh); // 4 total_length (pregap+track), less if truncated + track->total_len = le2me_32 (value32); +#if 1 + fseek (fh, 16, SEEK_CUR); // 16 NULL + fread (&value32, 4, 1, fh); // 4 sector_size (0 = 2048, 1 = 2336, 2 = 2352) + value32 = le2me_32 (value32); + if (/* value8 < 0 || */ value32 > 2) + { + fprintf (stderr, "ERROR: unsupported sector size (%u)\n", (unsigned int) value32); + return -1; + } + track->sector_size = cdi_track_modes[value32]; +#else + fseek (fh, 19, SEEK_CUR); // 19 NULL + fread (&value8, 1, 1, fh); // 1 sector_size (0 = 2048, 1 = 2336, 2 = 2352) +// value32 = le2me_32 (value32); + if (/* value8 < 0 || */ value8 > 2) + { + fprintf (stderr, "ERROR: unsupported sector size (%d)\n", value8); + return -1; + } + track->sector_size = cdi_track_modes[value8]; +#endif +#ifdef DEBUG + printf ("sector size: %d\n\n", track->sector_size); + fflush (stdout); +#endif + fseek (fh, 29, SEEK_CUR); // 4 0 = audio, 4 = data + // 1 NULL + // 4 total_length (again?) + // 20 NULL + if (version != CDI_V2) + { + fseek (fh, 5, SEEK_CUR); + fread (&value32, 4, 1, fh); // 9 unknown data 3.0 only (build 780+: 00FFFFFFFFFFFFFFFF) + if (le2me_32 (value32) == 0xffffffff) + fseek (fh, 78, SEEK_CUR); // 78 unknown data 3.00.780 only (not NULL) + } + + fseek (fh, (version == CDI_V2 ? 12 : 13), SEEK_CUR); // skip session + +#ifdef DEBUG + printf ("%lx\n", ftell(fh)); + fflush (stdout); +#endif + + track->track_start = position; // position inside the image + position += (track->total_len * track->sector_size); + +#if 0 + pos = ftell (fh); + fseek (fh, track->track_start, SEEK_SET); + dm_track_init (track, fh); // try to get more (precise) info from the track itself + fseek (fh, pos, SEEK_SET); +#endif + + return 0; +} + + +int +cdi_init (dm_image_t *image) +{ + typedef struct + { + uint32_t version; + char *version_s; + } st_probe_t; + + static const st_probe_t probe[] = { + {CDI_V2, "DiscJuggler/CDI image (v2.x)"}, + {CDI_V3, "DiscJuggler/CDI image (v3.x)"}, + {CDI_V35, "DiscJuggler/CDI image (v3.5)"}, + {CDI_V4, "DiscJuggler/CDI image (v4.x)"}, + {0, NULL} + }; + int s = 0, t = 0, x = 0, size = q_fsize (image->fname); + FILE *fh; + uint16_t value_16; + uint32_t value_32; +#ifdef DEBUG + int result; + unsigned char buf[MAXBUFSIZE]; +#endif + + header_start = + version = + position = 0; + + if (size < 8) + return -1; // image file is too small + + if (!(fh = fopen (image->fname, "rb"))) + return -1; + + fseek (fh, size - 8, SEEK_SET); + fread (&value_32, 1, 4, fh); + image->version = version = value_32; + fread (&value_32, 1, 4, fh); + image->header_start = header_start = value_32; + + if (!image->header_start) + { + fclose (fh); + return -1; // bad image + } + + // a supported DiscJuggler version? + for (x = 0; probe[x].version; x++) + if (image->version == probe[x].version) + break; + + if (image->version != probe[x].version) + { + fclose (fh); + return -1; + } + + image->desc = probe[x].version_s; + + image->header_start = + (image->version == CDI_V35 || image->version == CDI_V4 ? + size - image->header_start : image->header_start); + +//TODO: get the correct header size when != (image->version == CDI_V35 || image->version == CDI_V4) +// image->header_len = size - image->header_start; + +#ifdef DEBUG + fseek (fh, image->header_start, SEEK_SET); + memset (&buf, 0, MAXBUFSIZE); + result = fread (buf, 1, MAXBUFSIZE, fh); + mem_hexdump (buf, result, image->header_start); +#endif + + fseek (fh, image->header_start, SEEK_SET); + + fread (&value_16, 2, 1, fh); // how many sessions? + image->sessions = value_16; + + if (!image->sessions) + { + fclose (fh); + return -1; + } + + image->tracks = 0; + for (s = 0; s < image->sessions; s++) + { +#ifdef DEBUG + printf ("track# offset: %lx\n", ftell (fh)); + fflush (stdout); +#endif + fread (&value_16, 1, 2, fh); // how many tracks in this session? + + for (t = 0; t < value_16; t++) + if (!cdi_track_init (&image->track[image->tracks], fh)) + { + image->tracks++; // total # of tracks + image->session[s]++; // # of tracks for this session + } + else + { + fclose (fh); + return !image->tracks ? (-1) : 0; + } + } + + fclose (fh); + return 0; +} diff --git a/ucon64/2.0/src/libdiscmage/format/cdi.h b/ucon64/2.0/src/libdiscmage/format/cdi.h new file mode 100644 index 0000000..d624d64 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/format/cdi.h @@ -0,0 +1,27 @@ +/* +cdi.h - DiscJuggler/CDI image support for libdiscmage + +Copyright (c) 2002 NoisyB (noisyb@gmx.net) +based on specs and code by Dext + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef CDI_H +#define CDI_H +extern int cdi_init (dm_image_t *image); +extern int cdi_track_init (dm_track_t *track, FILE *fh); + +#endif // CDI_H diff --git a/ucon64/2.0/src/libdiscmage/format/cue.c b/ucon64/2.0/src/libdiscmage/format/cue.c new file mode 100644 index 0000000..4176a2b --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/format/cue.c @@ -0,0 +1,273 @@ +/* +cue.c - CUE support for libdiscmage + +Copyright (c) 2002 - 2003 NoisyB (noisyb@gmx.net) + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif +#include "../misc.h" +#include "../libdiscmage.h" +#include "../libdm_misc.h" +#include "format.h" +#ifdef DJGPP +#include "../dxedll_priv.h" +#endif + + +const st_track_desc_t cue_desc[] = + { + {DM_MODE1_2048, "MODE1/2048"}, // CD-ROM Mode1 Data (cooked) + {DM_MODE1_2352, "MODE1/2352"}, // CD-ROM Mode1 Data (raw) + {DM_MODE2_2336, "MODE2/2336"}, // CD-ROM XA Mode2 Data + {DM_MODE2_2352, "MODE2/2352"}, // CD-ROM XA Mode2 Data + {DM_AUDIO, "AUDIO"}, // Audio/Music (2352) +#if 0 + {DM_UNKNOWN, "CDG"}, // Karaoke CD+G (2448) + {DM_UNKNOWN, "CDI/2336"}, // CD-I Mode2 Data + {DM_UNKNOWN, "CDI/2352"}, // CD-I Mode2 Data +#endif + {0, NULL} + }; + + +static const char * +cue_get_desc (int id) +{ + int x = 0; + + for (x = 0; cue_desc[x].desc; x++) + if (id == cue_desc[x].id) + return cue_desc[x].desc; + return ""; +} + + +dm_image_t * +dm_cue_read (dm_image_t *image, const char *cue_file) +{ + char buf[MAXBUFSIZE]; +#if 0 + , *p = NULL; + char inum[3]; + char min; + char sec; + char fps; + int already_set = 0; +#endif + int t = 0, x = 0; + FILE *fh = NULL; + + if (!(fh = fopen (cue_file, "rb"))) + return NULL; // cue_file not found + + for (; fgets (buf, MAXBUFSIZE, fh); t++) + { + if (strstr (buf, " TRACK ")) + { + dm_track_t *track = (dm_track_t *) &image->track[t]; + + track->sector_size = track->mode = 0; + + for (x = 0; cue_desc[x].desc; x++) + if (stristr (buf, cue_desc[x].desc)) + { + dm_get_track_mode_by_id (cue_desc[x].id, &track->mode, &track->sector_size); + break; + } + + if (!track->sector_size) + { + fclose (fh); + return !t ? NULL : image; + } + } +#if 0 + else if (strstr (buf, " INDEX ")) + { + /* check stuff here so if the answer is false the else stuff below won't be executed */ + strncpy (inum, &buf[10], 2); + inum[2] = '\0'; + if ((already_set == 0) && + ((strcmp (inum, "00") == 0) || (strcmp (inum, "01") == 0))) + { + already_set = 1; + + min = ((buf[13] - '0') << 4) | (buf[14] - '0'); + sec = ((buf[16] - '0') << 4) | (buf[17] - '0'); + fps = ((buf[19] - '0') << 4) | (buf[20] - '0'); + + track->minute = (((min >> 4) * 10) + (min & 0xf)); + track->second = (((sec >> 4) * 10) + (sec & 0xf)); + track->frame = (((fps >> 4) * 10) + (fps & 0xf)); + } + } + else if (strstr (buf, "PREGAP ")) + { + } + else if (strstr (buf, "POSTGAP ")) + { + } +#endif + } + + fclose (fh); + + return image; +} + + +int +dm_cue_write (const dm_image_t *image) +{ + int result = (-1), t = 0; + + for (t = 0; t < image->tracks; t++) + { +#if FILENAME_MAX > MAXBUFSIZE + char buf[FILENAME_MAX]; +#else + char buf[MAXBUFSIZE]; +#endif +// char buf2[MAXBUFSIZE]; + dm_track_t *track = (dm_track_t *) &image->track[t]; + int m = 0, s = 0, f = 0; + FILE *fh = NULL; + + strcpy (buf, image->fname); +#if 0 + sprintf (buf2, "_%d.CUE", t); + set_suffix (buf, buf2); +#else + set_suffix (buf, ".CUE"); +#endif + + if (!(fh = fopen (buf, "wb"))) + { + result = -1; + continue; + } + else + result = 0; + + switch (track->mode) + { + case 0: // audio + // TODO: WAVE, AIFF, ... + fprintf (fh, "FILE \"%s\" WAVE\r\n", image->fname); + break; + + case 1: // iso + fprintf (fh, "FILE \"%s\" BINARY\r\n", image->fname); + break; + + default: // bin + fprintf (fh, "FILE \"%s\" BINARY\r\n", image->fname); + break; + } + + fprintf (fh, " TRACK %02d %s\r\n", + t + 1, + cue_get_desc (track->id)); + + /* + You can use the PREGAP command to specify the length of a track + pre-gap. CDRWIN internally generates the pre-gap data. No data is + consumed from the current data file. + */ + if (track->pregap_len > 0) + { + dm_lba_to_msf (track->pregap_len, &m, &s, &f); + fprintf (fh, " PREGAP %02d:%02d:%02d\r\n", m, s, f); + } + + /* + You can use the INDEX command to specify indexes (or + subindexes) within a track. + */ + fprintf (fh, " INDEX 01 00:00:00\r\n"); // default + + + /* + You can use the POSTGAP command to specify the length of a + track post-gap. CDRWIN internally generates the post-gap data. + No data is consumed from the current data file. + */ + if (track->postgap_len > 0) + { + dm_lba_to_msf (track->postgap_len, &m, &s, &f); + fprintf (fh, " POSTGAP %02d:%02d:%02d\r\n", m, s, f); + } + + fclose (fh); + } + + return result; +} + + +int +cue_init (dm_image_t *image) +{ + int t = 0; + FILE *fh = NULL; +#if 0 + char buf[FILENAME_MAX]; + + strcpy (buf, image->fname); + set_suffix (buf, ".CUE"); + if (dm_cue_read (image, buf)) // read and parse cue into dm_image_t + { + image->desc = "ISO/BIN track (with CUE file)"; + return 0; + } +#endif + +#if 1 + image->sessions = + image->tracks = + image->session[0] = 1; +#endif + + // missing or invalid cue? try the image itself + if (!(fh = fopen (image->fname, "rb"))) + return -1; + + for (t = 0; t < image->tracks; t++) + { + dm_track_t *track = (dm_track_t *) &image->track[t]; + + if (!dm_track_init (track, fh)) + { + track->track_len = + track->total_len = q_fsize (image->fname) / track->sector_size; + } + else + { + fclose (fh); + return !t ? (-1) : 0; + } + } + + dm_cue_write (image); // write the missing cue + + image->desc = "ISO/BIN track (missing CUE file created)"; + + fclose (fh); + return 0; +} diff --git a/ucon64/2.0/src/libdiscmage/format/cue.h b/ucon64/2.0/src/libdiscmage/format/cue.h new file mode 100644 index 0000000..5c0b34f --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/format/cue.h @@ -0,0 +1,25 @@ +/* +cue.h - CUE support for libdiscmage + +Copyright (c) 2002 - 2003 NoisyB (noisyb@gmx.net) + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef CUE_H +#define CUE_H +extern int dm_mkcue (const dm_image_t *image); +extern int cue_init (dm_image_t *image); +#endif // CUE_H diff --git a/ucon64/2.0/src/libdiscmage/format/format.c b/ucon64/2.0/src/libdiscmage/format/format.c new file mode 100644 index 0000000..6254932 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/format/format.c @@ -0,0 +1,358 @@ +/* +format.c - support of different image formats for libdiscmage + +Copyright (c) 2004 NoisyB (noisyb@gmx.net) + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include +#include +#ifdef DEBUG +#ifdef __GNUC__ +#warning DEBUG active +#else +#pragma message ("DEBUG active") +#endif +#endif +#include +#include "../misc.h" +#include "../libdiscmage.h" +#include "../libdm_misc.h" +#include "format.h" +#include "cdi.h" +#include "cue.h" +#include "nero.h" +#include "other.h" +#include "toc.h" +#include "ccd.h" +#ifdef DJGPP // DXE's are specific to DJGPP +#include "../dxedll_priv.h" +#endif + +/* + callibrate() a brute force function that tries to find a iso header + or anything else that could identify a file as an + image (can be very slow) +*/ +#if 0 +static FILE * +callibrate (const char *s, int len, FILE *fh) +// brute force callibration +{ + int32_t pos = ftell (fh); + char buf[MAXBUFSIZE]; +// malloc ((len + 1) * sizeof (char)); + int size = 0; + int tries = 0; //TODO: make this an arg + + fseek (fh, 0, SEEK_END); + size = ftell (fh); + fseek (fh, pos, SEEK_SET); + + for (; pos < size - len && tries < 32768; pos++, tries++) + { + fseek (fh, pos, SEEK_SET); + fread (&buf, len, 1, fh); +#ifdef DEBUG + mem_hexdump (buf, len, ftell (fh) - len); + mem_hexdump (s, len, ftell (fh) - len); +#endif + if (!memcmp (s, buf, len)) + { + fseek (fh, -len, SEEK_CUR); + return fh; + } + } + + return NULL; +} +#endif + + +int +dm_track_init (dm_track_t *track, FILE *fh) +{ + int pos = 0, x = 0, identified = 0; + const char sync_data[] = {0, (const char) 0xff, (const char) 0xff, + (const char) 0xff, (const char) 0xff, + (const char) 0xff, (const char) 0xff, + (const char) 0xff, (const char) 0xff, + (const char) 0xff, (const char) 0xff, 0}; + char value_s[32]; + uint8_t value8 = 0; + + fseek (fh, track->track_start, SEEK_SET); +#if 1 + fread (value_s, 1, 16, fh); +#else +// callibrate + fseek (fh, -15, SEEK_CUR); + for (x = 0; x < 64; x++) + { + if (fread (&value_s, 1, 16, fh) != 16) + return -1; + fseek (fh, -16, SEEK_CUR); + if (!memcmp (sync_data, value_s, 12)) + break; + fseek (fh, 1, SEEK_CUR); + } +#endif + + if (!memcmp (sync_data, value_s, 12)) + { + value8 = (uint8_t) value_s[15]; + + for (x = 0; track_probe[x].sector_size; x++) + if (track_probe[x].mode == value8) + { + // search for valid PVD in sector 16 of source image + pos = (track_probe[x].sector_size * 16) + + track_probe[x].seek_header + track->track_start; + fseek (fh, pos, SEEK_SET); + fread (value_s, 1, 16, fh); + if (!memcmp (pvd_magic, &value_s, 8) || + !memcmp (svd_magic, &value_s, 8) || + !memcmp (vdt_magic, &value_s, 8)) + { + identified = 1; + break; + } + } + } + + // no sync_data found? probably MODE1/2048 + if (!identified) + { + x = 0; + if (track_probe[x].sector_size != 2048) + fprintf (stderr, "ERROR: dm_track_init()\n"); + + fseek (fh, (track_probe[x].sector_size * 16) + + track_probe[x].seek_header + track->track_start, SEEK_SET); + fread (value_s, 1, 16, fh); + + if (!memcmp (pvd_magic, &value_s, 8) || + !memcmp (svd_magic, &value_s, 8) || + !memcmp (vdt_magic, &value_s, 8)) + identified = 1; + } + + if (!identified) + { + fprintf (stderr, "ERROR: could not find iso header of current track\n"); + return -1; + } + + track->sector_size = track_probe[x].sector_size; + track->mode = track_probe[x].mode; + track->seek_header = track_probe[x].seek_header; + track->seek_ecc = track_probe[x].seek_ecc; + track->iso_header_start = (track_probe[x].sector_size * 16) + track_probe[x].seek_header; + track->id = dm_get_track_mode_id (track->mode, track->sector_size); + + return 0; +} + + +dm_image_t * +dm_reopen (const char *fname, uint32_t flags, dm_image_t *image) +// recurses through all _init functions to find correct image type +{ + typedef struct + { + int type; + int (*init) (dm_image_t *); + int (*track_init) (dm_track_t *, FILE *); + } st_probe_t; + + static st_probe_t probe[] = + { + {DM_CDI, cdi_init, cdi_track_init}, + {DM_NRG, nrg_init, nrg_track_init}, +// {DM_CCD, ccd_init, ccd_track_init}, + {DM_CUE, cue_init, dm_track_init}, + {DM_TOC, toc_init, dm_track_init}, + {DM_OTHER, other_init, dm_track_init}, + {0, NULL, NULL} + }; + int x, identified = 0; +// static dm_image_t image2; + FILE *fh = NULL; + +#ifdef DEBUG + printf ("sizeof (dm_track_t) == %d\n", sizeof (dm_track_t)); + printf ("sizeof (dm_image_t) == %d\n", sizeof (dm_image_t)); + fflush (stdout); +#endif + + if (image) + dm_close (image); + + if (access (fname, F_OK) != 0) + return NULL; + + if (!image) +#if 1 + image = (dm_image_t *) malloc (sizeof (dm_image_t)); +#else + image = (dm_image_t *) &image2; +#endif + + memset (image, 0, sizeof (dm_image_t)); + if (!image) + return NULL; + + image->desc = ""; // deprecated + + for (x = 0; probe[x].type; x++) + if (probe[x].init) + { + dm_clean (image); + image->flags = flags; + strcpy (image->fname, fname); + + if (!probe[x].init (image)) + { + identified = 1; + break; + } + } + + if (!identified) // unknown image + return NULL; + + image->type = probe[x].type; + + if (!(fh = fopen (image->fname, "rb"))) + return image; + + // verify header or sheet informations + for (x = 0; x < image->tracks; x++) + { + dm_track_t *track = (dm_track_t *) &image->track[x]; + + if (track->mode != 0) // AUDIO/2352 has no iso header + track->iso_header_start = track->track_start + (track->sector_size * (16 + track->pregap_len)) + track->seek_header; + +#ifdef DEBUG + printf ("iso header offset: %d\n\n", (int) track->iso_header_start); + fflush (stdout); +#endif + + track->id = dm_get_track_mode_id (track->mode, track->sector_size); + } + + fclose (fh); + + return image; +} + + +dm_image_t * +dm_open (const char *fname, uint32_t flags) +{ + return dm_reopen (fname, flags, NULL); +} + + +int +dm_close (dm_image_t *image) +{ +#if 1 + free (image); +#else + memset (image, 0, sizeof (dm_image_t)); +#endif + image = NULL; + return 0; +} + + +int +dm_read (char *buffer, int track_num, int sector, const dm_image_t *image) +{ + dm_track_t *track = (dm_track_t *) &image->track[track_num]; + FILE *fh; + + if (!(fh = fopen (image->fname, "rb"))) + return 0; + + if (fseek (fh, track->track_start + (track->sector_size * sector), SEEK_SET) != 0) + { + fclose (fh); + return 0; + } + + if (fread (buffer, track->sector_size, 1, fh) != track->sector_size) + { + fclose (fh); + return 0; + } + + fclose (fh); + return track->sector_size; +} + + +int +dm_write (const char *buffer, int track_num, int sector, const dm_image_t *image) +{ + (void) buffer; + (void) track_num; + (void) sector; + (void) image; + return 0; +} + + +int +dm_disc_read (const dm_image_t *image) +{ + (void) image; + fprintf (stderr, dm_msg[DEPRECATED], "dm_disc_read()"); + fflush (stderr); + return 0; +} + + +int +dm_disc_write (const dm_image_t *image) +{ + (void) image; + fprintf (stderr, dm_msg[DEPRECATED], "dm_disc_write()"); + fflush (stderr); + return 0; +} + + +int +dm_seek (dm_image_t *image, int track_num, int sector) +{ + (void) image; // warning remover + (void) track_num; // warning remover + (void) sector; // warning remover + return 0; +} diff --git a/ucon64/2.0/src/libdiscmage/format/format.h b/ucon64/2.0/src/libdiscmage/format/format.h new file mode 100644 index 0000000..4374ac0 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/format/format.h @@ -0,0 +1,24 @@ +/* +format.h - support of different image formats for libdiscmage + +Copyright (c) 2004 NoisyB (noisyb@gmx.net) + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef FORMAT_H +#define FORMAT_H +extern int dm_track_init (dm_track_t *track, FILE *fh); +#endif // FORMAT_H diff --git a/ucon64/2.0/src/libdiscmage/format/nero.c b/ucon64/2.0/src/libdiscmage/format/nero.c new file mode 100644 index 0000000..d552aba --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/format/nero.c @@ -0,0 +1,519 @@ +/* +nero.c - Nero image support for libdiscmage + +Copyright (c) 2003 NoisyB (noisyb@gmx.net) +based on specs and code by Dext + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif +#include +#include +#include +#include "../misc.h" +#include "../libdiscmage.h" +#include "../libdm_misc.h" +#include "format.h" +#ifdef DJGPP +#include "../dxedll_priv.h" +#endif + +// nero images are big-endian + +// structs for chunks +typedef struct +{ + unsigned char type[4]; + int32_t tracks; // ? + int32_t track_type; + int32_t pad; +} st_cues_t; + + +typedef struct +{ + unsigned char type[4]; + int32_t tracks0; // ? + int32_t tracks1; // ? + unsigned char pad[14]; + unsigned char unknown; // ? 0x20 + unsigned char pad1; + unsigned char unknown2; // ? 0x01 + unsigned char tracks; +} st_daoi_t; + + +typedef struct +{ + int32_t current; // global_current_track? + int32_t current_msf; + int32_t unknown; // global_current_track |= 0x0100 + int32_t current_msf2; +} st_cues_track_t; + + +typedef struct +{ + unsigned char pad[10]; + int32_t sector_size; + int32_t mode; + int32_t offset; + int32_t offset2; + int32_t offset3; +} st_daoi_track_t; + + +typedef struct +{ + int32_t track_mode; + int32_t current_msf; +} st_cues_tail_t; + + +typedef struct +{ + unsigned char type[4]; + int32_t unknown; // 0x04 ? + int32_t tracks; +} st_sinf_t; + + +typedef struct +{ + unsigned char type[4]; + int32_t tracks; +} st_etnf_t; + + +typedef struct +{ + int32_t offset; + int32_t track_len; // image->track_len * sector_size + int32_t track_mode; + int32_t start_lba; + int32_t pad; +} st_etnf_track_t; + + +// header magic +#define NRG_CUES "CUES" // nero_pre5.5 +#define NRG_CUEX "CUEX" // nero5.5 +#define NRG_DAOI "DAOI" // nero_pre5.5 +#define NRG_DAOX "DAOX" // nero5.5 +#define NRG_SINF "SINF" +#define NRG_ETNF "ETNF" +#define NRG_ETN2 "ETN2" +#define NRG_END_ "END!" +#define NRG_NERO "NERO" // nero_pre5.5 +#define NRG_NER5 "NER5" // nero5.5 +#define NRG_CUES "CUES" +#define NRG_DAOI "DAOI" + +static uint32_t header_start = 0;//, version = 0, position = 0; + + +static int +nrg_chunk_offset (const dm_image_t *image, const char *chunk_id) +{ + FILE *fh = NULL; + int pos = 0; + char value_s[32]; + + if (!(fh = fopen (image->fname, "rb"))) + return FALSE; + + fseek (fh, image->header_start, SEEK_SET); + +// find + while (fread (&value_s, 4, 1, fh)) + if (!memcmp (value_s, chunk_id, 4)) + { + pos = ftell (fh); + fclose (fh); + return pos; + } + fclose (fh); + + return 0; +} + + +static int +nrg_chunk_size (const dm_image_t *image, const char *chunk_id) +// returns chunk length +{ + int pos = 0; + FILE *fh = NULL; + int value_32 = 0; + + pos = nrg_chunk_offset (image, chunk_id); + if (!pos) + return 0; + + if (!(fh = fopen (image->fname, "rb"))) + return 0; + + fseek (fh, pos, SEEK_SET); + + fread (&value_32, 4, 1, fh); + fclose (fh); + + return be2me_32 (value_32); // return chunk length +} + + +static int +nrg_ident_chunk (const char *chunk_id) +{ + static const char *chunk[] = + { + NRG_CUEX, + NRG_CUES, + NRG_DAOX, + NRG_DAOI, + NRG_SINF, + NRG_ETNF, + NRG_ETN2, + NRG_END_, + NRG_NERO, + NRG_NER5, + NRG_CUES, + NRG_DAOI, + NULL + }; + int x = 0; + + for (x = 0; chunk[x]; x++) + if (!memcmp (chunk[x], chunk_id, 4)) + return TRUE; + return FALSE; +} + + +int +nrg_track_init (dm_track_t *track, FILE *fh) +{ + uint8_t value8; + uint32_t value32; + + fread (&value8, 1, 1, fh); + if (value8 == 42) + track->mode = 2; + else if (value8 == 01) + track->mode = 0; + else + track->mode = 1; + + fread (&value8, 1, 1, fh); // track # + fread (&value8, 1, 1, fh); // index? + fread (&value8, 1, 1, fh); // pad byte + + fread (&value32, 4, 1, fh); // start_lba + track->start_lba = be2me_32 (value32); + + return 0; +} + + +int +nrg_init (dm_image_t * image) +{ + typedef struct + { + char *version; + char *version_s; + } st_probe_t; + + static const st_probe_t probe[] = { + {NRG_NERO, "Nero/NRG image (<=v5.0)"}, + {NRG_NER5, "Nero/NRG image (v5.5)"}, + {NULL, NULL} + }; + int s = 0, t = 0, x = 0, size = q_fsize (image->fname); + int result = 0; + FILE *fh; + char value_s[16]; +// uint16_t value_16; + uint32_t value_32; +#ifdef DEBUG + unsigned char buf[MAXBUFSIZE]; +#endif + + header_start = 0; +// version = +// position = 0; + + if (size < 12) + return -1; // image file is too small + + if (!(fh = fopen (image->fname, "rb"))) + return -1; + + fseek (fh, -4, SEEK_END); + fread (&value_32, 1, 4, fh); + image->header_start = header_start = be2me_32 (value_32); + if (image->header_start < 1) + { + fclose (fh); + return -1; // bad image + } + + image->desc = NULL; + for (x = 0; probe[x].version; x++) + if (nrg_chunk_offset (image, probe[x].version)) + { + image->desc = probe[x].version_s; + break; + } + + if (!image->desc) + { + fclose (fh); + return -1; + } + + fseek (fh, image->header_start, SEEK_SET); + fread (&value_s, 1, 4, fh); + if (!nrg_ident_chunk (value_s)) + { + fclose (fh); + return -1; + } + + image->header_len = size - image->header_start; + +#ifdef DEBUG + fseek (fh, image->header_start, SEEK_SET); + memset (&buf, 0, MAXBUFSIZE); + result = fread (buf, 1, MAXBUFSIZE, fh); + mem_hexdump (buf, result, image->header_start); +#endif + + fseek (fh, image->header_start, SEEK_SET); + + if (!(result = nrg_chunk_size (image, NRG_CUEX))) + { + fclose (fh); + return -1; + } + +// fread (&value_16, 2, 1, fh); // how many sessions? + image->sessions = 1; + image->tracks = (result / 16) - 1; + + for (s = 0; s < image->sessions; s++) + for (t = 0; t < image->tracks; t++) + { + if (!nrg_track_init (&image->track[t], fh)) + { + image->session[s]++; // # of tracks for this session + } + else + { + fclose (fh); + return -1; + } + } + + fclose (fh); + + return 0; +} + + +#if 0 +void +nrg_write_cues_hdr (char *fcues, int32_t *fcues_i, dm_image_t * image) +{ + int32_t value32; + st_cues_t cues; + + memset (&cues, 0, sizeof (st_cues_t)); + + strcpy (cues.type, CUES_S); + + cues.tracks = me2be_32 (((image->tracks + 1) * 16)); + + value32 = (image->track_type != 0 ? 0x41000000 : 0x01000000); + cues.track_type = me2be_32 (value32); + + memcpy (&(fcues[*fcues_i]), &cues, sizeof (st_cues_t)); + *fcues_i += sizeof (st_cues_t); +} + + +void +nrg_write_daoi (char *fdaoi, int32_t *fdaoi_i, dm_image_t * image) +{ + st_daoi_t daoi; + + memset (&daoi, 0, sizeof (st_daoi_t)); + + strcpy (daoi.type, DAOI_S); + + daoi.tracks0 = me2be_32 (22 + (30 * image->tracks)); + daoi.tracks1 = me2be_32 (22 + (30 * image->tracks)); + daoi.unknown = me2be_32 (0x20); + daoi.unknown2 = me2be_32 (0x01); + daoi.tracks = me2be_32 (image->tracks); + + memcpy (&(fdaoi[*fdaoi_i]), &daoi, sizeof (st_daoi_t)); + *fdaoi_i += sizeof (st_daoi_t); +} + + +void +nrg_write_cues_track (char *fcues, int32_t *fcues_i, dm_image_t * image) +{ + int32_t value32; + int32_t current_msf; + struct cdrom_msf msf; + st_cues_track_t cues_track; + + value32 = (image->mode != 0 ? 0x41000000 : 0x01000000); + value32 |= (to_bcd (image->global_current_track) << 16) & 0x00ff0000; + cues_track.current = me2be_32 (value32); + + lba_to_msf (image->start_lba, &msf); + current_msf = (to_bcd (msf.cdmsf_frame0) | to_bcd (msf.cdmsf_sec0) << 8 | to_bcd (msf.cdmsf_min0) << 16); + cues_track.current_msf = me2be_32 (current_msf); + + value32 |= 0x0100; + cues_track.unknown = me2be_32 (value32); + + lba_to_msf (image->start_lba + (image->track_length < 0 ? 0 : // pregap = 0 + image->pregap_length), &msf); + current_msf = (to_bcd (msf.cdmsf_frame0) | to_bcd (msf.cdmsf_sec0) << 8 | to_bcd (msf.cdmsf_min0) << 16); + + cues_track.current_msf2 = me2be_32 (current_msf); + + memcpy (&(fcues[*fcues_i]), &cues_track, sizeof (st_cues_track_t)); + *fcues_i += sizeof (st_cues_track_t); +} + + +void +nrg_write_daoi_track (char *fdaoi, int32_t *fdaoi_i, dm_image_t * image, + int32_t offset) +{ + int32_t value32; + st_daoi_track_t daoi_track; + + memset (&daoi_track, 0, sizeof (st_daoi_track_t)); + + daoi_track.sector_size = me2be_32 (image->sector_size); + + value32 = (!image->mode ? 0x07000001 : 0x03000001); + daoi_track.mode = me2be_32 (value32); + + daoi_track.offset = me2be_32 (offset); + + value32 = offset + (image->track_length < 0 ? 0 : // pregap = 0 + image->pregap_length * image->sector_size); + daoi_track.offset2 = me2be_32 (value32); + + value32 = offset + + ((image->pregap_length + image->track_length) * image->sector_size); + daoi_track.offset3 = me2be_32 (value32); + + memcpy (&(fdaoi[*fdaoi_i]), &daoi_track, sizeof (st_daoi_track_t)); + *fdaoi_i += sizeof (st_daoi_track_t); +} + + +void +nrg_write_cues_tail (char *fcues, int32_t *fcues_i, dm_image_t * image) +{ + int32_t value32; + int32_t current_msf; + struct cdrom_msf msf; + st_cues_tail_t cues_tail; + + memset (&cues_tail, 0, sizeof (st_cues_tail_t)); + + value32 = (image->mode != 0 ? 0x41000000 : 0x01000000); + value32 |= 0xAA0100; + cues_tail.track_mode = me2be_32 (value32); + + lba_to_msf (image->start_lba + image->track_length + image->pregap_length, &msf); + current_msf = (to_bcd (msf.cdmsf_frame0) | to_bcd (msf.cdmsf_sec0) << 8 | to_bcd (msf.cdmsf_min0) << 16); + cues_tail.current_msf = me2be_32 (current_msf); + + memcpy (&(fcues[*fcues_i]), &cues_tail, sizeof (st_cues_tail_t)); + *fcues_i += sizeof (st_cues_tail_t); +} + + +void +nrg_write_sinf (char *fdaoi, int32_t *fdaoi_i, dm_image_t * image) +{ + st_sinf_t sinf; + + memset (&sinf, 0, sizeof (st_sinf_t)); + + strcpy (sinf.type, SINF_S); + sinf.unknown = me2be_32 (0x04); + sinf.tracks = me2be_32 (image->tracks); + + memcpy (&(fdaoi[*fdaoi_i]), &sinf, sizeof (st_sinf_t)); + *fdaoi_i += sizeof (st_sinf_t); +} + + +void +nrg_write_etnf (char *fdaoi, int32_t *fdaoi_i, dm_image_t * image) +{ + st_etnf_t etnf; + + memset (&etnf, 0, sizeof (st_etnf_t)); + + strcpy (etnf.type, ETNF_S); + etnf.tracks = me2be_32 (image->tracks * 20); + + memcpy (&(fdaoi[*fdaoi_i]), &etnf, sizeof (st_etnf_t)); + *fdaoi_i += sizeof (st_etnf_t); +} + + +void +nrg_write_etnf_track (char *fdaoi, int32_t *fdaoi_i, dm_image_t * image, + int32_t offset) +{ + int32_t value32; + int32_t sector_size = (image->do_convert ? 2336 : // conversion + image->sector_size); + st_etnf_track_t etnf_track; + + memset (&etnf_track, 0, sizeof (st_etnf_track_t)); + + etnf_track.offset = me2be_32 (offset); + + value32 = image->track_length - (image->do_cut ? 2 : // equals savetrack + 0); + etnf_track.track_len = me2be_32 (value32 * sector_size); + + value32 = (image->mode != 0 ? 0x03 : 0x07); + etnf_track.track_mode = me2be_32 (value32); + + etnf_track.start_lba = me2be_32 (image->start_lba); + etnf_track.pad = 0; + + memcpy (&(fdaoi[*fdaoi_i]), &etnf_track, sizeof (st_etnf_track_t)); + *fdaoi_i += sizeof (st_etnf_track_t); +} +#endif diff --git a/ucon64/2.0/src/libdiscmage/format/nero.h b/ucon64/2.0/src/libdiscmage/format/nero.h new file mode 100644 index 0000000..0352b93 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/format/nero.h @@ -0,0 +1,26 @@ +/* +nero.h - Nero image support for libdiscmage + +Copyright (c) 2003 NoisyB (noisyb@gmx.net) +based on specs and code by Dext + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef NERO_H +#define NERO_H +extern int nrg_init (dm_image_t * image); +extern int nrg_track_init (dm_track_t *track, FILE *fh); +#endif // NERO_H diff --git a/ucon64/2.0/src/libdiscmage/format/other.c b/ucon64/2.0/src/libdiscmage/format/other.c new file mode 100644 index 0000000..a853ab5 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/format/other.c @@ -0,0 +1,183 @@ +/* +other.c - other/proprietary image support for libdiscmage + +Copyright (c) 2003 NoisyB (noisyb@gmx.net) +Gamecube support is based on specs from GC-NFO.COM - [w] / GC-NFO + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif +#include +#include +#include +#include "../misc.h" +#include "../libdiscmage.h" +#include "../libdm_misc.h" +#include "format.h" +#ifdef DJGPP +#include "../dxedll_priv.h" +#endif + +// header magic +#define GC_MAGIC (0x424e5231) +#define GC_MAGIC_S ("BNR1") +//if (0 != (rc = read_raw_frame(cdrom, 16, buf))) { /* Safe PSX signature is at 0x9200 */ +#if 0 +static unsigned char psx_sign[] = { + 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x01, 0x43, 0x44, 0x30, 0x30, 0x31, 0x01, 0x00, + 0x50, 0x4C, 0x41, 0x59, 0x53, 0x54, 0x41, 0x54, + 0x49, 0x4F, 0x4E, 0x20, 0x20, 0x20, 0x20, 0x20 +}; +#endif + +static uint32_t header_start = 0; //, version = 0, position = 0; + +#if 0 +//TODO: replace with dm_sheet_track_init() +int +other_track_init (dm_track_t *track, FILE *fh) +{ + (void) fh; + return 0; +} +#endif + + +static dm_image_t * +dm_other_gc_init (dm_image_t *image) +{ + typedef struct + { + char serial[4]; + char maker[2]; // currently identical to the gameboy advance makers + char pad[102]; + char desc[944]; + char pad2[3]; + char pad3; + char pad4[32]; + } st_boot_bin_t; + + typedef struct + { + char magic[4]; // "BNR1" + char pad[6172]; + char name[32]; + char company[32]; + char name_long[64]; + char company_long[64]; + char desc[128]; + } st_opening_bnr_t; + + st_boot_bin_t boot_bin; + st_opening_bnr_t opening_bnr; + dm_track_t *track = (dm_track_t *) &image->track[0]; + FILE *fh; + + image->sessions = + image->tracks = 1; + image->session[0] = 1; + + image->header_start = header_start; + image->header_len = sizeof (st_opening_bnr_t); + + track->track_start = 0; + track->track_len = + track->total_len = q_fsize (image->fname); + + track->sector_size = 1; + + memset (&boot_bin, 0, sizeof (st_boot_bin_t)); + memset (&opening_bnr, 0, sizeof (st_opening_bnr_t)); + + if (!(fh = fopen (image->fname, "rb"))) + return NULL; + + fread (&boot_bin, sizeof (st_boot_bin_t), 1, fh); + fseek (fh, header_start, SEEK_SET); + fread (&opening_bnr, sizeof (st_opening_bnr_t), 1, fh); + + fclose (fh); + +#ifdef DEBUG + mem_hexdump (&boot_bin, sizeof (st_boot_bin_t), sizeof (st_boot_bin_t)); + mem_hexdump (&opening_bnr, sizeof (st_opening_bnr_t), sizeof (st_opening_bnr_t)); + + printf ("%d\n%d\n\n", sizeof (st_boot_bin_t), sizeof (st_opening_bnr_t)); + fflush (stdout); +#endif + + opening_bnr.name[sizeof (opening_bnr.name) - 1] = + opening_bnr.company[sizeof (opening_bnr.company) - 1] = + opening_bnr.name_long[sizeof (opening_bnr.name_long) - 1] = + opening_bnr.company_long[sizeof (opening_bnr.company_long) - 1] = + opening_bnr.desc[sizeof (opening_bnr.desc) - 1] = 0; + + sprintf (image->misc, "Misc: %s\n %s\n %s\n %s\n %s", + opening_bnr.name, + opening_bnr.company, + opening_bnr.name_long, + opening_bnr.company_long, + opening_bnr.desc); + + return image; +} + + +int +other_init (dm_image_t *image) +{ + typedef struct + { + uint32_t version; + char *version_s; + uint32_t start; + uint32_t len; + char *version_long; + dm_image_t *(*func) (dm_image_t *); + } st_probe_t; + + static const st_probe_t probe[] = { + {GC_MAGIC, GC_MAGIC_S, 0, 0x20000, "proprietary GameCube image", dm_other_gc_init}, + {0, NULL, 0, 0, NULL, NULL} + }; + int x = 0; //, s = 0, t = 0, size = q_fsize (image->fname); +// FILE *fh; +// uint16_t value_16; +// uint32_t value_32; + +// if (!(fh = fopen (image->fname, "rb"))) +// return -1; + + for (x = 0; probe[x].version; x++) + if ((header_start = q_fncmp (image->fname, + probe[x].start, + probe[x].len, + probe[x].version_s, + strlen (probe[x].version_s), + 0))) + { + image->desc = probe[x].version_long; + probe[x].func (image); + break; + } + +// fclose (fh); + + return 0; +} diff --git a/ucon64/2.0/src/libdiscmage/format/other.h b/ucon64/2.0/src/libdiscmage/format/other.h new file mode 100644 index 0000000..81c088e --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/format/other.h @@ -0,0 +1,26 @@ +/* +other.h - other/proprietary image support for libdiscmage + +Copyright (c) 2003 NoisyB (noisyb@gmx.net) +Gamecube support is based on specs from GC-NFO.COM - [w] / GC-NFO + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef OTHER_H +#define OTHER_H +extern int other_init (dm_image_t *image); +//extern int other_track_init (dm_track_t *track, FILE *fh); +#endif // OTHER_H diff --git a/ucon64/2.0/src/libdiscmage/format/toc.c b/ucon64/2.0/src/libdiscmage/format/toc.c new file mode 100644 index 0000000..70524fe --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/format/toc.c @@ -0,0 +1,177 @@ +/* +toc.c - TOC support for libdiscmage + +Copyright (c) 2002 - 2003 NoisyB (noisyb@gmx.net) + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif +#include "../misc.h" +#include "../libdiscmage.h" +#include "../libdm_misc.h" +#include "format.h" +#ifdef DJGPP +#include "../dxedll_priv.h" +#endif + + +const st_track_desc_t toc_desc[] = + { + {DM_MODE1_2048, "MODE1"}, // MODE2_FORM1 + {DM_MODE1_2352, "MODE1_RAW"}, + {DM_MODE2_2336, "MODE2"}, // MODE2_FORM_MIX + {DM_MODE2_2352, "MODE2_RAW"}, + {DM_AUDIO, "AUDIO"}, + {0, NULL} + }; + + +static const char * +toc_get_desc (int id) +{ + int x = 0; + + for (x = 0; toc_desc[x].desc; x++) + if (id == toc_desc[x].id) + return toc_desc[x].desc; + return ""; +} + + +dm_image_t * +dm_toc_read (dm_image_t *image, const char *toc_file) +{ + (void) image; + (void) toc_file; + + return NULL; +} + + +int +dm_toc_write (const dm_image_t *image) +{ + int result = (-1), t = 0; + + for (t = 0; t < image->tracks; t++) + { +#if FILENAME_MAX > MAXBUFSIZE + char buf[FILENAME_MAX]; +#else + char buf[MAXBUFSIZE]; +#endif +// char buf2[MAXBUFSIZE]; + dm_track_t *track = (dm_track_t *) &image->track[t]; +// int m = 0, s = 0, f = 0; + FILE *fh = NULL; + + strcpy (buf, image->fname); +#if 0 + sprintf (buf2, "_%d.TOC", t); + set_suffix (buf, buf2); +#else + set_suffix (buf, ".TOC"); +#endif + + if (!(fh = fopen (buf, "wb"))) + { + result = -1; + continue; + } + else result = 0; + + switch (track->mode) + { + case 0: // audio + fprintf (fh, "AUDIO\n\n"); + break; + + case 1: // mode1 + fprintf (fh, "CD_ROM\n\n"); + break; + + default: // mode2 + fprintf (fh, "CD_ROM_XA\n\n"); + break; + } + + fprintf (fh, "TRACK \"%s\"\n" +// "NO COPY\n" + "DATAFILE \"%s\" %u// length in bytes: %u\n", + toc_get_desc (track->id), + image->fname, + (unsigned int) (track->total_len * track->sector_size), + (unsigned int) (track->total_len * track->sector_size)); + +//TODO: pregap? postgap? + + fclose (fh); + } + + return result; +} + + +int +toc_init (dm_image_t *image) +{ + int t = 0; + FILE *fh = NULL; + char buf[FILENAME_MAX]; + + strcpy (buf, image->fname); + set_suffix (buf, ".TOC"); + if ((dm_toc_read (image, buf))) // read and parse toc into dm_image_t + { + image->desc = "ISO/BIN track (with TOC file)"; + return 0; + } + + // missing or invalid cue? try the image itself + if (!(fh = fopen (image->fname, "rb"))) + return -1; + +#if 1 + image->sessions = + image->tracks = + image->session[0] = 1; +#endif + + for (t = 0; t < image->tracks; t++) + { + dm_track_t *track = (dm_track_t *) &image->track[t]; + + if (!dm_track_init (track, fh)) + { + track->track_len = + track->total_len = q_fsize (image->fname) / track->sector_size; + } + else + { + fclose (fh); + return !t ? (-1) : 0; + } + } + + dm_toc_write (image); // write the missing cue + + image->desc = "ISO/BIN track (missing TOC file created)"; + + fclose (fh); + return 0; +} diff --git a/ucon64/2.0/src/libdiscmage/format/toc.h b/ucon64/2.0/src/libdiscmage/format/toc.h new file mode 100644 index 0000000..245c0d7 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/format/toc.h @@ -0,0 +1,25 @@ +/* +toc.h - TOC support for libdiscmage + +Copyright (c) 2002 - 2003 NoisyB (noisyb@gmx.net) + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef TOC_H +#define TOC_H +extern int dm_mktoc (const dm_image_t *image); +extern int toc_init (dm_image_t *image); +#endif // TOC_H diff --git a/ucon64/2.0/src/libdiscmage/getopt.h b/ucon64/2.0/src/libdiscmage/getopt.h new file mode 100644 index 0000000..596cccc --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/getopt.h @@ -0,0 +1,132 @@ +/* Declarations for getopt. + Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + + extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + + extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + + extern int opterr; + +/* Set to an option character which was unrecognized. */ + + extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + + struct option + { +#if defined (__STDC__) && __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; + }; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if defined (__STDC__) && __STDC__ +#if defined __GNU_LIBRARY__ || defined __cplusplus +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ + extern int getopt (int argc, char *const *argv, const char *shortopts); +#else /* not __GNU_LIBRARY__ */ + extern int getopt (); +#endif /* __GNU_LIBRARY__ */ + extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); + extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ + extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ + extern int getopt (); + extern int getopt_long (); + extern int getopt_long_only (); + + extern int _getopt_internal (); +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ diff --git a/ucon64/2.0/src/libdiscmage/libdiscmage.h b/ucon64/2.0/src/libdiscmage/libdiscmage.h new file mode 100644 index 0000000..52bff2f --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/libdiscmage.h @@ -0,0 +1,202 @@ +/* +libdiscmage.h - libdiscmage + +Copyright (c) 2002 - 2004 NoisyB (noisyb@gmx.net) +Copyright (c) 2002 - 2004 dbjh + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef LIBDISCMAGE_H +#define LIBDISCMAGE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include // FILENAME_MAX + +#if defined __linux__ || defined __FreeBSD__ || defined __OpenBSD__ || \ + defined __solaris__ || defined __MINGW32__ || defined __CYGWIN__ || \ + defined __BEOS__ || defined AMIGA || defined __APPLE__ // Mac OS X actually +// We cannot use config.h (for HAVE_INTTYPES_H), because this header file may be +// installed in a system include directory +#include +#else // __MSDOS__, _WIN32 (VC++) +#ifndef OWN_INTTYPES +#define OWN_INTTYPES // signal that these are defined +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +#ifndef _WIN32 +typedef unsigned long long int uint64_t; +#else +typedef unsigned __int64 uint64_t; +#endif +typedef signed char int8_t; +typedef signed short int int16_t; +typedef signed int int32_t; +#ifndef _WIN32 +typedef signed long long int int64_t; +#else +typedef signed __int64 int64_t; +#endif +#endif // OWN_INTTYPES +#endif + +#define DM_VERSION_MAJOR 0 +#define DM_VERSION_MINOR 0 +#define DM_VERSION_STEP 7 + + +// a CD can have max. 99 tracks; this value might change in the future +#define DM_MAX_TRACKS 99 + +typedef struct +{ +// TODO?: replace those uint32_t with uint64_t or so... + +// some formats use to have the tracks "embedded" (like: cdi, nrg, etc..) +// this is the start offset inside the image + uint32_t track_start; // in bytes + uint32_t track_end; // in bytes + + int16_t pregap_len; // in sectors + uint32_t track_len; // in sectors + uint32_t total_len; // in sectors; pregap_len + track_len == total_len + // (less if the track is truncated) + + int16_t postgap_len;// in sectors + int16_t start_lba; // in sectors + +// start of the iso header inside the track (inside the image) + int32_t iso_header_start; // if -1 then no iso header + + int8_t mode; // 0 == AUDIO, 1 == MODE1, 2 == MODE2 + uint16_t sector_size; // in bytes; includes seek_header + seek_ecc + int16_t seek_header; // in bytes + int16_t seek_ecc; // in bytes + + const char *desc; // deprecated + int id; // DM_AUDIO, DM_MODE1_2048, ... +} dm_track_t; + + +typedef struct +{ + int type; // image type DM_CDI, DM_NRG, DM_TOC, etc. + char *desc; // like type but more verbose + int flags; // DM_FIX, ... + char fname[FILENAME_MAX]; // filename of image + uint32_t version; // version of image (used by CDI and NRG) + + int sessions; // # of sessions + int tracks; // # of tracks + + dm_track_t track[DM_MAX_TRACKS]; // array of dm_track_t + uint8_t session[DM_MAX_TRACKS + 1]; // array of uint32_t with tracks per session + +// some image formats (CDI) use embedded headers instead of TOC or CUE files + int header_start; + int header_len; // if header_len == 0 then no header(!) + + char misc[4096]; // miscellaneous information about proprietary images (other.c) +} dm_image_t; + + +/* + dm_get_version() returns version of libdiscmage as uint32_t + dm_get_version_s() returns version of libdiscmage as string + dm_open() this is the first function to call with the filename of the + image; it will try to recognize the image format, etc. + dm_reopen() like dm_open() but can reuse an existing dm_image_t + dm_close() the last function; close image + dm_fdopen() returns a FILE ptr from the start of a track (in image) +TODO: dm_seek() seek for tracks inside image + dm_fseek (image, 2, SEEK_END); + will seek to the end of the 2nd track in image + dm_set_gauge() enter here the name of a function that takes two integers + gauge (pos, total) + { + printf ("%d of %d done", pos, total); + } + dm_nfo() display dm_image_t + dm_read() read single sector from track (in image) +TODO: dm_write() write single sector to track (in image) + + dm_toc_read() read TOC sheet into dm_image_t (deprecated) + dm_toc_write() write dm_image_t as TOC sheet (deprecated) + dm_cue_read() read CUE sheet into dm_image_t (deprecated) + dm_cue_write() write dm_image_t as CUE sheet (deprecated) + dm_disc_read() deprecated reading or writing images is done + by those scripts in contrib/ (deprecated) + dm_disc_write() deprecated reading or writing images is done + by those scripts in contrib/ (deprecated) +*/ +extern uint32_t dm_get_version (void); +extern const char *dm_get_version_s (void); +extern void dm_set_gauge (void (*gauge) (int, int)); + +extern dm_image_t *dm_open (const char *fname, uint32_t flags); +extern dm_image_t *dm_reopen (const char *fname, uint32_t flags, dm_image_t *image); +extern int dm_close (dm_image_t *image); + +//extern int dm_seek (FILE *fp, int track_num, int how); +extern FILE *dm_fdopen (dm_image_t *image, int track_num, const char *mode); +//#define DM_NFO_ANSI_COLOR 1 +//#define DM_NFO_VERBOSE 2 +extern void dm_nfo (const dm_image_t *image, int verbose, int ansi_color); + +extern int dm_read (char *buffer, int track_num, int sector, const dm_image_t *image); +extern int dm_write (const char *buffer, int track_num, int sector, const dm_image_t *image); + +extern dm_image_t *dm_toc_read (dm_image_t *image, const char *toc_sheet); +extern int dm_toc_write (const dm_image_t *image); + +extern dm_image_t *dm_cue_read (dm_image_t *image, const char *cue_sheet); +extern int dm_cue_write (const dm_image_t *image); + +extern int dm_disc_read (const dm_image_t *image); +extern int dm_disc_write (const dm_image_t *image); + + +/* + dm_rip() convert/rip a track from an image +TODO: DM_FILES rip files from track instead of track + DM_WAV convert possible audio track to wav (instead of raw) + DM_2048 (bin2iso) convert binary sector_size to 2048 bytes + (could result in a malformed output image) +TODO: DM_FIX (isofix) takes an ISO image with PVD pointing + to bad DR offset and add padding data so actual DR + gets located in right absolute address. + Original boot area, PVD, SVD and VDT are copied to + the start of new, fixed ISO image. + Supported input images are: 2048, 2336, + 2352 and 2056 bytes per sector. All of them are + converted to 2048 bytes per sector when writing + excluding 2056 image which is needed by Mac users. +*/ +#define DM_CDMAGE 0 +#define DM_FILES 1 +#define DM_WAV 2 +#define DM_2048 4 +#define DM_FIX 8 +extern int dm_rip (const dm_image_t *image, int track_num, uint32_t flags); + +#ifdef __cplusplus +} +#endif + +#endif // LIBDISCMAGE_H diff --git a/ucon64/2.0/src/libdiscmage/libdm_misc.c b/ucon64/2.0/src/libdiscmage/libdm_misc.c new file mode 100644 index 0000000..f4475f0 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/libdm_misc.c @@ -0,0 +1,900 @@ +/* +libdm_misc.c - libdiscmage miscellaneous + +Copyright (c) 2002 - 2004 NoisyB (noisyb@gmx.net) + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include +#include +#ifdef DEBUG +#ifdef __GNUC__ +#warning DEBUG active +#else +#pragma message ("DEBUG active") +#endif +#endif +#include "misc.h" +#include "libdiscmage.h" +#include "libdm_misc.h" +#include "format/format.h" +#ifdef DJGPP // DXE's are specific to DJGPP +#include "dxedll_priv.h" +#endif +#include "misc_wav.h" + +#ifndef CD_MINS +#define CD_MINS 74 /* max. minutes per CD, not really a limit */ +#define CD_SECS 60 /* seconds per minute */ +#define CD_FRAMES 75 /* frames per second */ +#endif + +#define MBIT (131072) +#define TOMBIT(x) ((int)(x) / MBIT) +#define TOMBIT_F(x) ((float)(x) / MBIT) + + +const char pvd_magic[] = {0x01, 'C', 'D', '0', '0', '1', 0x01, 0}; +const char svd_magic[] = {0x02, 'C', 'D', '0', '0', '1', 0x01, 0}; +const char vdt_magic[] = {(const char) 0xff, 'C', 'D', '0', '0', '1', 0x01, 0}; + + +#define ISODCL(from, to) (to - from + 1) +typedef struct + { + char type[ISODCL (1, 1)]; /* 711 */ + char id[ISODCL (2, 6)]; + char version[ISODCL (7, 7)]; /* 711 */ + char unused1[ISODCL (8, 8)]; + char system_id[ISODCL (9, 40)]; /* achars */ + char volume_id[ISODCL (41, 72)]; /* dchars */ + char unused2[ISODCL (73, 80)]; + char volume_space_size[ISODCL (81, 88)]; /* 733 */ + char unused3[ISODCL (89, 120)]; + char volume_set_size[ISODCL (121, 124)]; /* 723 */ + char volume_sequence_number[ISODCL (125, 128)]; /* 723 */ + char logical_block_size[ISODCL (129, 132)]; /* 723 */ + char path_table_size[ISODCL (133, 140)]; /* 733 */ + char type_l_path_table[ISODCL (141, 144)]; /* 731 */ + char opt_type_l_path_table[ISODCL (145, 148)]; /* 731 */ + char type_m_path_table[ISODCL (149, 152)]; /* 732 */ + char opt_type_m_path_table[ISODCL (153, 156)]; /* 732 */ + char root_directory_record[ISODCL (157, 190)]; /* 9.1 */ + char volume_set_id[ISODCL (191, 318)]; /* dchars */ + char publisher_id[ISODCL (319, 446)]; /* achars */ + char preparer_id[ISODCL (447, 574)]; /* achars */ + char application_id[ISODCL (575, 702)]; /* achars */ + char copyright_file_id[ISODCL (703, 739)]; /* 7.5 dchars */ + char abstract_file_id[ISODCL (740, 776)]; /* 7.5 dchars */ + char bibliographic_file_id[ISODCL (777, 813)]; /* 7.5 dchars */ + char creation_date[ISODCL (814, 830)]; /* 8.4.26.1 */ + char modification_date[ISODCL (831, 847)]; /* 8.4.26.1 */ + char expiration_date[ISODCL (848, 864)]; /* 8.4.26.1 */ + char effective_date[ISODCL (865, 881)]; /* 8.4.26.1 */ + char file_structure_version[ISODCL (882, 882)]; /* 711 */ + char unused4[ISODCL (883, 883)]; + char application_data[ISODCL (884, 1395)]; + char unused5[ISODCL (1396, 2048)]; + } st_iso_header_t; + + +/* + * A CD-ROM physical sector size is 2048, 2052, 2056, 2324, 2332, 2336, + * 2340, or 2352 bytes long. + +* Sector types of the standard CD-ROM data formats: + * + * format sector type user data size (bytes) + * ----------------------------------------------------------------------------- + * 1 (Red Book) CD-DA 2352 (CD_FRAMESIZE_RAW) + * 2 (Yellow Book) Mode1 Form1 2048 (CD_FRAMESIZE) + * 3 (Yellow Book) Mode1 Form2 2336 (CD_FRAMESIZE_RAW0) + * 4 (Green Book) Mode2 Form1 2048 (CD_FRAMESIZE) + * 5 (Green Book) Mode2 Form2 2328 (2324+4 spare bytes) + * + * + * The layout of the standard CD-ROM data formats: + * ----------------------------------------------------------------------------- + * - audio (red): | audio_sample_bytes | + * | 2352 | + * + * - data (yellow, mode1): | sync - head - data - EDC - zero - ECC | + * | 12 - 4 - 2048 - 4 - 8 - 276 | + * + * - data (yellow, mode2): | sync - head - data | + * | 12 - 4 - 2336 | + * + * - XA data (green, mode2 form1): | sync - head - sub - data - EDC - ECC | + * | 12 - 4 - 8 - 2048 - 4 - 276 | + * + * - XA data (green, mode2 form2): | sync - head - sub - data - Spare | + * | 12 - 4 - 8 - 2324 - 4 | + * + */ + +/* + from CDmage + + m1_2048=iso + m1_2352=tao + m2_2336=mm2 + m2_2352=tao + cf_2048=fcd + a1_2352=pcm + a2_2352=wav + cg_2448=cdg + ci_2336=mci + ci_2352=cdi + cv_2048=vcd + +CDRWin cuesheet file (*.cue)|*.cue +M1/2048 track (*.iso)|*.iso +M1/2352 track (*.bin;*.tao;*.iso;*.img;*.bwi)|*.bin;*.tao;*.iso;*.img;*.bwi +M2/2336 track (*.mm2)|*.mm2 +M2/2352 track (*.bin;*.tao;*.iso;*.img;*.bwi)|*.bin;*.tao;*.iso;*.img;*.bwi +VirtualCD uncompressed container file (*.fcd)|*.fcd +Audio track image file (*.pcm;*.bin;*.img;*.bwi)|*.pcm;*.bin;*img;*.bwi +Wave file 44.1KHz 16-bit stereo (*.wav)|*.wav +Virtual Drive uncomp. container file (*.vcd)|*.vcd +Nero Burning Rom image file (*.nrg)|*.nrg +CloneCD image control file (*.ccd)|*.ccd +Raw Image (*.iso;*.bin;*.tao;*.img;*.bwi;*.mm2)|*.iso;*.bin;*.tao;*.img;*.bwi;*.mm2 +BlindWrite TOC file (*.bwt)|*.bwt +DiscJuggler CD image file (*.cdi)|*.cdi +Gear image files (*.rdb;*.md1;*.xa)|*.rdb;*.md1;*.xa +NTI CD image files (*.cdp;*.ncd)|*.cdp;*.ncd +Prassi Global-CD image file (*.gcd)|*.gcd +WinOnCD full CD image file (*.c2d)|*.c2d +Easy CD Creator image file (*.cif)|*.cif +*/ + +const st_track_probe_t track_probe[] = + { + {1, 0, 2048, 0, DM_MODE1_2048, ".iso"}, // MODE2_FORM1 + {1, 16, 2352, 288, DM_MODE1_2352, ".bin.tao.iso.img.bwi"}, + {2, 8, 2336, 280, DM_MODE2_2336, ".mm2"}, // MODE2_FORM_MIX + {2, 24, 2352, 280, DM_MODE2_2352, ".bin.tao.iso.img.bwi"}, +#if 0 + {2, 24, 2324, 4, DM_UNKNOWN, NULL}, // MODE2/2328, MODE2_FORM2 + {2, 0, 2340, 0, DM_UNKNOWN, NULL}, + {2, 0, 2368, 0, DM_UNKNOWN, NULL}, + {2, 0, 2448, 0, DM_UNKNOWN, NULL}, + {2, 0, 2646, 0, DM_UNKNOWN, NULL}, + {2, 0, 2647, 0, DM_UNKNOWN, NULL}, + {2, 0, 2336, 280, DM_UNKNOWN, NULL}, // MODE2/2336, Macintosh + {2, 16, 2352, 280, DM_UNKNOWN, NULL}, // MODE2/2352, Macintosh + {2, 0, 2056, 0, DM_UNKNOWN, NULL}, // MODE2/2056, Macintosh + {0, 0, 0, 0, DM_UNKNOWN, NULL}, + {0, 0, 0, 0, DM_UNKNOWN, NULL}, +#endif + {0, 0, 2352, 0, DM_AUDIO, ".pcm.bin.img.bwi"}, + // TODO: remove this! +// {0, 0, 1, 0, DM_AUDIO, ".pcm.bin.img.bwi"}, + {0, 0, 0, 0, 0, NULL} + }; + +const char *dm_msg[] = { + "ERROR: %s has been deprecated\n", + "ERROR: Unknown/unsupported track mode\n", + "ERROR: The images track mode is already MODE1/2048\n", + "WARNING: This function is still in ALPHA stage. It might not work properly\n", + NULL +}; + +static void (*dm_gauge_ptr) (int pos, int size) = NULL; + + +int +dm_get_track_mode_id (int mode, int sector_size) +{ + int x = 0; + + for (x = 0; track_probe[x].sector_size; x++) + if (track_probe[x].mode == mode && + track_probe[x].sector_size == sector_size) + return track_probe[x].id; + + return 0; // no id +} + + +void +dm_get_track_mode_by_id (int id, int8_t *mode, uint16_t *sector_size) +{ + int x = 0; + + for (x = 0; track_probe[x].sector_size; x++) + if (track_probe[x].id == id) + { + *mode = track_probe[x].mode; + *sector_size = track_probe[x].sector_size; + return; + } +} + + + +int +dm_bcd_to_int (int b) +{ +// return (b & 0x0F) + 10 * (((b) >> 4) & 0x0F); + return (b & 15) + ((b & 240) >> 4) * 10; +} + + +int +dm_int_to_bcd (int i) +{ +#if 0 +#if 1 + return i % 10 | ((i / 10) % 10) << 4; +#else + return ((i / 10) * 16) + (i % 10); +#endif +#else + return ((i / 10) << 4) | (i % 10); +#endif +} + + +int +dm_is_bcd (int b) +{ + return ((b & 15) < 10) && ((b >> 4) < 10); +} + + +int +dm_lba_to_msf (int lba, int *m0, int *s0, int *f0) +#if 1 +{ + static int m, s, f; + + m = s = f = -1; + +#ifdef __follow_redbook__ + if (lba >= -150 && lba < 405000) /* lba <= 404849 */ +#else + if (lba >= -150) +#endif + { + m = (lba + 150) / CD_SECS / CD_FRAMES; + s = (lba + 150 - m * CD_SECS * CD_FRAMES) / CD_FRAMES; + f = (lba + 150 - m * CD_SECS * CD_FRAMES - s * CD_FRAMES); + + } + else if (lba >= -45150 && lba <= -151) + { + m = (lba + 450150) / CD_SECS / CD_FRAMES; + s = (lba + 450150 - m * CD_SECS * CD_FRAMES) / CD_FRAMES; + f = (lba + 450150 - m * CD_SECS * CD_FRAMES - s * CD_FRAMES); + } + + *m0 = m; + *s0 = s; + *f0 = f; + + if (lba > 404849 || *m0 == -1 || *s0 == -1 || *f0 == -1) /* 404850 -> 404999: lead out */ + return FALSE; + return TRUE; +} +#else +{ + static int m, s, f; + + m = s = f = -1; + + if (lba >= -150) + { + m = (lba + 150) / (CD_SECS * CD_FRAMES); + lba -= m * CD_SECS * CD_FRAMES; + s = (lba + 150) / CD_FRAMES; + lba -= s * CD_FRAMES; + f = (lba + 150); + } + else + { + m = (lba + 450150) / (CD_SECS * CD_FRAMES); + lba -= m * CD_SECS * CD_FRAMES; + s = (lba + 450150) / CD_FRAMES; + lba -= s * CD_FRAMES; + f = (lba + 450150); + } + + *m0 = m; + *s0 = s; + *f0 = f; + + return TRUE; +} +#endif + + +int +dm_msf_to_lba (int m, int s, int f, int force_positive) +#if 1 +{ + long ret = (m * CD_SECS + s) +#if 0 + * CD_FRAMES + f; +#else + ; + ret *= CD_FRAMES; + ret += f; +#endif + + if (m < 90 || force_positive) + ret -= 150; + else + ret -= 450150; + + return ret; +} +#else +{ + return m * CD_SECS * CD_FRAMES + s * CD_FRAMES + f; +} +#endif + + +void +dm_clean (dm_image_t *image) +{ + int x = 0; + + memset (image, 0, sizeof (dm_image_t)); + for (x = 0; x < DM_MAX_TRACKS; x++) + image->track[x].iso_header_start = (-1); +} + + +uint32_t +dm_get_version (void) +{ + static const uint32_t dm_version = LIB_VERSION (DM_VERSION_MAJOR, + DM_VERSION_MINOR, + DM_VERSION_STEP); + return dm_version; +} + + +const char * +dm_get_version_s (void) +{ + return "supported: BIN, ISO, CDI"; +} + + +void +dm_set_gauge (void (*gauge) (int, int)) +{ + dm_gauge_ptr = gauge; +} + + +void +dm_gauge (int pos, int size) +{ + if (dm_gauge_ptr) + dm_gauge_ptr (pos, size); +} + + +FILE * +dm_fdopen (dm_image_t *image, int track_num, const char *mode) +{ + dm_track_t *track = (dm_track_t *) &image->track[track_num]; + FILE *fh; + + if (!(fh = fopen (image->fname, mode))) + return NULL; + + if (!fseek (fh, track->track_start, SEEK_SET)) + return fh; + + fclose (fh); + return NULL; +} + + + + +int +dm_rip (const dm_image_t *image, int track_num, uint32_t flags) +{ + dm_track_t *track = (dm_track_t * ) &image->track[track_num]; +#if FILENAME_MAX > MAXBUFSIZE + char buf[FILENAME_MAX], buf2[FILENAME_MAX]; +#else + char buf[MAXBUFSIZE], buf2[MAXBUFSIZE]; +#endif + unsigned int x = 0; + int result = 0, m = 0, s = 0, f = 0; + char *p = NULL; + FILE *fh = NULL, *fh2 = NULL; + + if (flags & DM_FIX || flags & DM_2048) + fprintf (stderr, dm_msg[ALPHA]); + +// set dest. name + strcpy (buf, basename (image->fname)); + p = (char *) get_suffix (buf); + if (p) + buf[strlen (buf) - strlen (p)] = 0; + sprintf (buf2, "%s_%d", buf, track_num + 1); + + switch (track->mode) + { + case 0: + if (flags & DM_WAV) + set_suffix (buf2, ".WAV"); + else + set_suffix (buf2, ".RAW"); + break; + + case 1: + case 2: + default: + if (flags & DM_2048 || track->sector_size == 2048) + set_suffix (buf2, ".ISO"); + else + set_suffix (buf2, ".BIN"); + break; + } + +#if 0 + if (track->total_len < track->track_len + track->pregap_len) + { + fprintf (stderr, "SKIPPING: track seems truncated\n"); + return -1; + } +#endif + +#if 0 +// this is not a problem + if (flags & DM_2048 && track->mode == 1 && track->sector_size == 2048) + { + fprintf (stderr, dm_msg[ALREADY_2048]); + return -1; + } +#endif + + if (track->pregap_len != 150) // 0x96 + fprintf (stderr, "WARNING: track seems to have a non-standard pregap (%d Bytes)\n", + track->pregap_len); + +#if 0 + if (image->pregap && track->mode == 0 && remaining_tracks > 1) // quick hack to save next track pregap (audio tracks only) + track->track_len += pregap_length; // if this isn't last track in current session +// does this mean i shouldn't skip pregrap if it's a audio track? +#endif + +// open source and check + if (!(fh = fopen (image->fname, "rb"))) + return -1; + +// open dest. + if (!(fh2 = fopen (buf2, "wb"))) + { + fclose (fh); + return -1; + } + + if (!track->mode && flags & DM_WAV) + misc_wav_write_header_v3 (fh2, track->track_len * 2352); //TODO: get that 2352 value from somewhere + + fseek (fh, track->track_start, SEEK_SET); // start of track + // skip pregap (always?) + fseek (fh, track->pregap_len * track->sector_size, SEEK_CUR); + + for (x = 0; x < track->track_len; x++) + { + memset (buf, 0, sizeof (buf)); + fread (&buf, 1, track->sector_size, fh); + + if (flags & DM_2048) + result = fwrite (&buf[track->seek_header], 1, 2048, fh2); + else + { + const char sync_data[] = {0, (const char) 0xff, (const char) 0xff, + (const char) 0xff, (const char) 0xff, + (const char) 0xff, (const char) 0xff, + (const char) 0xff, (const char) 0xff, + (const char) 0xff, (const char) 0xff, 0}; +// uint32_t value_32 = 0; + + memset (&buf2, 0, sizeof (buf2)); + result = 0; + result += fwrite (&sync_data, 1, 12, fh2); + + dm_lba_to_msf (150 + x, &m, &s, &f); +#ifdef DEBUG + fprintf (stderr, "%d: %d %d %d\n", track->sector_size, m, s, f); + fflush (stderr); +#endif + + result += fwrite (&buf2, 1, 3, fh2); //TODO: MSF + if (fputc (track->mode, fh2)) + result++; + result += fwrite (&buf2, 1, track->seek_header, fh2); // padding + result += fwrite (&buf, 1, track->sector_size, fh2); + result += fwrite (&buf2, 1, track->seek_ecc, fh2); // padding + } + + if (!result) + { + fprintf (stderr, "ERROR: writing sector %d\n", x); + fclose (fh); + fclose (fh2); + return -1; + } + + if (!(x % 100)) + dm_gauge (x * track->sector_size, track->track_len * track->sector_size); + } + + dm_gauge (x * track->sector_size, track->track_len * track->sector_size); + +// fseek (fh, track->total_len * track->sector_size, SEEK_CUR); + + fclose (fh); + fclose (fh2); + + return 0; +} + + +#if 0 +// TODO: merge into dm_rip +int +dm_isofix (const dm_image_t * image, int start_lba, int track_num) +{ +#define BOOTFILE_S "bootfile.bin" +#define HEADERFILE_S "header.iso" + int32_t size_left, last_pos, i, size; + dm_track_t *track = (dm_track_t *) &image->track[track_num]; + char buf[MAXBUFSIZE], buf2[FILENAME_MAX]; + FILE *dest = NULL, *src = NULL, *boot = NULL, *header = NULL; + int mac = FALSE; + const char sub_header[] = {0, 0, 0x08, 0, 0, 0, 0x08, 0}; + + if (start_lba <= 0) + { + fprintf (stderr, "ERROR: Bad LBA value"); + return -1; + } + + if (!track) + return -1; + + mac = (track->sector_size == 2056 ? TRUE : FALSE); + + if (!(src = fopen (image->fname, "rb"))) + return -1; + + strcpy (buf2, basename (image->fname)); + set_suffix (buf2, ".FIX"); + if (!(dest = fopen (buf2, "wb"))) + { + fclose (src); + return -1; + } + + // Saving boot area to file 'bootfile.bin'... + if (!(boot = fopen (BOOTFILE_S, "wb"))) + { + fclose (src); + + fclose (dest); + remove (buf); + + return -1; + } + + // Saving ISO header to file 'header.iso'... + if (!(header = fopen (HEADERFILE_S, "wb"))) + { + fclose (src); + + fclose (dest); + remove (buf); + + fclose (boot); + remove (BOOTFILE_S); + + return -1; + } + + // save boot area + for (i = 0; i < 16; i++) + { + fseek (src, track->seek_header, SEEK_CUR); + fread (buf, 2048, 1, src); + fseek (src, track->seek_ecc, SEEK_CUR); + + if (mac) + fwrite (sub_header, 8, 1, dest); + fwrite (buf, 2048, 1, dest); + + if (mac) + fwrite (sub_header, 8, 1, boot); + fwrite (buf, 2048, 1, boot); + + if (mac) + fwrite (sub_header, 8, 1, header); + fwrite (buf, 2048, 1, header); + } + fclose (boot); // boot area written + + // seek & copy pvd etc. +// last_pos = ftell (src); // start of pvd + for (last_pos = ftell (src); memcmp (vdt_magic, buf, 8) != 0; last_pos = ftell (src)) + { + fseek (src, track->seek_header, SEEK_CUR); + fread (buf, 2048, 1, src); + fseek (src, track->seek_ecc, SEEK_CUR); + + if (memcmp (pvd_magic, buf, 8) != 0 && + memcmp (svd_magic, buf, 8) != 0 && + memcmp (vdt_magic, buf, 8) != 0) + { + fprintf (stderr, "ERROR: Found unknown Volume Descriptor"); +// which sector? + return -1; + } + + printf ("Found %s at sector %d\n", + !memcmp (pvd_magic, buf, 8) ? "PVD" : + !memcmp (svd_magic, buf, 8) ? "SVD" : + !memcmp (vdt_magic, buf, 8) ? "VDT" : "unknown Volume Descriptor", + last_pos / track->sector_size); + + if (mac) + fwrite (sub_header, 8, 1, dest); + fwrite (buf, 2048, 1, dest); + + if (mac) + fwrite (sub_header, 8, 1, header); + fwrite (buf, 2048, 1, header); + } + + // add padding data to header file + memset (&buf, 0, sizeof (buf)); + size_left = 300 - (last_pos / track->sector_size); + for (i = 0; i < size_left; i++) + { + if (mac == TRUE) + fwrite (sub_header, 8, 1, header); + fwrite (buf, 2048, 1, header); + } + fclose (header); + + // add padding data to iso image + if (last_pos > (int) (start_lba * track->sector_size)) + { + fprintf (stderr, "ERROR: LBA value is too small\n" + " It should be at least %d for current ISO image (probably greater)", + last_pos / track->sector_size); + return -1; + } + + if (start_lba < 11700) + fprintf (stderr, + "WARNING: LBA value should be greater or equal to 11700 for multisession\n" + " images\n"); + + // adding padding data up to start LBA value... + size_left = start_lba - (last_pos / track->sector_size); + memset (&buf, 0, sizeof (buf)); + for (i = 0; i < size_left; i++) + { + if (mac) + fwrite (sub_header, 8, 1, dest); + if (!fwrite (buf, 2048, 1, dest)) + return -1; + } + +// append original iso image + fseek (src, 0L, SEEK_SET); + size = q_fsize (buf2); + size_left = size / track->sector_size; + for (i = 0; i < size_left; i++) + { + fseek (src, track->seek_header, SEEK_CUR); + if (!fread (buf, 2048, 1, src)) + break; + fseek (src, track->seek_ecc, SEEK_CUR); + + if (mac) + fwrite (sub_header, 8, 1, dest); + if (!fwrite (buf, 2048, 1, dest)) + return -1; + } + + fclose (src); + fclose (dest); + + return 0; +} +#endif + + +void +dm_nfo (const dm_image_t *image, int verbose, int ansi_color) +{ +#define COLS 80 + int t = 0, s = 0, x = 0, min = 0, sec = 0, frames = 0, + filesize = q_fsize (image->fname); + char buf[MAXBUFSIZE]; + st_iso_header_t iso_header; + FILE *fh = NULL; + int result = -1; + +#ifndef USE_ANSI_COLOR + (void) ansi_color; // warning remover +#endif + +#if 0 + if (image->console_usage != NULL) + { + strcpy (buf, rominfo->console_usage[0].desc); + puts (to_func (buf, strlen (buf), toprint2)); + +#if 0 + if (image->console_usage[1].desc) + { + strcpy (buf, rominfo->console_usage[1].desc); + printf (" %s\n", to_func (buf, strlen (buf), toprint2)); + } +#endif + } +#endif + + printf ("%d Bytes (%.4f MB)\n\n", filesize, (float) filesize / (1024 * 1024)); + printf ("Type: %s\n", image->desc); + + if (image->misc[0]) + puts (image->misc); + + printf ("Sessions: %d\n", image->sessions); + printf ("Tracks: %d\n", image->tracks); + + if ((COLS / image->tracks) > 1 && image->sessions && image->tracks) + { + printf ("Layout: "); + + for (s = t = 0; s < image->sessions; s++) + { +#ifdef USE_ANSI_COLOR + if (ansi_color) + printf ("\x1b[0m[\x1b[30;41m%2d \x1b[0m", s + 1); +// printf ("\x1b[0m[\x1b[30;41m"); + else +#endif + printf ("[%2d ", s + 1); +// printf ("["); + + for (x = 0; x < image->session[s]; x++, t++) +#ifdef USE_ANSI_COLOR + if (ansi_color) + printf ("\x1b[0m[\x1b[30;42m%2d \x1b[0m]", t + 1); + else +#endif + printf ("[%2d ]", t + 1); + +#ifdef USE_ANSI_COLOR + if (ansi_color) + printf ("\x1b[0m] "); + else +#endif + printf ("] "); + } + + fputc ('\n', stdout); + } + + for (t = 0; t < image->tracks; t++) + { + const dm_track_t *track = (const dm_track_t *) &image->track[t]; + + if (!track) + continue; + + if (!track->mode && track->sector_size == 2352) + strcpy (buf, "AUDIO"); + else + sprintf (buf, "MODE%d/%d", track->mode, track->sector_size); + + printf ("Track: %d %s", t + 1, buf); + + dm_lba_to_msf (track->track_len, &min, &sec, &frames); + printf ("\n %d Sectors, %d:%02d/%02d MSF, %d Bytes (%.4f MB)", + (int) track->total_len, + min, sec, frames, + (int) (track->total_len * track->sector_size), + (float) (track->total_len * track->sector_size) / (1024 * 1024)); + + fputc ('\n', stdout); + + if (verbose) + { + printf (" Pregap: %d, Start Sector: %d, End Sector: %d, Postgap: %d\n", + track->pregap_len, + (int) (track->track_start / track->sector_size), + (int) ((track->track_start / track->sector_size) + track->total_len), + track->postgap_len); + + dm_lba_to_msf (track->track_len, &min, &sec, &frames); + printf (" Total Time: %d:%02d/%02d MSF, File Start Pos: %d, End Pos: %d\n", + min, sec, frames, + (int) track->track_start, + (int) track->track_end); + } + + memset (&iso_header, 0, sizeof (st_iso_header_t)); + result = -1; + + if (track->iso_header_start != -1) + if ((fh = fopen (image->fname, "rb"))) + if (fread (&iso_header, track->iso_header_start, sizeof (st_iso_header_t), fh)) + { + if (verbose) + mem_hexdump (&iso_header, sizeof (st_iso_header_t), track->iso_header_start); + + // name, maker, country and size + // some have a name with control chars in it -> replace control chars + strncpy2 (buf, NULL_TO_EMPTY (iso_header.volume_id), + sizeof (iso_header.volume_id)); + to_func (buf, strlen (buf), toprint2); + if (*strtrim (buf)) + printf (" %s\n", buf); + + strncpy2 (buf, NULL_TO_EMPTY (iso_header.publisher_id), + sizeof (iso_header.publisher_id)); + to_func (buf, strlen (buf), toprint2); + if (*strtrim (buf)) + printf (" %s\n", buf); + + strncpy2 (buf, NULL_TO_EMPTY (iso_header.preparer_id), + sizeof (iso_header.preparer_id)); + to_func (buf, strlen (buf), toprint2); + if (*strtrim (buf)) + printf (" %s\n", buf); +#if 0 + strncpy2 (buf, NULL_TO_EMPTY (iso_header.logical_block_size), + sizeof (iso_header.logical_block_size)); + to_func (buf, strlen (buf), toprint2); + if (*strtrim (buf)) + printf (" %s\n", buf); +#endif + strncpy2 (buf, NULL_TO_EMPTY (iso_header.application_id), + sizeof (iso_header.application_id)); + to_func (buf, strlen (buf), toprint2); + if (*strtrim (buf)) + printf (" %s\n", buf); + } + } +} diff --git a/ucon64/2.0/src/libdiscmage/libdm_misc.h b/ucon64/2.0/src/libdiscmage/libdm_misc.h new file mode 100644 index 0000000..1cf8602 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/libdm_misc.h @@ -0,0 +1,123 @@ +/* +libdm_misc.h - libdiscmage miscellaneous + +Copyright (c) 2002 - 2003 NoisyB (noisyb@gmx.net) +Copyright (c) 2002 - 2004 dbjh + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef LIBDM_MISC_H +#define LIBDM_MISC_H +/* + libdm messages + + usage example: fprintf (stdout, dm_msg[DEPRECATED], filename); +*/ +enum +{ + DEPRECATED = 0, + UNKNOWN_IMAGE, + ALREADY_2048, + ALPHA +}; +extern const char *dm_msg[]; + + +typedef struct +{ + int mode; + int seek_header; // sync, head, sub + int sector_size; // data + int seek_ecc; // EDC, zero, ECC, spare + + int id; + + const char *suffix; +} st_track_probe_t; +extern const st_track_probe_t track_probe[]; + + +typedef struct +{ + int id; + const char *desc; +} st_track_desc_t; // used in toc.c and cue.c + + +enum { + DM_AUDIO = 1, +#if 0 + DM_SIZERAW, + DM_SIZEISO_MODE1, + DM_SIZEISO_MODE2_RAW, + DM_SIZEISO_MODE2_FORM1, + DM_SIZEISO_MODE2_FORM2, + DM_MODE1, + DM_MODE2, +#endif + DM_MODE1_2352, + DM_MODE2_2352, + DM_MODE1_2048, + DM_MODE2_2336 +}; + + +enum { + DM_UNKNOWN = -1, + DM_CUE = 1, + DM_TOC, + DM_CDI, + DM_NRG, +// DM_CCD, + DM_OTHER +}; + + +/* + dm_get_track_desc() returns a string like "MODE1/2352" depending on the + mode and sector_size specified; if cue == FALSE + it will return the string in TOC format +*/ +extern int dm_get_track_mode_id (int mode, int sector_size); +extern void dm_get_track_mode_by_id (int id, int8_t *mode, uint16_t *sector_size); +extern void dm_clean (dm_image_t *image); +extern void dm_gauge (int, int); + +extern const char pvd_magic[]; +extern const char svd_magic[]; +extern const char vdt_magic[]; + + +/* + dm_lba_to_msf() convert LBA to minutes, seconds, frames + dm_msf_to_lba() convert minutes, seconds, frames to LBA + + LBA represents the logical block address for the CD-ROM absolute + address field or for the offset from the beginning of the current track + expressed as a number of logical blocks in a CD-ROM track relative + address field. + MSF represents the physical address written on CD-ROM discs, + expressed as a sector count relative to either the beginning of the + medium or the beginning of the current track. + + dm_bcd_to_int() convert BCD to integer + dm_int_to_bcd() convert integer to BCD +*/ +extern int dm_lba_to_msf (int lba, int *m, int *s, int *f); +extern int dm_msf_to_lba (int m, int s, int f, int force_positive); +extern int dm_bcd_to_int (int b); +extern int dm_int_to_bcd (int i); +#endif // LIBDM_MISC_H diff --git a/ucon64/2.0/src/libdiscmage/map.c b/ucon64/2.0/src/libdiscmage/map.c new file mode 100644 index 0000000..5eea896 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/map.c @@ -0,0 +1,129 @@ +/* +map.c - a map (associative array) implementation + +Copyright (c) 2002 - 2003 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#include +#include +#include +#include "map.h" +#if defined DJGPP && defined DLL +#include "dxedll_priv.h" +#endif + + +st_map_t * +map_create (int n_elements) +{ + st_map_t *map; + int size = sizeof (st_map_t) + n_elements * sizeof (st_map_element_t); + + if ((map = (st_map_t *) malloc (size)) == NULL) + { + fprintf (stderr, "ERROR: Not enough memory for buffer (%d bytes)\n", size); + exit (1); + } + map->data = (st_map_element_t *) (((unsigned char *) map) + sizeof (st_map_t)); + memset (map->data, MAP_FREE_KEY, n_elements * sizeof (st_map_element_t)); + map->size = n_elements; + map->cmp_key = map_cmp_key_def; + return map; +} + + +void +map_copy (st_map_t *dest, st_map_t *src) +{ + memcpy (dest->data, src->data, src->size * sizeof (st_map_element_t)); + dest->cmp_key = src->cmp_key; +} + + +int +map_cmp_key_def (void *key1, void *key2) +{ + return key1 != key2; +} + + +st_map_t * +map_put (st_map_t *map, void *key, void *object) +{ + int n = 0; + + while (n < map->size && map->data[n].key != MAP_FREE_KEY && + map->cmp_key (map->data[n].key, key)) + n++; + + if (n == map->size) // current map is full + { + int new_size = map->size + 20; + st_map_t *map2; + + map2 = map_create (new_size); + map_copy (map2, map); + free (map); + map = map2; + } + + map->data[n].key = key; + map->data[n].object = object; + + return map; +} + + +void * +map_get (st_map_t *map, void *key) +{ + int n = 0; + + while (n < map->size && map->cmp_key (map->data[n].key, key)) + n++; + + if (n == map->size) + return NULL; + + return map->data[n].object; +} + + +void +map_del (st_map_t *map, void *key) +{ + int n = 0; + + while (n < map->size && map->cmp_key (map->data[n].key, key)) + n++; + + if (n < map->size) + map->data[n].key = MAP_FREE_KEY; +} + + +void +map_dump (st_map_t *map) +{ + int n = 0; + + while (n < map->size) + { + printf ("%p -> %p\n", map->data[n].key, map->data[n].object); + n++; + } +} diff --git a/ucon64/2.0/src/libdiscmage/map.h b/ucon64/2.0/src/libdiscmage/map.h new file mode 100644 index 0000000..28b695d --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/map.h @@ -0,0 +1,72 @@ +/* +map.h - a map (associative array) implementation + +Copyright (c) 2002 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef MAP_H +#define MAP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + map_create() create a new map (associative array) + map_copy() copy map src to map dest + dest must be a larger map than src + Note that this function also copies the member cmp_key. + map_put() put object in map under key + Callers should always reset the passed map pointer with the one + this function returns. This is necessary in case the map had to + be resized. + map_get() get object from map stored under key + returns NULL if there is no object with key in map + map_del() remove the object stored under key from map + map_dump() display the current contents of map + + The value MAP_FREE_KEY is reserved as a special key value. Don't use that + value. +*/ +#define MAP_FREE_KEY 0 + +typedef struct st_map_element +{ + void *key; + void *object; +} st_map_element_t; + +typedef struct st_map +{ + st_map_element_t *data; + int size; + int (*cmp_key) (void *key1, void *key2); +} st_map_t; + +extern st_map_t *map_create (int n_elements); +extern void map_copy (st_map_t *dest, st_map_t *src); +extern st_map_t *map_put (st_map_t *map, void *key, void *object); +extern int map_cmp_key_def (void *key1, void *key2); +extern void *map_get (st_map_t *map, void *key); +extern void map_del (st_map_t *map, void *key); +extern void map_dump (st_map_t *map); + +#ifdef __cplusplus +} +#endif + +#endif // MAP_H diff --git a/ucon64/2.0/src/libdiscmage/misc.c b/ucon64/2.0/src/libdiscmage/misc.c new file mode 100644 index 0000000..f28e9d5 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/misc.c @@ -0,0 +1,3181 @@ +/* +misc.c - miscellaneous functions + +Copyright (c) 1999 - 2004 NoisyB +Copyright (c) 2001 - 2004 dbjh +Copyright (c) 2002 - 2004 Jan-Erik Karlsson (Amiga code) + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" // USE_ZLIB +#endif +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include // struct option +#include +#include +#include +#include +#include // va_arg() +#include // for S_IFLNK + +#ifdef __MSDOS__ +#include // delay(), milliseconds +#elif defined __unix__ +#include // usleep(), microseconds +#elif defined __BEOS__ +#include // snooze(), microseconds +// Include OS.h before misc.h, because OS.h includes StorageDefs.h which +// includes param.h which unconditionally defines MIN and MAX. +#elif defined AMIGA +#include +#include +#include +#include +#include // GetKey() +#include +#include +#elif defined _WIN32 +#include // Sleep(), milliseconds +#endif + +#ifdef USE_ZLIB +#include "misc_z.h" +#endif +#include "misc.h" + +#ifdef __CYGWIN__ // under Cygwin (gcc for Windows) we +#define USE_POLL // need poll() for kbhit(). poll() +#include // is available on Linux, not on +#endif // BeOS. DOS already has kbhit() + +#if (defined __unix__ && !defined __MSDOS__) || defined __BEOS__ || \ + defined __APPLE__ // Mac OS X actually +#include +typedef struct termios tty_t; +#endif + + +#ifdef DJGPP +#include // needed for __dpmi_int() by ansi_init() +#ifdef DLL +#include "dxedll_priv.h" +#endif +#endif + +extern int errno; + +typedef struct st_func_node +{ + void (*func) (void); + struct st_func_node *next; +} st_func_node_t; + +static st_func_node_t func_list = { NULL, NULL }; +static int func_list_locked = 0; +static int misc_ansi_color = 0; + +#if (defined __unix__ && !defined __MSDOS__) || defined __BEOS__ || \ + defined __APPLE__ // Mac OS X actually +static void set_tty (tty_t *param); +#endif + + +#ifndef USE_ZLIB +int +q_fsize (const char *filename) +{ + struct stat fstate; + + if (!stat (filename, &fstate)) + return fstate.st_size; + + errno = ENOENT; + return -1; +} +#endif + + +#ifdef DEBUG +static void +getopt2_sanity_check (const st_getopt2_t *option) +{ + int x, y; + + for (x = 0; option[x].name || option[x].help; x++) + if (option[x].name) + for (y = 0; option[y].name || option[y].help; y++) + if (option[y].name) + if (!strcmp (option[x].name, option[y].name)) + if (option[x].val != option[y].val || + option[x].has_arg != option[y].has_arg) + { + fprintf (stderr, "ERROR: getopt2_sanity_check(): found dupe %s%s with different has_arg, or val\n", + option[x].name[1] ? OPTION_LONG_S : OPTION_S, option[x].name); + } +} + + +void +getopt2_parse_usage (const char *usage_output) +// parse usage output into st_getopt2_t array (for development) +{ + int i = 0, count = 0; + char buf[MAXBUFSIZE], *s = NULL, *d = NULL; + FILE *fh = fopen (usage_output, "r"); + + if (!fh) + return; + + while (fgets (buf, MAXBUFSIZE, fh)) + { + st_getopt2_t usage; + int value = 0; + + if (*buf == '\n') + continue; + + memset (&usage, 0, sizeof (st_getopt2_t)); + +#ifdef DEBUG + printf (buf); +#endif + s = d = buf; + d = strstr (s, " " OPTION_S); + if (d && (d - s) < 10) + { + s = (d + strspn (++d, OPTION_S)); + + for (i = 0; s[i] && s[i] != ' '; i++) + if (s[i] == OPTARG) + { + value = 1; + d = strtok (s, OPTARG_S); + break; + } + + if (!value) + d = strtok (s, " "); + + if (d) + usage.name = d; + + if (value) // parse =VALUE + { + d = strtok (NULL, " "); + + if (d) + usage.arg_name = d; + } + } + + + if (usage.name) + { + printf ("{\"%s\", ", usage.name); + + if (usage.arg_name) + printf ("1, \"%s\", ", usage.arg_name); + else + printf ("0, NULL, "); + + printf ("\"%s\", NULL},", strtrim (strtok (NULL, "\n"))); + + } + else + printf ("{NULL, 0, NULL, \"%s\", NULL},", strtrim (strtok (s, "\n"))); + + count++; + if (!(count % 10)) + printf (" // %d", count); + fputc ('\n', stdout); + } +} +#endif // DEBUG + + +#ifdef DEBUG +static char * +string_code (char *d, const char *s) +{ + char *p = d; + + *p = 0; + for (; *s; s++) + switch (*s) + { + case '\n': + strcat (p, "\\n\"\n \""); + break; + + case '\"': + strcat (p, "\\\""); + break; + + default: + p = strchr (p, 0); + *p = *s; + *(++p) = 0; + } + + return d; +} + + +static void +getopt2_usage_code (const st_getopt2_t *usage) +{ + int i = 0; + char buf[MAXBUFSIZE]; + +#ifdef DEBUG + getopt2_sanity_check (usage); +#endif + + for (; usage[i].name || usage[i].help; i++) + { + printf ("{\n %s%s%s, %d, 0, %d, // %d\n %s%s%s, %s%s%s,\n (void *) %d\n},\n", + usage[i].name ? "\"" : "", + usage[i].name ? usage[i].name : "NULL", + usage[i].name ? "\"" : "", + usage[i].has_arg, + usage[i].val, + i, + usage[i].arg_name ? "\"" : "", + usage[i].arg_name ? usage[i].arg_name : "NULL", + usage[i].arg_name ? "\"" : "", + usage[i].help ? "\"" : "", + usage[i].help ? string_code (buf, usage[i].help) : "NULL", + usage[i].help ? "\"" : "", + (int) usage[i].object); + } +} +#endif // DEBUG + + +void +getopt2_usage (const st_getopt2_t *usage) +{ +#ifdef DEBUG + getopt2_usage_code (usage); +#else + int i = 0; + char buf[MAXBUFSIZE]; + + for (i = 0; usage[i].name || usage[i].help; i++) + if (usage[i].help) // hidden options ARE allowed + { + if (usage[i].name) + { + sprintf (buf, "%s%s%s%s%s%s ", + // long or short name? + (usage[i].name[1] ? " " OPTION_LONG_S : " " OPTION_S), + usage[i].name, + usage[i].has_arg == 2 ? "[" : "", // == 2 arg is optional + usage[i].arg_name ? OPTARG_S : "", + usage[i].arg_name ? usage[i].arg_name : "", + usage[i].has_arg == 2 ? "]" : ""); // == 2 arg is optional + + if (strlen (buf) < 16) + { + strcat (buf, " "); + buf[16] = 0; + } + fputs (buf, stdout); + } + + if (usage[i].help) + { + char c, *p = buf, *p2 = NULL; + + strcpy (buf, usage[i].help); + + if (usage[i].name) + for (; (p2 = strchr (p, '\n')); p = p2 + 1) + { + c = p2[1]; + p2[1] = 0; + fputs (p, stdout); + fputs (" ", stdout); + p2[1] = c; + } + + fputs (p, stdout); + fputc ('\n', stdout); + } + } +#endif // DEBUG +} + + +int +getopt2_long (struct option *long_option, const st_getopt2_t *option, int n) +{ + int i = 0, j = 0, x = 0; + +#ifdef DEBUG + getopt2_sanity_check (option); +#endif + + memset (long_option, 0, sizeof (struct option) * n); + + for (; option[i].name || option[i].help; i++) + if (option[i].name) // IS option + { + for (j = 0; j < i; j++) + if (option[j].name) + if (!strcmp (option[i].name, option[j].name)) + break; // no dupes + + if (j == i && x < n) + { +#ifdef _MSC_VER + (char *) +#endif + long_option[x].name = +#ifdef _MSC_VER + (char *) +#endif + option[i].name; + long_option[x].has_arg = option[i].has_arg; + long_option[x].flag = option[i].flag; + long_option[x++].val = option[i].val; + } + } + + return x < n ? x + 1 : 0; +} + + +int +getopt2_short (char *short_option, const st_getopt2_t *option, int n) +{ + int i = 0; + char *p = short_option; + +#ifdef DEBUG + getopt2_sanity_check (option); +#endif + + *p = 0; + for (; option[i].name || option[i].help; i++) + if ((int) strlen (short_option) + 3 < n && option[i].name) // IS option + if (!option[i].name[1]) // IS short + if (!strchr (short_option, option[i].name[0])) // no dupes + { + *p++ = option[i].name[0]; + switch (option[i].has_arg) + { + case 2: + *p++ = ':'; + case 1: // falling through + *p++ = ':'; + case 0: + break; +#ifdef DEBUG + default: + fprintf (stderr, "ERROR: getopt2_short(): unexpected has_arg value (%d)\n", option[i].has_arg); +#endif // DEBUG + } + *p = 0; + } +#ifdef DEBUG + printf ("%s\n", short_option); + fflush (stdout); +#endif + + return (int) strlen (short_option) + 3 < n ? (int) strlen (short_option) : 0; +} + + +const st_getopt2_t * +getopt2_get_index_by_val (const st_getopt2_t *option, int val) +{ + int x = 0; + + for (; option[x].name || option[x].help; x++) + if (option[x].name) // it IS an option + if (option[x].val == val) + return &option[x]; + + return NULL; +} + + +#if defined _WIN32 && defined USE_ANSI_COLOR +int +vprintf2 (const char *format, va_list argptr) +// Cheap hack to get the Visual C++ and MinGW ports support "ANSI colors". +// Cheap, because it only supports the ANSI escape sequences uCON64 uses. +{ +#undef printf +#undef fprintf + int n_chars = 0, n_ctrl = 0, n_print, done = 0; + char output[MAXBUFSIZE], *ptr, *ptr2; + HANDLE stdout_handle; + CONSOLE_SCREEN_BUFFER_INFO info; + WORD org_attr, new_attr = 0; + + n_chars = _vsnprintf (output, MAXBUFSIZE, format, argptr); + if (n_chars == -1) + { + fprintf (stderr, "INTERNAL ERROR: Output buffer in vprintf2() is too small (%d bytes).\n" + " Please send a bug report\n", MAXBUFSIZE); + exit (1); + } + + if ((ptr = strchr (output, 0x1b)) == NULL) + fputs (output, stdout); + else + { + stdout_handle = GetStdHandle (STD_OUTPUT_HANDLE); + GetConsoleScreenBufferInfo (stdout_handle, &info); + org_attr = info.wAttributes; + + if (ptr > output) + { + *ptr = 0; + fputs (output, stdout); + *ptr = 0x1b; + } + while (!done) + { + if (memcmp (ptr, "\x1b[0m", 4) == 0) + { + new_attr = org_attr; + n_ctrl = 4; + } + else if (memcmp (ptr, "\x1b[01;31m", 8) == 0) + { + new_attr = FOREGROUND_INTENSITY | FOREGROUND_RED; + n_ctrl = 8; + } + else if (memcmp (ptr, "\x1b[01;32m", 8) == 0) + { + new_attr = FOREGROUND_INTENSITY | FOREGROUND_GREEN; + n_ctrl = 8; + } + else if (memcmp (ptr, "\x1b[01;33m", 8) == 0) // bright yellow + { + new_attr = FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN; + n_ctrl = 8; + } + else if (memcmp (ptr, "\x1b[31;41m", 8) == 0) + { + new_attr = FOREGROUND_RED | BACKGROUND_RED; + n_ctrl = 8; + } + else if (memcmp (ptr, "\x1b[32;42m", 8) == 0) + { + new_attr = FOREGROUND_GREEN | BACKGROUND_GREEN; + n_ctrl = 8; + } + else if (memcmp (ptr, "\x1b[30;41m", 8) == 0) // 30 = foreground black + { + new_attr = BACKGROUND_RED; + n_ctrl = 8; + } + else if (memcmp (ptr, "\x1b[30;42m", 8) == 0) + { + new_attr = BACKGROUND_GREEN; + n_ctrl = 8; + } + else if (*ptr == 0x1b) + { + new_attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + SetConsoleTextAttribute (stdout_handle, new_attr); + printf ("\n" + "INTERNAL WARNING: vprintf2() encountered an unsupported ANSI escape sequence\n" + " Please send a bug report\n"); + n_ctrl = 0; + } + SetConsoleTextAttribute (stdout_handle, new_attr); + + ptr2 = strchr (ptr + 1, 0x1b); + if (ptr2) + n_print = ptr2 - ptr; + else + { + n_print = strlen (ptr); + done = 1; + } + + ptr[n_print] = 0; + ptr += n_ctrl; + fputs (ptr, stdout); + (ptr - n_ctrl)[n_print] = 0x1b; + ptr = ptr2; + } + } + return n_chars; +#define printf printf2 +#define fprintf fprintf2 +} + + +int +printf2 (const char *format, ...) +{ + va_list argptr; + int n_chars; + + va_start (argptr, format); + n_chars = vprintf2 (format, argptr); + va_end (argptr); + return n_chars; +} + + +int +fprintf2 (FILE *file, const char *format, ...) +{ + va_list argptr; + int n_chars; + + va_start (argptr, format); + if (file != stdout) + n_chars = vfprintf (file, format, argptr); + else + n_chars = vprintf2 (format, argptr); + va_end (argptr); + return n_chars; +} +#endif // defined _WIN32 && defined USE_ANSI_COLOR + + +void +clear_line (void) +/* + This function is used to fix a problem when using the MinGW or Visual C++ + port under Windows 98 (probably Windows 95 too) while ANSI.SYS is not loaded. + If a line contains colors, printed with printf() or fprintf() (actually + printf2() or fprintf2()), it cannot be cleared by printing spaces on the same + line. A solution is using SetConsoleTextAttribute(). + The problem doesn't occur if ANSI.SYS is loaded. It also doesn't occur under + Windows XP, even if ANSI.SYS isn't loaded. + We print 79 spaces (not 80), because under command.com the cursor advances to + the next line if we print something on the 80th column (in 80 column mode). + This doesn't happen under xterm. +*/ +{ +#if !defined _WIN32 || !defined USE_ANSI_COLOR + fputs ("\r \r", stdout); +#else + WORD org_attr; + CONSOLE_SCREEN_BUFFER_INFO info; + HANDLE stdout_handle = GetStdHandle (STD_OUTPUT_HANDLE); + GetConsoleScreenBufferInfo (stdout_handle, &info); + org_attr = info.wAttributes; + SetConsoleTextAttribute (stdout_handle, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + fputs ("\r \r", stdout); + SetConsoleTextAttribute (stdout_handle, org_attr); +#endif +} + + +int +ansi_init (void) +{ + int result = isatty (STDOUT_FILENO); + +#ifdef DJGPP + if (result) + { + // Don't use __MSDOS__, because __dpmi_regs and __dpmi_int are DJGPP specific + __dpmi_regs reg; + + reg.x.ax = 0x1a00; // DOS 4.0+ ANSI.SYS installation check + __dpmi_int (0x2f, ®); + if (reg.h.al != 0xff) // AL == 0xff if installed + result = 0; + } +#endif + + misc_ansi_color = result; + + return result; +} + + +#if 0 // currently not used +char * +ansi_strip (char *str) +{ + int ansi = 0; + char *p = str, *s = str; + + for (; *p; p++) + switch (*p) + { + case '\x1b': // escape + ansi = 1; + break; + + case 'm': + if (ansi) + { + ansi = 0; + break; + } + + default: + if (!ansi) + { + *s = *p; + s++; + } + break; + } + *s = 0; + + return str; +} +#endif + + +int +isfname (int c) +{ + if (isalnum (c)) + return TRUE; + + // characters that are also allowed in filenames + return strchr (".,'+- ()[]!&", c) ? TRUE : FALSE; +} + + +int +isprint2 (int c) +{ + if (isprint (c)) + return TRUE; + + // characters that also work with printf + if (c == '\x1b') + return misc_ansi_color ? TRUE : FALSE; + + return strchr ("\t\n\r", c) ? TRUE : FALSE; +} + + +int +tofname (int c) +{ + return isfname (c) ? c : '_'; +} + + +int +toprint2 (int c) +{ + return isprint2 (c) ? c : '.'; +} + + +int +is_func (char *s, int size, int (*func) (int)) +{ + char *p = s; + + /* + Casting to unsigned char * is necessary to avoid differences between the + different compilers' run-time environments. At least for isprint(). Without + the cast the isprint() of (older versions of) DJGPP, MinGW, Cygwin and + Visual C++ returns nonzero values for ASCII characters > 126. + */ + for (; size >= 0; p++, size--) + if (!func (*(unsigned char *) p)) + return FALSE; + + return TRUE; +} + + +char * +to_func (char *s, int size, int (*func) (int)) +{ + char *p = s; + + for (; size > 0; p++, size--) + *p = func (*p); + + return s; +} + + +char * +strcasestr2 (const char *str, const char *search) +{ + char *p = (char *) str; + int len = strlen (search); + + if (!len) + return p; + + for (; *p; p++) + if (!strnicmp (p, search, len)) + return p; + + return NULL; +} + + +char * +strncpy2 (char *dest, const char *src, size_t size) +{ + if (dest) + { + strncpy (dest, src ? src : "", size); + dest[size] = 0; + } + return dest; +} + + +int +isupper2 (int c) +{ + return isupper (c); +} + + +char * +set_suffix (char *filename, const char *suffix) +{ + char suffix2[FILENAME_MAX], *p, *p2; + + if (!(p = basename2 (filename))) + p = filename; + if ((p2 = strrchr (p, '.'))) + if (p2 != p) // files can start with '.' + *p2 = 0; + + strcpy (suffix2, suffix); + strcat (filename, is_func (p, strlen (p), isupper2) ? strupr (suffix2) : strlwr (suffix2)); + + return filename; +} + + +char * +set_suffix_i (char *filename, const char *suffix) +{ + char *p, *p2; + + if (!(p = basename2 (filename))) + p = filename; + if ((p2 = strrchr (p, '.'))) + if (p2 != p) // files can start with '.' + *p2 = 0; + + strcat (filename, suffix); + + return filename; +} + + +const char * +get_suffix (const char *filename) +// Note that get_suffix() never returns NULL. Other code relies on that! +{ + char *p, *p2; + + if (!(p = basename2 (filename))) + p = (char *) filename; + if (!(p2 = strrchr (p, '.'))) + p2 = ""; + if (p2 == p) + p2 = ""; // files can start with '.'; be + // consistent with set_suffix{_i}() + return p2; +} + + +static int +strtrimr (char *str) +/* + Removes all trailing blanks from a string. + Blanks are defined with isspace (blank, tab, newline, return, formfeed, + vertical tab = 0x09 - 0x0D + 0x20) +*/ +{ + int i, j; + + j = i = strlen (str) - 1; + + while (isspace ((int) str[i]) && (i >= 0)) + str[i--] = 0; + + return j - i; +} + + +static int +strtriml (char *str) +/* + Removes all leading blanks from a string. + Blanks are defined with isspace (blank, tab, newline, return, formfeed, + vertical tab = 0x09 - 0x0D + 0x20) +*/ +{ + int i = 0, j; + + j = strlen (str) - 1; + + while (isspace ((int) str[i]) && (i <= j)) + i++; + + if (0 < i) + strcpy (str, &str[i]); + + return i; +} + + +char * +strtrim (char *str) +/* + Removes all leading and trailing blanks in a string. + Blanks are defined with isspace (blank, tab, newline, return, formfeed, + vertical tab = 0x09 - 0x0D + 0x20) +*/ +{ + strtrimr (str); + strtriml (str); + + return str; +} + + +int +memwcmp (const void *buffer, const void *search, uint32_t searchlen, int wildcard) +{ + uint32_t n; + + for (n = 0; n < searchlen; n++) + if (((uint8_t *) search)[n] != wildcard && + ((uint8_t *) buffer)[n] != ((uint8_t *) search)[n]) + return -1; + + return 0; +} + + +void * +mem_search (const void *buffer, uint32_t buflen, + const void *search, uint32_t searchlen) +{ + int32_t n; + + for (n = 0; n <= (int32_t) (buflen - searchlen); n++) + if (memcmp ((uint8_t *) buffer + n, search, searchlen) == 0) + return (uint8_t *) buffer + n; + + return 0; +} + + +void * +mem_swap_b (void *buffer, uint32_t n) +{ + uint8_t *a = (uint8_t *) buffer, byte; + + for (; n > 1; n -= 2) + { + byte = *a; + *a = *(a + 1); + *(a + 1) = byte; + a += 2; + } + + return buffer; +} + + +void * +mem_swap_w (void *buffer, uint32_t n) +{ + uint16_t *a = (uint16_t *) buffer, word; + + n >>= 1; // # words = # bytes / 2 + for (; n > 1; n -= 2) + { + word = *a; + *a = *(a + 1); + *(a + 1) = word; + a += 2; + } + + return buffer; +} + + +#ifdef DEBUG +static void +mem_hexdump_code (const void *buffer, uint32_t n, int virtual_start) +// hexdump something into C code (for development) +{ + uint32_t pos; + const unsigned char *p = (const unsigned char *) buffer; + + for (pos = 0; pos < n; pos++, p++) + { + printf ("0x%02x, ", *p); + + if (!((pos + 1) & 7)) + fprintf (stdout, "// 0x%x (%d)\n", pos + virtual_start + 1, pos + virtual_start + 1); + } +} +#endif + + +void +mem_hexdump (const void *buffer, uint32_t n, int virtual_start) +// hexdump something +{ +#ifdef DEBUG + mem_hexdump_code (buffer, n, virtual_start); +#else + uint32_t pos; + char buf[17]; + const unsigned char *p = (const unsigned char *) buffer; + + buf[16] = 0; + for (pos = 0; pos < n; pos++, p++) + { + if (!(pos & 15)) + printf ("%08x ", (unsigned int) (pos + virtual_start)); + printf ((pos + 1) & 3 ? "%02x " : "%02x ", *p); + + *(buf + (pos & 15)) = isprint (*p) ? *p : '.'; + if (!((pos + 1) & 15)) + puts (buf); + } + if (pos & 15) + { + *(buf + (pos & 15)) = 0; + puts (buf); + } +#endif +} + + +#if 0 // currently not used +int +mkdir2 (const char *name) +// create a directory and check its permissions +{ + struct stat *st = NULL; + + if (stat (name, st) == -1) + { + if (errno != ENOENT) + { + fprintf (stderr, "stat %s", name); + return -1; + } + if (mkdir (name, 0700) == -1) + { + fprintf (stderr, "mkdir %s", name); + return -1; + } + if (stat (name, st) == -1) + { + fprintf (stderr, "stat %s", name); + return -1; + } + } + + if (!S_ISDIR (st->st_mode)) + { + fprintf (stderr, "%s is not a directory\n", name); + return -1; + } + if (st->st_uid != getuid ()) + { + fprintf (stderr, "%s is not owned by you\n", name); + return -1; + } + if (st->st_mode & 077) + { + fprintf (stderr, "%s must not be accessible by other users\n", name); + return -1; + } + + return 0; +} +#endif + + +char * +basename2 (const char *path) +// basename() clone (differs from Linux's basename()) +{ + char *p1; +#if defined DJGPP || defined __CYGWIN__ + char *p2; +#endif + + if (path == NULL) + return NULL; + +#if defined DJGPP || defined __CYGWIN__ + // Yes, DJGPP, not __MSDOS__, because DJGPP's basename() behaves the same + // Cygwin has no basename() + p1 = strrchr (path, '/'); + p2 = strrchr (path, '\\'); + if (p2 > p1) // use the last separator in path + p1 = p2; +#else + p1 = strrchr (path, FILE_SEPARATOR); +#endif +#if defined DJGPP || defined __CYGWIN__ || defined _WIN32 + if (p1 == NULL) // no slash, perhaps a drive? + p1 = strrchr (path, ':'); +#endif + + return p1 ? p1 + 1 : (char *) path; +} + + +char * +dirname2 (const char *path) +// dirname() clone (differs from Linux's dirname()) +{ + char *p1, *dir; +#if defined DJGPP || defined __CYGWIN__ + char *p2; +#endif + + if (path == NULL) + return NULL; + // real dirname() uses malloc() so we do too + // +2: +1 for string terminator +1 if path is ":" + if ((dir = (char *) malloc (strlen (path) + 2)) == NULL) + return NULL; + + strcpy (dir, path); +#if defined DJGPP || defined __CYGWIN__ + // Yes, DJGPP, not __MSDOS__, because DJGPP's dirname() behaves the same + // Cygwin has no dirname() + p1 = strrchr (dir, '/'); + p2 = strrchr (dir, '\\'); + if (p2 > p1) // use the last separator in path + p1 = p2; +#else + p1 = strrchr (dir, FILE_SEPARATOR); +#endif +#if defined DJGPP || defined __CYGWIN__ || defined _WIN32 + if (p1 == NULL) // no slash, perhaps a drive? + { + if ((p1 = strrchr (dir, ':'))) + { + p1[1] = '.'; + p1 += 2; + } + } +#endif + + while (p1 > dir && // find first of last separators (we have to strip trailing ones) +#if defined DJGPP || defined __CYGWIN__ + ((*(p1 - 1) == '/' && (*p1 == '/' || *p1 == '\\')) + || + (*(p1 - 1) == '\\' && (*p1 == '\\' || *p1 == '/')))) +#else + (*(p1 - 1) == FILE_SEPARATOR && *p1 == FILE_SEPARATOR)) +#endif + p1--; + + if (p1 == dir) + p1++; // don't overwrite single separator (root dir) +#if defined DJGPP || defined __CYGWIN__ || defined _WIN32 + else if (p1 > dir) + if (*(p1 - 1) == ':') + p1++; // we must not overwrite the last separator if +#endif // it was directly preceded by a drive letter + + if (p1) + *p1 = 0; // terminate string (overwrite the separator) + else + { + dir[0] = '.'; + dir[1] = 0; + } + + return dir; +} + + +#ifndef HAVE_REALPATH +#undef realpath +char * +realpath (const char *path, char *full_path) +{ +#if defined __unix__ || defined __BEOS__ || defined __MSDOS__ +/* + Keep the "defined _WIN32"'s in this code in case GetFullPathName() turns out + to have some unexpected problems. This code works for Visual C++, but it + doesn't return the same paths as GetFullPathName() does. Most notably, + GetFullPathName() expands :. to the current directory of + : while this code doesn't. +*/ +#define MAX_READLINKS 32 + char copy_path[FILENAME_MAX], got_path[FILENAME_MAX], *new_path = got_path, + *max_path; +#if defined __MSDOS__ || defined _WIN32 || defined __CYGWIN__ + char c; +#endif +#ifdef S_IFLNK + char link_path[FILENAME_MAX]; + int readlinks = 0; +#endif + int n; + + // Make a copy of the source path since we may need to modify it + n = strlen (path); + if (n >= FILENAME_MAX - 2) + return NULL; + else if (n == 0) + return NULL; + + strcpy (copy_path, path); + path = copy_path; + max_path = copy_path + FILENAME_MAX - 2; +#if defined __MSDOS__ || defined _WIN32 || defined __CYGWIN__ + c = toupper (*path); + if (c >= 'A' && c <= 'Z' && path[1] == ':') + { + *new_path++ = *path++; + *new_path++ = *path++; + if (*path == FILE_SEPARATOR) + *new_path++ = *path++; + } + else +#endif + if (*path != FILE_SEPARATOR) + { + getcwd (new_path, FILENAME_MAX - 1); +#ifdef DJGPP + // DJGPP's getcwd() returns a path with forward slashes + { + int l = strlen (new_path); + for (n = 0; n < l; n++) + if (new_path[n] == '/') + new_path[n] = FILE_SEPARATOR; + } +#endif + new_path += strlen (new_path); + if (*(new_path - 1) != FILE_SEPARATOR) + *new_path++ = FILE_SEPARATOR; + } + else + { + *new_path++ = FILE_SEPARATOR; + path++; + } + + // Expand each (back)slash-separated pathname component + while (*path != 0) + { + // Ignore stray FILE_SEPARATOR + if (*path == FILE_SEPARATOR) + { + path++; + continue; + } + if (*path == '.') + { + // Ignore "." + if (path[1] == 0 || path[1] == FILE_SEPARATOR) + { + path++; + continue; + } + if (path[1] == '.') + { + if (path[2] == 0 || path[2] == FILE_SEPARATOR) + { + path += 2; + // Ignore ".." at root + if (new_path == got_path + 1) + continue; + // Handle ".." by backing up + while (*((--new_path) - 1) != FILE_SEPARATOR) + ; + continue; + } + } + } + // Safely copy the next pathname component + while (*path != 0 && *path != FILE_SEPARATOR) + { + if (path > max_path) + return NULL; + + *new_path++ = *path++; + } +#ifdef S_IFLNK + // Protect against infinite loops + if (readlinks++ > MAX_READLINKS) + return NULL; + + // See if latest pathname component is a symlink + *new_path = 0; + n = readlink (got_path, link_path, FILENAME_MAX - 1); + if (n < 0) + { + // EINVAL means the file exists but isn't a symlink + if (errno != EINVAL +#ifdef __BEOS__ + // Make this function work for a mounted ext2 fs ("/:") + && errno != B_NAME_TOO_LONG +#endif + ) + { + // Make sure it's null terminated + *new_path = 0; + strcpy (full_path, got_path); + return NULL; + } + } + else + { + // Note: readlink() doesn't add the null byte + link_path[n] = 0; + if (*link_path == FILE_SEPARATOR) + // Start over for an absolute symlink + new_path = got_path; + else + // Otherwise back up over this component + while (*(--new_path) != FILE_SEPARATOR) + ; + if (strlen (path) + n >= FILENAME_MAX - 2) + return NULL; + // Insert symlink contents into path + strcat (link_path, path); + strcpy (copy_path, link_path); + path = copy_path; + } +#endif // S_IFLNK + *new_path++ = FILE_SEPARATOR; + } + // Delete trailing slash but don't whomp a lone slash + if (new_path != got_path + 1 && *(new_path - 1) == FILE_SEPARATOR) + { +#if defined __MSDOS__ || defined _WIN32 || defined __CYGWIN__ + if (new_path >= got_path + 3) + { + if (*(new_path - 2) == ':') + { + c = toupper (*(new_path - 3)); + if (!(c >= 'A' && c <= 'Z')) + new_path--; + } + else + new_path--; + } + else + new_path--; +#else + new_path--; +#endif + } + // Make sure it's null terminated + *new_path = 0; + strcpy (full_path, got_path); + + return full_path; +#elif defined _WIN32 + char *p, c; + int n; + + if (GetFullPathName (path, FILENAME_MAX, full_path, &p) == 0) + return NULL; + + c = toupper (full_path[0]); + n = strlen (full_path) - 1; + // Remove trailing separator if full_path is not the root dir of a drive, + // because Visual C++'s run-time system is *really* stupid + if (full_path[n] == FILE_SEPARATOR && + !(c >= 'A' && c <= 'Z' && full_path[1] == ':' && full_path[3] == 0)) // && full_path[2] == FILE_SEPARATOR + full_path[n] = 0; + + return full_path; +#elif defined AMIGA + strcpy (full_path, path); + return full_path; +#endif +} +#endif + + +char * +realpath2 (const char *path, char *full_path) +// enhanced realpath() which returns the absolute path of a file +{ + char path1[FILENAME_MAX]; + const char *path2; + + if (path[0] == '~') + { + if (path[1] == FILE_SEPARATOR +#ifdef __CYGWIN__ + || path[1] == '\\' +#endif + ) + sprintf (path1, "%s"FILE_SEPARATOR_S"%s", getenv2 ("HOME"), &path[2]); + else if (path[1] == 0) + strcpy (path1, getenv2 ("HOME")); + path2 = path1; + } + else + path2 = path; + + return realpath (path2, full_path); +} + + +int +one_file (const char *filename1, const char *filename2) +// returns 1 if filename1 and filename2 refer to one file, 0 if not (or error) +{ +#ifndef _WIN32 + struct stat finfo1, finfo2; + + /* + Not the name, but the combination inode & device identify a file. + Note that stat() doesn't need any access rights except search rights for + the directories in the path to the file. + */ + if (stat (filename1, &finfo1) != 0) + return 0; + if (stat (filename2, &finfo2) != 0) + return 0; + if (finfo1.st_dev == finfo2.st_dev && finfo1.st_ino == finfo2.st_ino) + return 1; + else + return 0; +#else + HANDLE file1, file2; + BY_HANDLE_FILE_INFORMATION finfo1, finfo2; + + file1 = CreateFile (filename1, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (file1 == INVALID_HANDLE_VALUE) + return 0; + file2 = CreateFile (filename2, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (file2 == INVALID_HANDLE_VALUE) + { + CloseHandle (file1); + return 0; + } + GetFileInformationByHandle (file1, &finfo1); + GetFileInformationByHandle (file2, &finfo2); + CloseHandle (file1); + CloseHandle (file2); + if (finfo1.dwVolumeSerialNumber == finfo2.dwVolumeSerialNumber && + (finfo1.nFileIndexHigh << 16 | finfo1.nFileIndexLow) == + (finfo2.nFileIndexHigh << 16 | finfo2.nFileIndexLow)) + return 1; + else + return 0; +#endif +} + + +int +one_filesystem (const char *filename1, const char *filename2) +// returns 1 if filename1 and filename2 reside on one file system, 0 if not +// (or an error occurred) +{ +#ifndef _WIN32 + struct stat finfo1, finfo2; + + if (stat (filename1, &finfo1) != 0) + return 0; + if (stat (filename2, &finfo2) != 0) + return 0; + if (finfo1.st_dev == finfo2.st_dev) + return 1; + else + return 0; +#else + DWORD fattrib1, fattrib2; + char path1[FILENAME_MAX], path2[FILENAME_MAX], *p, d1, d2; + HANDLE file1, file2; + BY_HANDLE_FILE_INFORMATION finfo1, finfo2; + + if ((fattrib1 = GetFileAttributes (filename1)) == (DWORD) -1) + return 0; + if ((fattrib2 = GetFileAttributes (filename2)) == (DWORD) -1) + return 0; + if (fattrib1 & FILE_ATTRIBUTE_DIRECTORY || fattrib2 & FILE_ATTRIBUTE_DIRECTORY) + /* + We special-case directories, because we can't use + FILE_FLAG_BACKUP_SEMANTICS as argument to CreateFile() under + Windows 9x/ME. There seems to be no Win32 function other than + CreateFile() to obtain a handle to a directory. + */ + { + if (GetFullPathName (filename1, FILENAME_MAX, path1, &p) == 0) + return 0; + if (GetFullPathName (filename2, FILENAME_MAX, path2, &p) == 0) + return 0; + d1 = toupper (path1[0]); + d2 = toupper (path2[0]); + if (d1 == d2 && d1 >= 'A' && d1 <= 'Z' && d2 >= 'A' && d2 <= 'Z') + if (strlen (path1) >= 2 && strlen (path2) >= 2) + // We don't handle unique volume names + if (path1[1] == ':' && path2[1] == ':') + return 1; + return 0; + } + + file1 = CreateFile (filename1, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (file1 == INVALID_HANDLE_VALUE) + return 0; + file2 = CreateFile (filename2, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (file2 == INVALID_HANDLE_VALUE) + { + CloseHandle (file1); + return 0; + } + GetFileInformationByHandle (file1, &finfo1); + GetFileInformationByHandle (file2, &finfo2); + CloseHandle (file1); + CloseHandle (file2); + if (finfo1.dwVolumeSerialNumber == finfo2.dwVolumeSerialNumber) + return 1; + else + return 0; +#endif +} + + +int +rename2 (const char *oldname, const char *newname) +{ + int retval; + char *dir1 = dirname2 (oldname), *dir2 = dirname2 (newname); + struct stat fstate; + + // We should use dirname{2}() in case oldname or newname doesn't exist yet + if (one_filesystem (dir1, dir2)) + { + if (access (newname, F_OK) == 0) + { + stat (newname, &fstate); + chmod (newname, fstate.st_mode | S_IWUSR); + remove (newname); // *try* to remove or rename() will fail + } + retval = rename (oldname, newname); + } + else + { + retval = q_rfcpy (oldname, newname); + // don't remove unless the file can be copied + if (retval == 0) + { + stat (oldname, &fstate); + chmod (oldname, fstate.st_mode | S_IWUSR); + remove (oldname); + } + } + + free (dir1); + free (dir2); + return retval; +} + + +int +truncate2 (const char *filename, int new_size) +{ + int size = q_fsize (filename); + struct stat fstate; + + stat (filename, &fstate); + if (chmod (filename, fstate.st_mode | S_IWUSR)) + return -1; + + if (size < new_size) + { + FILE *file; + unsigned char padbuffer[MAXBUFSIZE]; + int n_bytes; + + if ((file = fopen (filename, "ab")) == NULL) + return -1; + + memset (padbuffer, 0, MAXBUFSIZE); + + while (size < new_size) + { + n_bytes = new_size - size > MAXBUFSIZE ? MAXBUFSIZE : new_size - size; + fwrite (padbuffer, 1, n_bytes, file); + size += n_bytes; + } + + fclose (file); + } + else + truncate (filename, new_size); + + return 0; // success +} + + +int +change_mem (char *buf, int bufsize, char *searchstr, int strsize, + char wc, char esc, char *newstr, int newsize, int offset, ...) +// convenience wrapper for change_mem2() +{ + va_list argptr; + int i, n_esc = 0, retval; + st_cm_set_t *sets; + + va_start (argptr, offset); + for (i = 0; i < strsize; i++) + if (searchstr[i] == esc) + n_esc++; + + sets = (st_cm_set_t *) malloc (n_esc * sizeof (st_cm_set_t)); + va_start (argptr, offset); + for (i = 0; i < n_esc; i++) + { + sets[i].data = va_arg (argptr, char *); // get next set of characters + sets[i].size = va_arg (argptr, int); // get set size + } + va_end (argptr); + retval = change_mem2 (buf, bufsize, searchstr, strsize, wc, esc, newstr, + newsize, offset, sets); + free (sets); + return retval; +} + + +int +change_mem2 (char *buf, int bufsize, char *searchstr, int strsize, char wc, + char esc, char *newstr, int newsize, int offset, st_cm_set_t *sets) +/* + Search for all occurrences of string searchstr in buf and replace newsize + bytes in buf by copying string newstr to the end of the found search string + in buf plus offset. + If searchstr contains wildcard characters wc, then n wildcard characters in + searchstr match any n characters in buf. + If searchstr contains escape characters esc, sets must point to an array of + sets. sets must contain as many elements as there are escape characters in + searchstr. searchstr matches for an escape character if one of the characters + in sets[i]->data matches. + Note that searchstr is not necessarily a C string; it may contain one or more + zero bytes as strsize indicates the length. + offset is the relative offset from the last character in searchstring and may + have a negative value. + The return value is the number of times a match was found. + This function was written to patch SNES ROM dumps. It does basically the same + as the old uCON does, with one exception, the line with: + bufpos -= n_wc; + + As stated in the comment, this causes the search to restart at the first + wildcard character of the sequence of wildcards that was most recently + skipped if the current character in buf didn't match the current character + in searchstr. This makes change_mem() behave a bit more intuitive. For + example + char str[] = "f foobar means..."; + change_mem (str, strlen (str), "f**bar", 6, '*', '!', "XXXXXXXX", 8, 2, NULL); + finds and changes "foobar means..." into "foobar XXXXXXXX", while with uCON's + algorithm it would not (but does the job good enough for patching SNES ROMs). + + One example of using sets: + char str[] = "fu-bar is the same as foobar "; + st_cm_set_t sets[] = {{"o-", 2}, {"uo", 2}}; + change_mem (str, strlen (str), "f!!", 3, '*', '!', "fighter", 7, 1, sets); + This changes str into "fu-fighter is the same as foofighter". +*/ +{ + char *set; + int bufpos, strpos = 0, pos_1st_esc = -1, setsize, i, n_wc, n_matches = 0, + setindex = 0; + + for (bufpos = 0; bufpos < bufsize; bufpos++) + { + if (strpos == 0 && searchstr[0] != esc && searchstr[0] != wc) + while (bufpos < bufsize && searchstr[0] != buf[bufpos]) + bufpos++; + + // handle escape character in searchstr + while (searchstr[strpos] == esc && bufpos < bufsize) + { + if (strpos == pos_1st_esc) + setindex = 0; // reset argument pointer + if (pos_1st_esc == -1) + pos_1st_esc = strpos; + + set = sets[setindex].data; // get next set of characters + setsize = sets[setindex].size; // get set size + setindex++; + i = 0; + // see if buf[bufpos] matches with any character in current set + while (i < setsize && buf[bufpos] != set[i]) + i++; + if (i == setsize) + break; // buf[bufpos] didn't match with any char + + if (strpos == strsize - 1) // check if we are at the end of searchstr + { + memcpy (buf + bufpos + offset, newstr, newsize); + n_matches++; + break; + } + + strpos++; + bufpos++; + } + if (searchstr[strpos] == esc) + { + strpos = 0; + continue; + } + + // skip wildcards in searchstr + n_wc = 0; + while (searchstr[strpos] == wc && bufpos < bufsize) + { + if (strpos == strsize - 1) // check if at end of searchstr + { + memcpy (buf + bufpos + offset, newstr, newsize); + n_matches++; + break; + } + + strpos++; + bufpos++; + n_wc++; + } + if (bufpos == bufsize) + break; + if (searchstr[strpos] == wc) + { + strpos = 0; + continue; + } + + if (searchstr[strpos] == esc) + { + bufpos--; // current char has to be checked, but `for' + continue; // increments bufpos + } + + // no escape char, no wildcard -> normal character + if (searchstr[strpos] == buf[bufpos]) + { + if (strpos == strsize - 1) // check if at end of searchstr + { + memcpy (buf + bufpos + offset, newstr, newsize); + n_matches++; + strpos = 0; + } + else + strpos++; + } + else + { + bufpos -= n_wc; // scan the most recent wildcards too if + if (strpos > 0) // the character didn't match + { + bufpos--; // current char has to be checked, but `for' + strpos = 0; // increments bufpos + } + } + } + + return n_matches; +} + + +int +build_cm_patterns (st_cm_pattern_t **patterns, const char *filename, int verbose) +/* + This function goes a bit over the top what memory allocation technique + concerns, but at least it's stable. + Note the important difference between (*patterns)[0].n_sets and + patterns[0]->n_sets (not especially that member). I (dbjh) am too ashamed to + tell how long it took me to finally realise that... +*/ +{ + char src_name[FILENAME_MAX], line[MAXBUFSIZE], buffer[MAXBUFSIZE], + *token, *last, *ptr; + int line_num = 0, n_sets, n_codes = 0, n, currentsize1, requiredsize1, + currentsize2, requiredsize2, currentsize3, requiredsize3; + FILE *srcfile; + + strcpy (src_name, filename); + if (access (src_name, F_OK | R_OK)) + return -1; // NOT an error, it's optional + + if ((srcfile = fopen (src_name, "r")) == NULL) // open in text mode + { + fprintf (stderr, "ERROR: Can't open \"%s\" for reading\n", src_name); + return -1; + } + + *patterns = NULL; + currentsize1 = requiredsize1 = 0; + while (fgets (line, sizeof line, srcfile) != NULL) + { + line_num++; + n_sets = 0; + + ptr = line + strspn (line, "\t "); + if (*ptr == '#' || *ptr == '\n' || *ptr == '\r') + continue; + if ((ptr = strpbrk (line, "\n\r#"))) // text after # is comment + *ptr = 0; + + requiredsize1 += sizeof (st_cm_pattern_t); + if (requiredsize1 > currentsize1) + { + currentsize1 = requiredsize1 + 10 * sizeof (st_cm_pattern_t); + if (!(*patterns = (st_cm_pattern_t *) realloc (*patterns, currentsize1))) + { + fprintf (stderr, "ERROR: Not enough memory for buffer (%d bytes)\n", currentsize1); + return -1; + } + } + + (*patterns)[n_codes].search = NULL; + currentsize2 = 0; + requiredsize2 = 1; // for string terminator + n = 0; + strcpy (buffer, line); + token = strtok (buffer, ":"); + token = strtok (token, " "); +// printf ("token: \"%s\"\n", token); + last = token; + // token is never NULL here (yes, tested with empty files and such) + do + { + requiredsize2++; + if (requiredsize2 > currentsize2) + { + currentsize2 = requiredsize2 + 10; + if (!((*patterns)[n_codes].search = + (char *) realloc ((*patterns)[n_codes].search, currentsize2))) + { + fprintf (stderr, "ERROR: Not enough memory for buffer (%d bytes)\n", currentsize2); + free (*patterns); + *patterns = NULL; + return -1; + } + } + (*patterns)[n_codes].search[n] = (unsigned char) strtol (token, NULL, 16); + n++; + } + while ((token = strtok (NULL, " "))); + (*patterns)[n_codes].search_size = n; // size in bytes + + strcpy (buffer, line); + token = strtok (last, ":"); + token = strtok (NULL, ":"); + token = strtok (token, " "); + last = token; + if (!token) + { + printf ("WARNING: Line %d is invalid, no wildcard value is specified\n", + line_num); + continue; + } + (*patterns)[n_codes].wildcard = (char) strtol (token, NULL, 16); + + strcpy (buffer, line); + token = strtok (last, ":"); + token = strtok (NULL, ":"); + token = strtok (token, " "); + last = token; + if (!token) + { + printf ("WARNING: Line %d is invalid, no escape value is specified\n", + line_num); + continue; + } + (*patterns)[n_codes].escape = (char) strtol (token, NULL, 16); + + strcpy (buffer, line); + token = strtok (last, ":"); + token = strtok (NULL, ":"); + token = strtok (token, " "); + last = token; + if (!token) + { + printf ("WARNING: Line %d is invalid, no replacement is specified\n", line_num); + continue; + } + (*patterns)[n_codes].replace = NULL; + currentsize2 = 0; + requiredsize2 = 1; // for string terminator + n = 0; + do + { + requiredsize2++; + if (requiredsize2 > currentsize2) + { + currentsize2 = requiredsize2 + 10; + if (!((*patterns)[n_codes].replace = + (char *) realloc ((*patterns)[n_codes].replace, currentsize2))) + { + fprintf (stderr, "ERROR: Not enough memory for buffer (%d bytes)\n", currentsize2); + free ((*patterns)[n_codes].search); + free (*patterns); + *patterns = NULL; + return -1; + } + } + (*patterns)[n_codes].replace[n] = (unsigned char) strtol (token, NULL, 16); + n++; + } + while ((token = strtok (NULL, " "))); + (*patterns)[n_codes].replace_size = n; // size in bytes + + strcpy (buffer, line); + token = strtok (last, ":"); + token = strtok (NULL, ":"); + token = strtok (token, " "); + last = token; + if (!token) + { + printf ("WARNING: Line %d is invalid, no offset is specified\n", line_num); + continue; + } + (*patterns)[n_codes].offset = strtol (token, NULL, 10); // yes, offset is decimal + + if (verbose) + { + printf ("\n" + "line: %d\n" + "searchstring: ", + line_num); + for (n = 0; n < (*patterns)[n_codes].search_size; n++) + printf ("%02x ", (unsigned char) (*patterns)[n_codes].search[n]); + printf ("(%d)\n" + "wildcard: %02x\n" + "escape: %02x\n" + "replacement: ", + (*patterns)[n_codes].search_size, + (unsigned char) (*patterns)[n_codes].wildcard, + (unsigned char) (*patterns)[n_codes].escape); + for (n = 0; n < (*patterns)[n_codes].replace_size; n++) + printf ("%02x ", (unsigned char) (*patterns)[n_codes].replace[n]); + printf ("(%d)\n" + "offset: %d\n", + (*patterns)[n_codes].replace_size, + (*patterns)[n_codes].offset); + } + + (*patterns)[n_codes].sets = NULL; + currentsize2 = 0; + requiredsize2 = 1; // for string terminator + strcpy (buffer, line); + token = strtok (last, ":"); + token = strtok (NULL, ":"); + last = token; + while (token) + { + requiredsize2 += sizeof (st_cm_set_t); + if (requiredsize2 > currentsize2) + { + currentsize2 = requiredsize2 + 10 * sizeof (st_cm_set_t); + if (!((*patterns)[n_codes].sets = (st_cm_set_t *) + realloc ((*patterns)[n_codes].sets, currentsize2))) + { + fprintf (stderr, "ERROR: Not enough memory for buffer (%d bytes)\n", currentsize2); + free ((*patterns)[n_codes].replace); + free ((*patterns)[n_codes].search); + free (*patterns); + *patterns = NULL; + return -1; + } + } + + (*patterns)[n_codes].sets[n_sets].data = NULL; + currentsize3 = 0; + requiredsize3 = 1; // for string terminator + n = 0; + token = strtok (token, " "); + do + { + requiredsize3++; + if (requiredsize3 > currentsize3) + { + currentsize3 = requiredsize3 + 10; + if (!((*patterns)[n_codes].sets[n_sets].data = (char *) + realloc ((*patterns)[n_codes].sets[n_sets].data, currentsize3))) + { + fprintf (stderr, "ERROR: Not enough memory for buffer (%d bytes)\n", currentsize3); + free ((*patterns)[n_codes].sets); + free ((*patterns)[n_codes].replace); + free ((*patterns)[n_codes].search); + free (*patterns); + *patterns = NULL; + return -1; + } + } + (*patterns)[n_codes].sets[n_sets].data[n] = + (unsigned char) strtol (token, NULL, 16); + n++; + } + while ((token = strtok (NULL, " "))); + (*patterns)[n_codes].sets[n_sets].size = n; + + if (verbose) + { + printf ("set: "); + for (n = 0; n < (*patterns)[n_codes].sets[n_sets].size; n++) + printf ("%02x ", (unsigned char) (*patterns)[n_codes].sets[n_sets].data[n]); + printf ("(%d)\n", (*patterns)[n_codes].sets[n_sets].size); + } + + strcpy (buffer, line); + token = strtok (last, ":"); + token = strtok (NULL, ":"); + last = token; + + n_sets++; + } + (*patterns)[n_codes].n_sets = n_sets; + + n_codes++; + } + fclose (srcfile); + return n_codes; +} + + +void +cleanup_cm_patterns (st_cm_pattern_t **patterns, int n_patterns) +{ + int n, m; + for (n = 0; n < n_patterns; n++) + { + free ((*patterns)[n].search); + (*patterns)[n].search = NULL; + free ((*patterns)[n].replace); + (*patterns)[n].replace = NULL; + for (m = 0; m < (*patterns)[n].n_sets; m++) + { + free ((*patterns)[n].sets[m].data); + (*patterns)[n].sets[m].data = NULL; + } + free ((*patterns)[n].sets); + (*patterns)[n].sets = NULL; + } + free (*patterns); + *patterns = NULL; +} + + +int +gauge (time_t init_time, int pos, int size) +{ +#define GAUGE_LENGTH ((int64_t) 24) + + int curr, bps, left, p, percentage; + char progress[MAXBUFSIZE]; + + if (pos > size || !size) + return -1; + + if ((curr = time (0) - init_time) == 0) + curr = 1; // `round up' to at least 1 sec (no division + // by zero below) + bps = pos / curr; // # bytes/second (average transfer speed) + left = size - pos; + left /= bps ? bps : 1; + + p = (int) ((GAUGE_LENGTH * pos) / size); + *progress = 0; + strncat (progress, "========================", p); + + if (misc_ansi_color) + { + progress[p] = 0; + if (p < GAUGE_LENGTH) + strcat (progress, "\x1b[31;41m"); + } + + strncat (&progress[p], "------------------------", (int) (GAUGE_LENGTH - p)); + + percentage = (int) ((((int64_t) 100) * pos) / size); + + printf ( + misc_ansi_color ? "\r%10d Bytes [\x1b[32;42m%s\x1b[0m] %d%%, BPS=%d, " : + "\r%10d Bytes [%s] %d%%, BPS=%d, ", pos, progress, percentage, bps); + + if (pos == size) + printf ("TOTAL=%03d:%02d", curr / 60, curr % 60); // DON'T print a newline + else // -> gauge can be cleared + printf ("ETA=%03d:%02d ", left / 60, left % 60); + + fflush (stdout); + + return 0; +} + + +#ifdef __CYGWIN__ +/* + Weird problem with combination Cygwin uCON64 exe and cmd.exe (Bash is ok): + When a string with "e (e with diaeresis, one character) is read from an + environment variable, the character isn't the right character for accessing + the file system. We fix this. + TODO: fix the same problem for other non-ASCII characters (> 127). +*/ +char * +fix_character_set (char *str) +{ + int n, l = strlen (str); + unsigned char *ptr = (unsigned char *) str; + + for (n = 0; n < l; n++) + { + if (ptr[n] == 0x89) // e diaeresis + ptr[n] = 0xeb; + else if (ptr[n] == 0x84) // a diaeresis + ptr[n] = 0xe4; + else if (ptr[n] == 0x8b) // i diaeresis + ptr[n] = 0xef; + else if (ptr[n] == 0x94) // o diaeresis + ptr[n] = 0xf6; + else if (ptr[n] == 0x81) // u diaeresis + ptr[n] = 0xfc; + } + + return str; +} +#endif + + +char * +getenv2 (const char *variable) +/* + getenv() suitable for enviroments w/o HOME, TMP or TEMP variables. + The caller should copy the returned string to it's own memory, because this + function will overwrite that memory on the next call. + Note that this function never returns NULL. +*/ +{ + char *tmp; + static char value[MAXBUFSIZE]; +#if defined __CYGWIN__ || defined __MSDOS__ +/* + Under DOS and Windows the environment variables are not stored in a case + sensitive manner. The run-time systems of DJGPP and Cygwin act as if they are + stored in upper case. Their getenv() however *is* case sensitive. We fix this + by changing all characters of the search string (variable) to upper case. + + Note that under Cygwin's Bash environment variables *are* stored in a case + sensitive manner. +*/ + char tmp2[MAXBUFSIZE]; + + strcpy (tmp2, variable); + variable = strupr (tmp2); // DON'T copy the string into variable +#endif // (variable itself is local) + + *value = 0; + + if ((tmp = getenv (variable)) != NULL) + strcpy (value, tmp); + else + { + if (!strcmp (variable, "HOME")) + { + if ((tmp = getenv ("USERPROFILE")) != NULL) + strcpy (value, tmp); + else if ((tmp = getenv ("HOMEDRIVE")) != NULL) + { + strcpy (value, tmp); + tmp = getenv ("HOMEPATH"); + strcat (value, tmp ? tmp : FILE_SEPARATOR_S); + } + else + /* + Don't just use C:\\ under DOS, the user might not have write access + there (Windows NT DOS-box). Besides, it would make uCON64 behave + differently on DOS than on the other platforms. + Returning the current directory when none of the above environment + variables are set can be seen as a feature. A frontend could execute + uCON64 with an environment without any of the environment variables + set, so that the directory from where uCON64 starts will be used. + */ + { + char c; + getcwd (value, FILENAME_MAX); + c = toupper (*value); + // if current dir is root dir strip problematic ending slash (DJGPP) + if (c >= 'A' && c <= 'Z' && + value[1] == ':' && value[2] == '/' && value[3] == 0) + value[2] = 0; + } + } + + if (!strcmp (variable, "TEMP") || !strcmp (variable, "TMP")) + { +#if defined __MSDOS__ || defined __CYGWIN__ + /* + DJGPP and (yet another) Cygwin quirck + A trailing backslash is used to check for a directory. Normally + DJGPP's run-time system is able to handle forward slashes in paths, + but access() won't differentiate between files and dirs if a + forward slash is used. Cygwin's run-time system seems to handle + paths with forward slashes quite different from paths with + backslashes. This trick seems to work only if a backslash is used. + */ + if (access ("\\tmp\\", R_OK | W_OK) == 0) +#else + // trailing file separator to force it to be a directory + if (access (FILE_SEPARATOR_S"tmp"FILE_SEPARATOR_S, R_OK | W_OK) == 0) +#endif + strcpy (value, FILE_SEPARATOR_S"tmp"); + else + getcwd (value, FILENAME_MAX); + } + } + +#ifdef __CYGWIN__ + /* + Under certain circumstances Cygwin's run-time system returns "/" as value + of HOME while that var has not been set. To specify a root dir a path like + /cygdrive/ or simply a drive letter should be used. + */ + if (!strcmp (variable, "HOME") && !strcmp (value, "/")) + getcwd (value, FILENAME_MAX); + + return fix_character_set (value); +#else + return value; +#endif +} + + +char * +get_property (const char *filename, const char *propname, char *buffer, + const char *def) +{ + char line[MAXBUFSIZE], *p = NULL; + FILE *fh; + int prop_found = 0, i, whitespace_len; + + if ((fh = fopen (filename, "r")) != 0) // opening the file in text mode + { // avoids trouble under DOS + while (fgets (line, sizeof line, fh) != NULL) + { + whitespace_len = strspn (line, "\t "); + p = line + whitespace_len; // ignore leading whitespace + if (*p == '#' || *p == '\n' || *p == '\r') + continue; // text after # is comment + if ((p = strpbrk (line, "#\r\n"))) // strip *any* returns + *p = 0; + + p = strchr (line, PROPERTY_SEPARATOR); + // if no divider was found the propname must be a bool config entry + // (present or not present) + if (p) + *p = 0; // note that this "cuts" _line_ + // strip trailing whitespace from property name part of line + for (i = strlen (line) - 1; + i >= 0 && (line[i] == '\t' || line[i] == ' '); + i--) + ; + line[i + 1] = 0; + + if (!stricmp (line + whitespace_len, propname)) + { + if (p) + { + p++; + // strip leading whitespace from value + strcpy (buffer, p + strspn (p, "\t ")); + // strip trailing whitespace from value + for (i = strlen (buffer) - 1; + i >= 0 && (buffer[i] == '\t' || buffer[i] == ' '); + i--) + ; + buffer[i + 1] = 0; + } + prop_found = 1; + break; // an environment variable + } // might override this + } + fclose (fh); + } + + p = getenv2 (propname); + if (*p == 0) // getenv2() never returns NULL + { + if (!prop_found) + { + if (def) + strcpy (buffer, def); + else + buffer = NULL; // buffer won't be changed + } // after this func (=ok) + } + else + strcpy (buffer, p); + return buffer; +} + + +int32_t +get_property_int (const char *filename, const char *propname) +{ + char buf[160]; // 159 is enough for a *very* large number + int32_t value = 0; + + get_property (filename, propname, buf, NULL); + + if (buf[0]) + switch (tolower (buf[0])) + { + case '0': // 0 + case 'n': // [Nn]o + return 0; + } + + value = strtol (buf, NULL, 10); + return value ? value : 1; // if buf was only text like 'Yes' +} // we'll return at least 1 + + +char * +get_property_fname (const char *filename, const char *propname, char *buffer, + const char *def) +// get a filename from file with name filename, expand it and fix characters +{ + char tmp[FILENAME_MAX]; + + get_property (filename, propname, tmp, def); +#ifdef __CYGWIN__ + fix_character_set (tmp); +#endif + return realpath2 (tmp, buffer); +} + + +int +set_property (const char *filename, const char *propname, const char *value, + const char *comment) +{ + int found = 0, result = 0, file_size = 0, i; + char line[MAXBUFSIZE], line2[MAXBUFSIZE], *str = NULL, *p = NULL; + FILE *fh; + struct stat fstate; + + if (stat (filename, &fstate) != 0) + file_size = fstate.st_size; + + if (!(str = (char *) malloc ((file_size + MAXBUFSIZE) * sizeof (char)))) + { + errno = ENOMEM; + return -1; + } + *str = 0; + + if ((fh = fopen (filename, "r")) != 0) // opening the file in text mode + { // avoids trouble under DOS + while (fgets (line, sizeof line, fh) != NULL) + { + strcpy (line2, line); + if ((p = strpbrk (line2, PROPERTY_SEPARATOR_S "#\r\n"))) + *p = 0; // note that this "cuts" _line2_ + for (i = strlen (line2) - 1; + i >= 0 && (line2[i] == '\t' || line2[i] == ' '); + i--) + ; + line2[i + 1] = 0; + + if (!stricmp (line2 + strspn (line2, "\t "), propname)) + { + found = 1; + if (value == NULL) + continue; + + sprintf (line, "%s" PROPERTY_SEPARATOR_S "%s\n", propname, value); + } + strcat (str, line); + } + fclose (fh); + } + + if (!found && value) + { + if (comment) + { + strcat (str, PROPERTY_COMMENT_S "\n" PROPERTY_COMMENT_S " "); + + for (p = strchr (str, 0); *comment; comment++) + switch (*comment) + { + case '\r': + break; + case '\n': + strcat (str, "\n" PROPERTY_COMMENT_S " "); + break; + + default: + p = strchr (str, 0); + *p = *comment; + *(++p) = 0; + break; + } + + strcat (str, "\n" PROPERTY_COMMENT_S "\n"); + } + + sprintf (line, "%s" PROPERTY_SEPARATOR_S "%s\n", propname, value); + strcat (str, line); + } + + if ((fh = fopen (filename, "w")) == NULL) // open in text mode + return -1; + result = fwrite (str, 1, strlen (str), fh); + fclose (fh); + + return result; +} + + +char * +tmpnam2 (char *temp) +// tmpnam() clone +{ + char *p = getenv2 ("TEMP"); + static time_t init = 0; + + if (!init) + { + init = time (0); + srand (init); + } + + *temp = 0; + while (!(*temp) || !access (temp, F_OK)) // must work for files AND dirs + sprintf (temp, "%s%s%08x.tmp", p, FILE_SEPARATOR_S, rand()); + + return temp; +} + + +#if (defined __unix__ && !defined __MSDOS__) || defined __BEOS__ || \ + defined __APPLE__ // Mac OS X actually +static int oldtty_set = 0, stdin_tty = 1; // 1 => stdin is a tty, 0 => it's not +static tty_t oldtty, newtty; + + +void +set_tty (tty_t *param) +{ + if (stdin_tty && tcsetattr (STDIN_FILENO, TCSANOW, param) == -1) + { + fprintf (stderr, "ERROR: Could not set tty parameters\n"); + exit (100); + } +} + + +/* + This code compiles with DJGPP, but is not neccesary. Our kbhit() conflicts + with DJGPP's one, so it won't be used for that function. Perhaps it works + for making getchar() behave like getch(), but that's a bit pointless. +*/ +void +init_conio (void) +{ + if (!isatty (STDIN_FILENO)) + { + stdin_tty = 0; + return; // rest is nonsense if not a tty + } + + if (tcgetattr (STDIN_FILENO, &oldtty) == -1) + { + fprintf (stderr, "ERROR: Could not get tty parameters\n"); + exit (101); + } + oldtty_set = 1; + + if (register_func (deinit_conio) == -1) + { + fprintf (stderr, "ERROR: Could not register function with register_func()\n"); + exit (102); + } + + newtty = oldtty; + newtty.c_lflag &= ~(ICANON | ECHO); + newtty.c_lflag |= ISIG; + newtty.c_cc[VMIN] = 1; // if VMIN != 0, read calls + newtty.c_cc[VTIME] = 0; // block (wait for input) + + set_tty (&newtty); +} + + +void +deinit_conio (void) +{ + if (oldtty_set) + { + tcsetattr (STDIN_FILENO, TCSAFLUSH, &oldtty); + oldtty_set = 0; + } +} + + +#if defined __CYGWIN__ && !defined USE_POLL +#warning kbhit() does not work properly in Cygwin executable if USE_POLL is not defined +#endif +// this kbhit() conflicts with DJGPP's one +int +kbhit (void) +{ +#ifdef USE_POLL + struct pollfd fd; + + fd.fd = STDIN_FILENO; + fd.events = POLLIN; + fd.revents = 0; + + return poll (&fd, 1, 0) > 0; +#else + tty_t tmptty = newtty; + int ch, key_pressed; + + tmptty.c_cc[VMIN] = 0; // doesn't work as expected under + set_tty (&tmptty); // Cygwin (define USE_POLL) + + if ((ch = fgetc (stdin)) != EOF) + { + key_pressed = 1; + ungetc (ch, stdin); + } + else + key_pressed = 0; + + set_tty (&newtty); + + return key_pressed; +#endif +} +#elif defined AMIGA // (__unix__ && !__MSDOS__) || +int // __BEOS__ ||__APPLE__ +kbhit (void) +{ + return GetKey () != 0xff ? 1 : 0; +} + + +int +getch (void) +{ + BPTR con_fileh; + int temp; + + con_fileh = Input (); + // put the console into RAW mode which makes getchar() behave like getch()? + if (con_fileh) + SetMode (con_fileh, 1); + temp = getchar (); + // put the console out of RAW mode (might make sense) + if (con_fileh) + SetMode (con_fileh, 0); + + return temp; +} +#endif // AMIGA + + +#if defined __unix__ && !defined __MSDOS__ +int +drop_privileges (void) +{ + uid_t uid; + gid_t gid; + + uid = getuid (); + if (setuid (uid) == -1) + { + fprintf (stderr, "ERROR: Could not set uid\n"); + return 1; + } + gid = getgid (); // This shouldn't be necessary + if (setgid (gid) == -1) // if `make install' was + { // used, but just in case + fprintf (stderr, "ERROR: Could not set gid\n"); // (root did `chmod +s') + return 1; + } + + return 0; +} +#endif + + +int +register_func (void (*func) (void)) +{ + st_func_node_t *func_node = &func_list, *new_node; + + while (func_node->next != NULL) + func_node = func_node->next; + + if ((new_node = (st_func_node_t *) malloc (sizeof (st_func_node_t))) == NULL) + return -1; + + new_node->func = func; + new_node->next = NULL; + func_node->next = new_node; + return 0; +} + + +int +unregister_func (void (*func) (void)) +{ + st_func_node_t *func_node = &func_list, *prev_node = &func_list; + + while (func_node->next != NULL && func_node->func != func) + { + prev_node = func_node; + func_node = func_node->next; + } + if (func_node->func != func) + return -1; + + if (!func_list_locked) + { + prev_node->next = func_node->next; + free (func_node); + return 0; + } + else + return -1; +} + + +void +handle_registered_funcs (void) +{ + st_func_node_t *func_node = &func_list; + + func_list_locked = 1; + while (func_node->next != NULL) + { + func_node = func_node->next; // first node contains no valid address + if (func_node->func != NULL) + func_node->func (); + } + func_list_locked = 0; +} + + +#ifndef HAVE_BYTESWAP_H +uint16_t +bswap_16 (uint16_t x) +{ +#if 1 + uint8_t *ptr = (uint8_t *) &x, tmp; + tmp = ptr[0]; + ptr[0] = ptr[1]; + ptr[1] = tmp; + return x; +#else + return (((x) & 0x00ff) << 8 | ((x) & 0xff00) >> 8); +#endif +} + + +uint32_t +bswap_32 (uint32_t x) +{ +#if 1 + uint8_t *ptr = (uint8_t *) &x, tmp; + tmp = ptr[0]; + ptr[0] = ptr[3]; + ptr[3] = tmp; + tmp = ptr[1]; + ptr[1] = ptr[2]; + ptr[2] = tmp; + return x; +#else + return ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | + (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)); +#endif +} + + +uint64_t +bswap_64 (uint64_t x) +{ + uint8_t *ptr = (uint8_t *) &x, tmp; + tmp = ptr[0]; + ptr[0] = ptr[7]; + ptr[7] = tmp; + tmp = ptr[1]; + ptr[1] = ptr[6]; + ptr[6] = tmp; + tmp = ptr[2]; + ptr[2] = ptr[5]; + ptr[5] = tmp; + tmp = ptr[3]; + ptr[3] = ptr[4]; + ptr[4] = tmp; + return x; +} +#endif // #ifndef HAVE_BYTESWAP_H + + +void +wait2 (int nmillis) +{ +#ifdef __MSDOS__ + delay (nmillis); +#elif defined __unix__ || defined __APPLE__ // Mac OS X actually + usleep (nmillis * 1000); +#elif defined __BEOS__ + snooze (nmillis * 1000); +#elif defined AMIGA + Delay (nmillis * 1000); +#elif defined _WIN32 + Sleep (nmillis); +#else +#ifdef __GNUC__ +#warning Please provide a wait2() implementation +#else +#pragma message ("Please provide a wait2() implementation") +#endif + volatile int n; + for (n = 0; n < nmillis * 65536; n++) + ; +#endif +} + + +char * +q_fbackup (const char *filename, int mode) +{ + static char buf[FILENAME_MAX]; + + if (access (filename, R_OK) != 0) + return (char *) filename; + + strcpy (buf, filename); + set_suffix (buf, ".BAK"); + if (strcmp (filename, buf) != 0) + { + remove (buf); // *try* to remove or rename() will fail + if (rename (filename, buf)) // keep file attributes like date, etc. + { + fprintf (stderr, "ERROR: Can't rename \"%s\" to \"%s\"\n", filename, buf); + exit (1); + } + } + else // handle the case where filename has the suffix ".BAK". + { + char *dir = dirname2 (filename), buf2[FILENAME_MAX]; + + if (dir == NULL) + { + fprintf (stderr, "INTERNAL ERROR: dirname2() returned NULL\n"); + exit (1); + } + strcpy (buf, dir); + if (buf[0] != 0) + if (buf[strlen (buf) - 1] != FILE_SEPARATOR) + strcat (buf, FILE_SEPARATOR_S); + + strcat (buf, basename2 (tmpnam2 (buf2))); + if (rename (filename, buf)) + { + fprintf (stderr, "ERROR: Can't rename \"%s\" to \"%s\"\n", filename, buf); + exit (1); + } + free (dir); + } + + switch (mode) + { + case BAK_MOVE: + return buf; + + case BAK_DUPE: + default: + if (q_fcpy (buf, 0, q_fsize (buf), filename, "wb")) + { + fprintf (stderr, "ERROR: Can't open \"%s\" for writing\n", filename); + exit (1); + } + sync (); + return buf; + } +} + + +int +q_fcpy (const char *src, int start, int len, const char *dest, const char *mode) +{ + int seg_len; + char buf[MAXBUFSIZE]; + FILE *fh, *fh2; + + if (one_file (dest, src)) // other code depends on this + return -1; // behaviour! + + if (!(fh = fopen (src, "rb"))) + { + errno = ENOENT; + return -1; + } + if (!(fh2 = fopen (dest, mode))) + { + errno = ENOENT; + fclose (fh); + return -1; + } + + fseek (fh, start, SEEK_SET); + fseek (fh2, 0, SEEK_END); + + for (; len > 0; len -= seg_len) + { + if (!(seg_len = fread (buf, 1, MIN (len, MAXBUFSIZE), fh))) + break; + fwrite (buf, 1, seg_len, fh2); + } + + fclose (fh); + fclose (fh2); + sync (); + return 0; +} + + +int +q_rfcpy (const char *src, const char *dest) +// Raw file copy function. Raw, because it will copy the file data as it is, +// unlike q_fcpy() +{ +#ifdef USE_ZLIB +#undef fopen +#undef fread +#undef fwrite +#undef fclose +#endif + FILE *fh, *fh2; + int seg_len; + char buf[MAXBUFSIZE]; + + if (one_file (dest, src)) + return -1; + + if (!(fh = fopen (src, "rb"))) + return -1; + if (!(fh2 = fopen (dest, "wb"))) + { + fclose (fh); + return -1; + } + while ((seg_len = fread (buf, 1, MAXBUFSIZE, fh))) + fwrite (buf, 1, seg_len, fh2); + + fclose (fh); + fclose (fh2); + return 0; +#ifdef USE_ZLIB +#define fopen fopen2 +#define fread fread2 +#define fwrite fwrite2 +#define fclose fclose2 +#endif +} + + +int +q_fswap (const char *filename, int start, int len, swap_t type) +{ + int seg_len; + FILE *fh; + char buf[MAXBUFSIZE]; + struct stat fstate; + + // First (try to) change the file mode or we won't be able to write to it if + // it's a read-only file. + stat (filename, &fstate); + if (chmod (filename, fstate.st_mode | S_IWUSR)) + { + errno = EACCES; + return -1; + } + + if (!(fh = fopen (filename, "r+b"))) + { + errno = ENOENT; + return -1; + } + + fseek (fh, start, SEEK_SET); + + for (; len > 0; len -= seg_len) + { + if (!(seg_len = fread (buf, 1, MIN (len, MAXBUFSIZE), fh))) + break; + if (type == SWAP_BYTE) + mem_swap_b (buf, seg_len); + else // SWAP_WORD + mem_swap_w (buf, seg_len); + fseek (fh, -seg_len, SEEK_CUR); + fwrite (buf, 1, seg_len, fh); + /* + This appears to be a bug in DJGPP and Solaris. Without an extra call to + fseek() a part of the file won't be swapped (DJGPP: after 8 MB, Solaris: + after 12 MB). + */ + fseek (fh, 0, SEEK_CUR); + } + + fclose (fh); + sync (); + return 0; +} + + +int +q_fncmp (const char *filename, int start, int len, const char *search, + int searchlen, int wildcard) +{ +#define BUFSIZE 8192 + char buf[BUFSIZE]; + FILE *fh; + int seglen, maxsearchlen, searchpos, filepos = 0, matchlen = 0; + + if (!(fh = fopen (filename, "rb"))) + { + errno = ENOENT; + return -1; + } + fseek (fh, start, SEEK_SET); + filepos = start; + + while ((seglen = fread (buf, 1, BUFSIZE + filepos > start + len ? + start + len - filepos : BUFSIZE, fh))) + { + maxsearchlen = searchlen - matchlen; + for (searchpos = 0; searchpos <= seglen; searchpos++) + { + if (searchpos + maxsearchlen >= seglen) + maxsearchlen = seglen - searchpos; + if (!memwcmp (buf + searchpos, search + matchlen, maxsearchlen, wildcard)) + { + if (matchlen + maxsearchlen < searchlen) + { + matchlen += maxsearchlen; + break; + } + else + { + fclose (fh); + return filepos + searchpos - matchlen; + } + } + else + matchlen = 0; + } + filepos += seglen; + } + + fclose (fh); + return -1; +} + + +int +quick_io (void *buffer, size_t start, size_t len, const char *filename, + const char *mode) +{ + int result; + FILE *fh; + + if ((fh = fopen (filename, (const char *) mode)) == NULL) + { +#ifdef DEBUG + fprintf (stderr, "ERROR: Could not open \"%s\" in mode \"%s\"\n" + "CAUSE: %s\n", filename, mode, strerror (errno)); +#endif + return -1; + } + +#ifdef DEBUG + fprintf (stderr, "\"%s\": \"%s\"\n", filename, (char *) mode); +#endif + + fseek (fh, start, SEEK_SET); + + // Note the order of arguments of fread() and fwrite(). Now quick_io() + // returns the number of characters read or written. Some code relies on + // this behaviour! + if (*mode == 'r' && mode[1] != '+') // "r+b" always writes + result = (int) fread (buffer, 1, len, fh); + else + result = (int) fwrite (buffer, 1, len, fh); + + fclose (fh); + return result; +} + + +int +quick_io_c (int value, size_t start, const char *filename, const char *mode) +{ + int result; + FILE *fh; + + if ((fh = fopen (filename, (const char *) mode)) == NULL) + { +#ifdef DEBUG + fprintf (stderr, "ERROR: Could not open \"%s\" in mode \"%s\"\n" + "CAUSE: %s\n", filename, mode, strerror (errno)); +#endif + return -1; + } + +#ifdef DEBUG + fprintf (stderr, "\"%s\": \"%s\"\n", filename, (char *) mode); +#endif + + fseek (fh, start, SEEK_SET); + + if (*mode == 'r' && mode[1] != '+') // "r+b" always writes + result = fgetc (fh); + else + result = fputc (value, fh); + + fclose (fh); + return result; +} + + +#if 0 +int +process_file (const char *src, int start, int len, const char *dest, const char *mode, int (*func) (char *, int)) +{ + int seg_len; + char buf[MAXBUFSIZE]; + FILE *fh, *fh2; + + if (one_file (dest, src)) + return -1; + + if (!(fh = fopen (src, "rb"))) + { + errno = ENOENT; + return -1; + } + if (!(fh2 = fopen (dest, mode))) + { + errno = ENOENT; + fclose (fh); + return -1; + } + + fseek (fh, start, SEEK_SET); + fseek (fh2, 0, SEEK_END); + + for (; len > 0; len -= seg_len) + { + if (!(seg_len = fread (buf, 1, MIN (len, MAXBUFSIZE), fh))) + break; + func (buf, seg_len); + fwrite (buf, 1, seg_len, fh2); + } + + fclose (fh); + fclose (fh2); + sync (); + return 0; +} +#endif + + +int +strarg (char **argv, char *str, const char *separator_s, int max_args) +{ +#ifdef DEBUG + int pos = 0; +#endif + int argc = 0; + + if (!str) + return 0; + if (!*str) + return 0; + + for (; (argv[argc] = (char *) strtok (!argc ? str : NULL, separator_s)) && + (argc < (max_args - 1)); argc++) + ; + +#ifdef DEBUG + fprintf (stderr, "argc: %d\n", argc); + for (pos = 0; pos < argc; pos++) + fprintf (stderr, "argv[%d]: %s\n", pos, argv[pos]); + + fflush (stderr); +#endif + + return argc; +} + + +#ifdef _WIN32 +int +truncate (const char *path, off_t size) +{ + int retval; + HANDLE file = CreateFile (path, GENERIC_WRITE, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (file == INVALID_HANDLE_VALUE) + return -1; + + SetFilePointer (file, size, 0, FILE_BEGIN); + retval = SetEndOfFile (file); // returns nonzero on success + CloseHandle (file); + + return retval ? 0 : -1; // truncate() returns zero on success +} + + +int +sync (void) +{ + _commit (fileno (stdout)); + _commit (fileno (stderr)); + fflush (NULL); // flushes all streams opened for output + return 0; +} + + +#if defined __MINGW32__ && defined DLL +// Ugly hack in order to fix something in zlib (yep, it's that bad) +FILE * +fdopen (int fd, const char *mode) +{ + return _fdopen (fd, mode); +} +#endif + + +#elif defined AMIGA // _WIN32 +int +truncate (const char *path, off_t size) +{ + BPTR fh; + ULONG newsize; + + if (!(fh = Open (path, MODE_OLDFILE))) + return -1; + + newsize = SetFileSize (fh, size, OFFSET_BEGINNING); + Close (fh); + + return newsize == (ULONG) size ? 0 : -1; // truncate() returns zero on success +} + + +int +chmod (const char *path, mode_t mode) +{ + if (!SetProtection ((STRPTR) path, + ((mode & S_IRUSR ? 0 : FIBF_READ) | + (mode & S_IWUSR ? 0 : FIBF_WRITE | FIBF_DELETE) | + (mode & S_IXUSR ? 0 : FIBF_EXECUTE) | + (mode & S_IRGRP ? FIBF_GRP_READ : 0) | + (mode & S_IWGRP ? FIBF_GRP_WRITE | FIBF_GRP_DELETE : 0) | + (mode & S_IXGRP ? FIBF_GRP_EXECUTE : 0) | + (mode & S_IROTH ? FIBF_OTR_READ : 0) | + (mode & S_IWOTH ? FIBF_OTR_WRITE | FIBF_OTR_DELETE : 0) | + (mode & S_IXOTH ? FIBF_OTR_EXECUTE : 0)))) + return -1; + else + return 0; +} + + +void +sync (void) +{ +} + + +int +readlink (const char *path, char *buf, int bufsize) +{ + (void) path; // warning remover + (void) buf; // idem + (void) bufsize; // idem + // always return -1 as if anything passed to it isn't a soft link + return -1; +} + + +// custom _popen() and _pclose(), because the standard ones (named popen() and +// pclose()) are buggy +FILE * +_popen (const char *path, const char *mode) +{ + int fd; + BPTR fh; + long fhflags; + char *apipe = malloc (strlen (path) + 7); + + if (!apipe) + return NULL; + + strcpy (apipe, "APIPE:"); + strcat (apipe, path); + + if (*mode == 'w') + fhflags = MODE_NEWFILE; + else + fhflags = MODE_OLDFILE; + + if (!(fh = Open (apipe, fhflags))) + return NULL; + + return fdopen (fd, mode); +} + + +int +_pclose (FILE *stream) +{ + return fclose (stream); +} +#endif // AMIGA diff --git a/ucon64/2.0/src/libdiscmage/misc.h b/ucon64/2.0/src/libdiscmage/misc.h new file mode 100644 index 0000000..e030f4b --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/misc.h @@ -0,0 +1,617 @@ +/* +misc.h - miscellaneous functions + +Copyright (c) 1999 - 2004 NoisyB +Copyright (c) 2001 - 2004 dbjh +Copyright (c) 2002 - 2004 Jan-Erik Karlsson (Amiga code) + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef MISC_H +#define MISC_H + +#ifdef HAVE_CONFIG_H +#include "config.h" // USE_ZLIB, USE_ANSI_COLOR support +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include // gauge() prototype contains time_t +#include +#ifdef USE_ZLIB +#include "misc_z.h" +#endif // USE_ZLIB + + +#ifdef __sun +#ifdef __SVR4 +#define __solaris__ +#endif +#endif + +#ifdef HAVE_INTTYPES_H +#include +#else // __MSDOS__, _WIN32 (VC++) +#ifndef OWN_INTTYPES +#define OWN_INTTYPES // signal that these are defined +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +#ifndef _WIN32 +typedef unsigned long long int uint64_t; +#else +typedef unsigned __int64 uint64_t; +#endif +typedef signed char int8_t; +typedef signed short int int16_t; +typedef signed int int32_t; +#ifndef _WIN32 +typedef signed long long int int64_t; +#else +typedef signed __int64 int64_t; +#endif +#endif // OWN_INTTYPES +#endif + +#if (defined __unix__ && !defined __MSDOS__) || defined __BEOS__ || \ + defined AMIGA || defined __APPLE__ // Mac OS X actually +// GNU/Linux, Solaris, FreeBSD, Cygwin, BeOS, Amiga, Mac (OS X) +#define FILE_SEPARATOR '/' +#define FILE_SEPARATOR_S "/" +#else // DJGPP, Win32 +#define FILE_SEPARATOR '\\' +#define FILE_SEPARATOR_S "\\" +#endif + +#ifndef MAXBUFSIZE +#define MAXBUFSIZE 32768 +#endif // MAXBUFSIZE + +#ifndef ARGS_MAX +#define ARGS_MAX 128 +#endif // ARGS_MAX + +#if (!defined TRUE || !defined FALSE) +#define FALSE 0 +#define TRUE (!FALSE) +#endif + +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif +#ifndef MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) +#endif +#define LIB_VERSION(ver, rel, seq) (((ver) << 16) | ((rel) << 8) | (seq)) +#define NULL_TO_EMPTY(str) ((str) ? (str) : ("")) +//#define RANDOM(min, max) ((rand () % (max - min)) + min) +#define OFFSET(a, offset) ((((unsigned char *)&(a))+(offset))[0]) + +#ifdef WORDS_BIGENDIAN +#undef WORDS_BIGENDIAN +#endif + +#if defined _LIBC || defined __GLIBC__ + #include + #if __BYTE_ORDER == __BIG_ENDIAN + #define WORDS_BIGENDIAN 1 + #endif +#elif defined AMIGA || defined __sparc__ || defined __BIG_ENDIAN__ || \ + defined __APPLE__ + #define WORDS_BIGENDIAN 1 +#endif + +#ifdef __MSDOS__ // __MSDOS__ must come before __unix__, + #define CURRENT_OS_S "MSDOS" // because DJGPP defines both +#elif defined __unix__ + #ifdef __CYGWIN__ + #define CURRENT_OS_S "Win32 (Cygwin)" + #elif defined __FreeBSD__ + #define CURRENT_OS_S "Unix (FreeBSD)" + #elif defined __OpenBSD__ + #define CURRENT_OS_S "Unix (OpenBSD)" + #elif defined __linux__ + #define CURRENT_OS_S "Unix (Linux)" + #elif defined __solaris__ + #ifdef __sparc__ + #define CURRENT_OS_S "Unix (Solaris/Sparc)" + #else + #define CURRENT_OS_S "Unix (Solaris/i386)" + #endif + #else + #define CURRENT_OS_S "Unix" + #endif +#elif defined _WIN32 + #ifdef __MINGW32__ + #define CURRENT_OS_S "Win32 (MinGW)" + #else + #define CURRENT_OS_S "Win32 (Visual C++)" + #endif +#elif defined __APPLE__ + #if defined __POWERPC__ || defined __ppc__ + #define CURRENT_OS_S "Apple (PPC)" + #else + #define CURRENT_OS_S "Apple" + #endif +#elif defined __BEOS__ + #define CURRENT_OS_S "BeOS" +#elif defined AMIGA + #if defined __PPC__ + #define CURRENT_OS_S "Amiga (PPC)" + #else + #define CURRENT_OS_S "Amiga (68K)" + #endif +#else + #define CURRENT_OS_S "?" +#endif + + +/* + mem functions + + memwcmp() memcmp with wildcard support + mem_search() search for a byte sequence + mem_swap_b() swap n bytes of buffer + mem_swap_w() swap n/2 words of buffer + mem_hexdump() hexdump n bytes of buffer; you can use here a virtual_start for the displayed counter +*/ +#ifdef HAVE_BYTESWAP_H +#include +#else +extern uint16_t bswap_16 (uint16_t x); +extern uint32_t bswap_32 (uint32_t x); +extern uint64_t bswap_64 (uint64_t x); +#endif +#ifdef WORDS_BIGENDIAN +#define me2be_16(x) (x) +#define me2be_32(x) (x) +#define me2be_64(x) (x) +#define me2le_16(x) (bswap_16(x)) +#define me2le_32(x) (bswap_32(x)) +#define me2le_64(x) (bswap_64(x)) +#define be2me_16(x) (x) +#define be2me_32(x) (x) +#define be2me_64(x) (x) +#define le2me_16(x) (bswap_16(x)) +#define le2me_32(x) (bswap_32(x)) +#define le2me_64(x) (bswap_64(x)) +#else +#define me2be_16(x) (bswap_16(x)) +#define me2be_32(x) (bswap_32(x)) +#define me2be_64(x) (bswap_64(x)) +#define me2le_16(x) (x) +#define me2le_32(x) (x) +#define me2le_64(x) (x) +#define be2me_16(x) (bswap_16(x)) +#define be2me_32(x) (bswap_32(x)) +#define be2me_64(x) (bswap_64(x)) +#define le2me_16(x) (x) +#define le2me_32(x) (x) +#define le2me_64(x) (x) +#endif +extern int memwcmp (const void *buffer, const void *search, uint32_t searchlen, int wildcard); +extern void *mem_search (const void *buffer, uint32_t buflen, const void *search, uint32_t searchlen); +extern void *mem_swap_b (void *buffer, uint32_t n); +extern void *mem_swap_w (void *buffer, uint32_t n); +extern void mem_hexdump (const void *buffer, uint32_t n, int virtual_start); + + +/* + String manipulation + + isfname() test if char could be used for filenames + isprint2() test if char could be used for stdout + tofname() replaces chars that can not be used for filenames + toprint2() replaces chars that should not be used for stdout + + is_func() use all is*() functions on an array of char + to_func() use all to*() functions on an array of char + + strtrim() trim isspace()'s from start and end of string + strncpy2() a safer strncpy that DOES terminate a string + + is_upper2() wrapper for is_upper() (DAMN stupid programmers!) + set_suffix() set/replace suffix of filename with suffix + suffix means in this case the suffix INCLUDING the dot '.' + set_suffix_i() like set_suffix(), but doesn't change the case + get_suffix() get suffix of filename + + basename2() DJGPP basename() clone + dirname2() DJGPP dirname() clone + realpath2() realpath() replacement + one_file() returns 1 if two filenames refer to one file, otherwise it + returns 0 + one_filesystem() returns 1 if two filenames refer to files on one file + system, otherwise it returns 0 + mkdir2() mkdir() wrapper who automatically cares for rights, etc. + rename2() renames oldname to newname even if oldname and newname are not + on one file system + truncate2() don't use truncate() to enlarge files, because the result is + undefined (by POSIX) use truncate2() instead which does both + strarg() break a string into tokens +*/ +extern int isfname (int c); +extern int isprint2 (int c); +extern int tofname (int c); +extern int toprint2 (int c); +extern int is_func (char *s, int size, int (*func) (int)); +extern char *to_func (char *s, int size, int (*func) (int)); +#define strupr(s) (to_func(s, strlen(s), toupper)) +#define strlwr(s) (to_func(s, strlen(s), tolower)) +//#ifndef HAVE_STRCASESTR +// strcasestr is GNU only +extern char *strcasestr2 (const char *str, const char *search); +#define stristr strcasestr2 +//#else +//#define stristr strcasestr +//#endif +#ifndef _WIN32 +#define stricmp strcasecmp +#define strnicmp strncasecmp +#endif +extern char *strtrim (char *str); +extern char *strncpy2 (char *dest, const char *src, size_t size); +extern int isupper2 (int c); +extern char *set_suffix (char *filename, const char *suffix); +extern char *set_suffix_i (char *filename, const char *suffix); +extern const char *get_suffix (const char *filename); +extern char *basename2 (const char *path); +// override a possible XPG basename() which modifies its arg +#define basename basename2 +extern char *dirname2 (const char *path); +#define dirname dirname2 +extern int one_file (const char *filename1, const char *filename2); +extern int one_filesystem (const char *filename1, const char *filename2); +extern char *realpath2 (const char *src, char *full_path); +extern int mkdir2 (const char *name); +extern int rename2 (const char *oldname, const char *newname); +extern int truncate2 (const char *filename, int size); +extern int strarg (char **argv, char *str, const char *separator_s, int max_args); + + +/* + Misc stuff + + change_mem{2}() see header of implementation for usage + build_cm_patterns() helper function for change_mem2() to read search patterns + from a file + cleanup_cm_patterns() helper function for build_cm_patterns() to free all + memory allocated for a (list of) st_pattern_t structure(s) + clear_line () clear the current line (79 spaces) + ansi_init() initialize ANSI output + ansi_strip() strip ANSI codes from a string + gauge() init_time == time when gauge() was first started or when + the transfer started + pos == current position + size == full size + gauge given these three values will calculate many + informative things like time, status bar, cps, etc. + it can be used for procedures which take some time to + inform the user about the actual progress + getenv2() getenv() clone for enviroments w/o HOME, TMP or TEMP variables + tmpnam2() replacement for tmpnam() temp must have the size of FILENAME_MAX + renlwr() renames all files tolower() + drop_privileges() switch to the real user and group id (leave "root mode") + register_func() atexit() replacement + returns -1 if it fails, 0 if it was successful + unregister_func() unregisters a previously registered function + returns -1 if it fails, 0 if it was successful + handle_registered_funcs() calls all the registered functions + wait2 wait (sleep) a specified number of milliseconds +*/ +typedef struct st_cm_set +{ + char *data; + int size; +} st_cm_set_t; + +typedef struct st_cm_pattern +{ + char *search, wildcard, escape, *replace; + int search_size, replace_size, offset, n_sets; + st_cm_set_t *sets; +} st_cm_pattern_t; + +extern int change_mem (char *buf, int bufsize, char *searchstr, int strsize, + char wc, char esc, char *newstr, int newsize, int offset, ...); +extern int change_mem2 (char *buf, int bufsize, char *searchstr, int strsize, + char wc, char esc, char *newstr, int newsize, + int offset, st_cm_set_t *sets); +extern int build_cm_patterns (st_cm_pattern_t **patterns, const char *filename, int verbose); +extern void cleanup_cm_patterns (st_cm_pattern_t **patterns, int n_patterns); + +extern void clear_line (void); +extern int ansi_init (void); +extern char *ansi_strip (char *str); +extern int gauge (time_t init_time, int pos, int size); +extern char *getenv2 (const char *variable); +extern char *tmpnam2 (char *temp); +//extern int renlwr (const char *path); +#if defined __unix__ && !defined __MSDOS__ +extern int drop_privileges (void); +#endif +extern int register_func (void (*func) (void)); +extern int unregister_func (void (*func) (void)); +extern void handle_registered_funcs (void); +extern void wait2 (int nmillis); + + +/* + Extended getopt(), usage and workflow handling + + getopt2_usage() a renderer for a nice usage output + takes an st_getopt2_t array + getop2_parse_usage() parse usage output into st_getopt2_t + array (for development) + getopt2_long() turn st_getopt2_t into struct option for getopt1() + getopt2_short() turn st_getopt2_t shortoptions string for getopt1() + + getopt2_get_index_by_val() +*/ +#include "getopt.h" // getopt2 needs struct option from getopt1 + +#define OPTION '-' +#define OPTION_S "-" +#define OPTION_LONG_S "--" +#define OPTARG '=' +#define OPTARG_S "=" + +typedef struct +{ + const char *name; // see getopt() + int has_arg; // see getopt() + int *flag; // see getopt() + int val; // see getopt() + const char *arg_name; // name of the options arg as it should be + // displayed in the --help output + // "--name=arg_name" if has_arg == 1 + // "--name[=arg_name]" if has_arg == 2 + const char *help; // --help, -h, -? output for the current option + void *object; // could be used for workflow objects +} st_getopt2_t; + +extern void getopt2_usage (const st_getopt2_t *option); +#ifdef DEBUG +extern void getopt2_parse_usage (const char *usage_output); +#endif +extern int getopt2_long (struct option *long_option, const st_getopt2_t *option, int n); +extern int getopt2_short (char *short_option, const st_getopt2_t *option, int n); +extern const st_getopt2_t *getopt2_get_index_by_val (const st_getopt2_t *option, int val); + + +/* + Quick I/O + + mode + "r", "rb", "w", "wb", "a", "ab" + + quick_io_c() returns byte read or fputc()'s status + quick_io() returns number of bytes read or written + + Macros + + q_fread() same as fread but takes start and src is a filename + q_fwrite() same as fwrite but takes start and dest is a filename; mode + is the same as fopen() modes + q_fgetc() same as fgetc but takes filename instead of FILE and a pos + q_fputc() same as fputc but takes filename instead of FILE and a pos + b,s,l,f,m == buffer,start,len,filename,mode + + Misc + + q_fncmp() search in filename from start len bytes for the first appearance + of search which has searchlen + wildcard could be one character or -1 (wildcard off) + q_fcpy() copy src from start for len to dest with mode (fopen(..., mode)) + q_rfcpy() copy src to dest without looking at the file data (no + decompression like with q_fcpy()) + q_fswap() swap len bytes of file starting from start + q_fbackup() + + modes + + BAK_DUPE (default) + rename file to keep attributes and copy it back to old name and return + new name + + filename -> rename() -> buf -> f_cpy() -> filename -> return buf + + BAK_MOVE + just rename file and return new name (static) + + filename -> rename() -> buf -> return buf +*/ +extern int quick_io (void *buffer, size_t start, size_t len, const char *fname, const char *mode); +extern int quick_io_c (int value, size_t start, const char *fname, const char *mode); +#define q_fread(b,s,l,f) (quick_io(b,s,l,f,"rb")) +#define q_fwrite(b,s,l,f,m) (quick_io((void *)b,s,l,f,m)) +#define q_fgetc(f,s) (quick_io_c(0,s,f,"rb")) +#define q_fputc(f,s,b,m) (quick_io_c(b,s,f,m)) +typedef enum { SWAP_BYTE, SWAP_WORD } swap_t; + +extern int q_fncmp (const char *filename, int start, int len, + const char *search, int searchlen, int wildcard); +extern int q_fcpy (const char *src, int start, int len, const char *dest, const char *mode); +extern int q_rfcpy (const char *src, const char *dest); +extern int q_fswap (const char *filename, int start, int len, swap_t type); +#define q_fswap_b(f, s, l) q_fswap(f, s, l, SWAP_BYTE) +#define q_fswap_w(f, s, l) q_fswap(f, s, l, SWAP_WORD) +#if 1 +#define BAK_DUPE 0 +#define BAK_MOVE 1 +extern char *q_fbackup (const char *filename, int mode); +#else +extern char *q_fbackup (char *move_name, const char *filename); +#endif +#ifndef USE_ZLIB +extern int q_fsize (const char *filename); +#endif + + +/* + Configuration file handling + + get_property() get value of propname from filename or return value of env + with name like propname or return def + get_property_int() like get_property() but returns an integer which is 0 + if the value of propname was 0, [Nn] or [Nn][Oo] and an + integer or at least 1 for every other case + get_property_fname() like get_property() but specifically for filenames, + i.e., it runs realpath2() on the filename and fixes the + characters if necessary (Cygwin) + set_property() set propname with value in filename + DELETE_PROPERTY() like set_property but when value of propname is NULL the + whole property will disappear from filename +*/ +#define PROPERTY_SEPARATOR '=' +#define PROPERTY_SEPARATOR_S "=" +#define PROPERTY_COMMENT '#' +#define PROPERTY_COMMENT_S "#" +extern char *get_property (const char *filename, const char *propname, char *value, + const char *def); +extern int32_t get_property_int (const char *filename, const char *propname); +extern char *get_property_fname (const char *filename, const char *propname, + char *buffer, const char *def); +extern int set_property (const char *filename, const char *propname, const char *value, const char *comment); +#define DELETE_PROPERTY(a, b) (set_property(a, b, NULL, NULL)) + + +/* + Portability (conio.h, etc...) + + init_conio() init console I/O + deinit_conio() stop console I/O + getch() + kbhit() + fix_character_set() fixes some Cygwin problems with filenames + truncate() + sync() + popen() + pclose() + vprintf2() + printf2() + fprintf2() +*/ +#if (defined __unix__ && !defined __MSDOS__) || defined __BEOS__ || \ + defined __APPLE__ // Mac OS X actually +extern void init_conio (void); +extern void deinit_conio (void); +#define getch getchar // getchar() acts like DOS getch() after init_conio() +extern int kbhit (void); // may only be used after init_conio()! + +#elif defined __MSDOS__ +#include // getch() +#include // kbhit() + +#elif defined _WIN32 +#include // kbhit() & getch() + +#elif defined AMIGA +extern int kbhit (void); +//#define getch getchar +// Gonna use my (Jan-Erik) fake one. Might work better and more like the real +// getch(). +#endif + +#ifdef __CYGWIN__ +extern char *fix_character_set (char *value); +#endif + + +#ifdef _WIN32 +// Note that _WIN32 is defined by cl.exe while the other constants (like WIN32) +// are defined in header files. MinGW's gcc.exe defines all constants. + +#include + +extern int truncate (const char *path, off_t size); +extern int sync (void); +// For MinGW popen() and pclose() are unavailable for DLL's. For DLL's _popen() +// and _pclose() should be used. Visual C++ only has the latter two. +#ifndef pclose // misc_z.h's definition gets higher "precedence" +#define pclose _pclose +#endif +#ifndef popen // idem +#define popen _popen +#endif + +#ifdef USE_ANSI_COLOR +#include + +extern int vprintf2 (const char *format, va_list argptr); +extern int printf2 (const char *format, ...); +extern int fprintf2 (FILE *file, const char *format, ...); +#define vprintf vprintf2 +#define printf printf2 +#define fprintf fprintf2 +#endif // USE_ANSI_COLOR + +#ifndef __MINGW32__ +#include +#include +#include // According to MSDN must + // come after . Yep, that's M$. +#define S_IWUSR _S_IWRITE +#define S_IRUSR _S_IREAD +#define S_ISDIR(mode) ((mode) & _S_IFDIR ? 1 : 0) +#define S_ISREG(mode) ((mode) & _S_IFREG ? 1 : 0) + +#define F_OK 00 +#define W_OK 02 +#define R_OK 04 +#define X_OK R_OK // this is correct for dirs, but not for exes + +#define STDIN_FILENO (fileno (stdin)) +#define STDOUT_FILENO (fileno (stdout)) +#define STDERR_FILENO (fileno (stderr)) + +#else +#ifdef DLL +#define access _access +#define chmod _chmod +#define fileno _fileno +#define getcwd _getcwd +#define isatty _isatty +#define rmdir _rmdir +#define stat _stat +#define strdup _strdup +#define stricmp _stricmp +#define strnicmp _strnicmp +#endif // DLL + +#endif // !__MINGW32__ + +#elif defined AMIGA // _WIN32 +// custom _popen() and _pclose(), because the standard ones (named popen() and +// pclose()) are buggy +#ifndef pclose // misc_z.h's definition gets higher "precedence" +#define pclose _pclose +#endif +#ifndef popen // idem +#define popen _popen +#endif +extern FILE *_popen (const char *path, const char *mode); +extern int _pclose (FILE *stream); +#endif // AMIGA + +#ifdef __cplusplus +} +#endif + +#endif // MISC_H diff --git a/ucon64/2.0/src/libdiscmage/misc_wav.c b/ucon64/2.0/src/libdiscmage/misc_wav.c new file mode 100644 index 0000000..47b9702 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/misc_wav.c @@ -0,0 +1,148 @@ +/* +misc_wav.c + +Copyright (c) 2004 NoisyB (noisyb@gmx.net) + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include +#include +#include "misc.h" +#include "misc_wav.h" +#ifndef M_PI +#define M_PI 3.1415926535 +#endif +// TODO: replace ceil(), floor() and sin() + +unsigned char wav_header[3][80] = { + { + 'R', 'I', 'F', 'F', 0x80, 0x80, 0x80, 0x80, // RIFF TAG + 'W', 'A', 'V', 'E', // WAV TAG + 'f', 'm', 't', ' ', 0x10, 0, 0, 0, // FMT TAG + 1, 0, // format (WAVE_FORMAT_PCM) + 1, 0, // CHANNELS + 0x22, 0x56, 0, 0, // SamplesPerSec + 0x22, 0x56, 0, 0, // BytesPerSec + 1, 0, // Block align + 8, 0, // Bits per sample + 'd', 'a', 't', 'a', 0, 0, 0, 0, '\0' // DATA TAG + }, + { + 'R', 'I', 'F', 'F', 0x80, 0x80, 0x80, 0x80, // RIFF TAG + 'W', 'A', 'V', 'E', // WAV TAG + 'f', 'm', 't', ' ', 0x10, 0, 0, 0, // FMT TAG + 1, 0, // format (WAVE_FORMAT_PCM) + 1, 0, // CHANNELS + 0x44, 0xac, 0, 0, // SamplesPerSec + 0x44, 0xac, 0, 0, // BytesPerSec + 1, 0, // Block align + 8, 0, // Bits per sample + 'd', 'a', 't', 'a', 0, 0, 0, 0, '\0' // DATA TAG + }, + { + 'R', 'I', 'F', 'F', 0x80, 0x80, 0x80, 0x80, // RIFF TAG + 'W', 'A', 'V', 'E', // WAV TAG + 'f', 'm', 't', ' ', 0x10, 0, 0, 0, // FMT TAG + 1, 0, // format (WAVE_FORMAT_PCM) + 2, 0, // CHANNELS + 0x44, 0xac, 0, 0, // SamplesPerSec + 0x10, 0xb1, 2, 0, // BytesPerSec + 4, 0, // Block align + 0x10, 0, // Bits per sample + 'd', 'a', 't', 'a', 0, 0, 0, 0, '\0' // DATA TAG + } +}; + + +typedef struct +{ + uint8_t magic[4]; // 'RIFF' + uint32_t total_length; // length of file minus the 8 byte riff header + + uint8_t type[4]; // 'WAVE' + + uint8_t fmt[4]; // 'fmt ' + uint32_t header_length; // length of format chunk minus 8 byte header + uint16_t format; // identifies WAVE_FORMAT_PCM + uint16_t channels; + uint32_t freq; // samples per second per channel + uint32_t bytespersecond; + uint16_t blockalign; // basic block size + uint16_t bitspersample; + + // PCM formats then go straight to the data chunk + uint8_t data[4]; // 'data' + uint32_t data_length; // length of data chunk minus 8 byte header +} st_wav_header_t; + + +int +misc_wav_write_header (FILE *fh, int channels, int freq, + int bytespersecond, int blockalign, + int bitspersample, int data_length) +{ + st_wav_header_t wav_header; + memset (&wav_header, 0, sizeof (st_wav_header_t)); + + strncpy ((char *) wav_header.magic, "RIFF", 4); + wav_header.total_length = me2le_32 (data_length + sizeof (st_wav_header_t) - 8); + strncpy ((char *) wav_header.type, "WAVE", 4); + strncpy ((char *) wav_header.fmt, "fmt ", 4); + wav_header.header_length = me2le_32 (16); // always 16 + wav_header.format = me2le_16 (1); // WAVE_FORMAT_PCM == default + wav_header.channels = me2le_16 (channels); + wav_header.freq = me2le_32 (freq); + wav_header.bytespersecond = me2le_32 (bytespersecond); + wav_header.blockalign = me2le_16 (blockalign); + wav_header.bitspersample = me2le_16 (bitspersample); + strncpy ((char *) wav_header.data, "data", 4); + wav_header.data_length = me2le_32 (data_length); + + return fwrite (&wav_header, 1, sizeof (st_wav_header_t), fh); +} + + +void +misc_wav_generator (unsigned char *bit, int bitLength, float volume, int wavType) +{ + int i; + +#ifndef USE_LIBMATH + (void) wavType; +#else + if (wavType == SQUARE_WAVE) +#endif // USE_LIBMATH + { + int halfBitLength = (int) (floor ((float) bitLength) / 2.0); + int isOdd = (int) (ceil ((float) bitLength / 2.0) - halfBitLength); + + for (i = 0; i < halfBitLength; i++) + bit[i] = (unsigned char) floor (0xfc * volume); + + if (isOdd) + bit[i++] = 0x80; + + for (; i < bitLength; i++) + bit[i] = (unsigned char) floor (0x06 * volume); + } +#ifdef USE_LIBMATH + else // SINE_WAV + for (i = 0; i < bitLength; i++) + bit[i] = (unsigned char) floor + (((sin ((((double) 2 * (double) M_PI) / (double) bitLength) * (double) i) * volume + 1) * 128)); +#endif // USE_LIBMATH +} diff --git a/ucon64/2.0/src/libdiscmage/misc_wav.h b/ucon64/2.0/src/libdiscmage/misc_wav.h new file mode 100644 index 0000000..8fa3117 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/misc_wav.h @@ -0,0 +1,41 @@ +/* +misc_wav.h + +Copyright (c) 2004 NoisyB (noisyb@gmx.net) + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef MISC_WAV_H +#define MISC_WAV_H +/* + misc_wav_header() read/(over-)write a wav header + mode == "r", "rb", "w", "wb", etc... + it does NOT insert a wav header + misc_wav_generator() generate a SQUARE_WAVE or SINE_WAVE +*/ +extern int misc_wav_write_header (FILE *fh, int channels, int freq, + int bytespersecond, int blockalign, + int bitspersample, int data_length); +// TODO: remove these +#define misc_wav_write_header_v2(fh,c,f,bit,dl) misc_wav_write_header(fh, c, f, (bit*c*f)/8, (bit*c)/8, bit, dl) +#define misc_wav_write_header_v3(fh,dl) misc_wav_write_header (fh, 2, 44100, 176400, 4, 16, dl) + + +#define SQUARE_WAVE 0 +#define SINE_WAVE 1 +extern void misc_wav_generator (unsigned char *bit, int bitLength, + float volume, int wavType); +#endif // MISC_WAV_H diff --git a/ucon64/2.0/src/libdiscmage/misc_z.c b/ucon64/2.0/src/libdiscmage/misc_z.c new file mode 100644 index 0000000..ae1417b --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/misc_z.c @@ -0,0 +1,666 @@ +/* +misc_z.c - miscellaneous zlib functions + +Copyright (c) 2001 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" // USE_ZLIB +#endif +#ifdef USE_ZLIB +#include +#include +#include +#include +#include +#include +#include "misc_z.h" +#include "misc.h" +#include "map.h" +#include "unzip.h" +#if defined DJGPP && defined DLL +#include "dxedll_priv.h" +#endif + + +extern int errno; + + +int +q_fsize (const char *filename) +// If USE_ZLIB is defined this function is very slow. Please avoid to use +// it much. +{ + FILE *file; + unsigned char magic[4] = { 0 }; + +#undef fopen +#undef fread +#undef fclose + if ((file = fopen (filename, "rb")) == NULL) + { + errno = ENOENT; + return -1; + } + fread (magic, 1, sizeof (magic), file); + fclose (file); +#define fopen fopen2 +#define fclose fclose2 +#define fread fread2 + + if (magic[0] == 0x1f && magic[1] == 0x8b && magic[2] == 0x08) + { // ID1, ID2 and CM. gzip uses Compression Method 8 + int size = 0; + + if ((file = (FILE *) gzopen (filename, "rb")) == NULL) + { // Shouldn't fail because we could open it with fopen() + errno = ENOENT; + return -1; + } +#if 1 + // This is not much faster than the other method + while (!gzeof (file)) + gzseek (file, 1024 * 1024, SEEK_CUR); + size = gztell (file); +#else + // Is there a more efficient way to determine the uncompressed size? + while ((bytesread = gzread (file, buf, MAXBUFSIZE)) > 0) + size += bytesread; +#endif + gzclose (file); + return size; + } + else if (magic[0] == 'P' && magic[1] == 'K' && magic[2] == 0x03 && magic[3] == 0x04) + { + unz_file_info info; + + if ((file = (FILE *) unzOpen (filename)) == NULL) + { + errno = ENOENT; + return -1; + } + unzip_goto_file (file, unzip_current_file_nr); + unzGetCurrentFileInfo (file, &info, NULL, 0, NULL, 0, NULL, 0); + unzClose (file); + + return info.uncompressed_size; + } + else + { + struct stat fstate; + + if (!stat (filename, &fstate)) + return fstate.st_size; + + errno = ENOENT; + return -1; + } +} + + +/* + The zlib functions gzwrite() and gzputc() write compressed data if the file + was opened with a "w" in the mode string, but we want to control whether we + write compressed data. Note that for the mode string "w+", zlib ignores the + '+'. zlib does the same for "r+". + Currently we never write compressed data. That is, no code fopen()s files + with a mode string containing 'f', 'h' or a number, but writing compressed + output does work. +*/ +st_map_t *fh_map = NULL; // associative array: file handle -> file mode + +typedef enum { FM_NORMAL, FM_GZIP, FM_ZIP, FM_UNDEF } fmode2_t; + +typedef struct st_finfo +{ + fmode2_t fmode; + int compressed; +} st_finfo_t; + +static st_finfo_t finfo_list[6] = { {FM_NORMAL, 0}, + {FM_NORMAL, 1}, // should never be used + {FM_GZIP, 0}, + {FM_GZIP, 1}, + {FM_ZIP, 0}, // should never be used + {FM_ZIP, 1} }; + +int unzip_current_file_nr = 0; + + +static void +init_fh_map (void) +{ + fh_map = map_create (20); // 20 simultaneous open files + map_put (fh_map, stdin, &finfo_list[0]); // should be enough to start with + map_put (fh_map, stdout, &finfo_list[0]); + map_put (fh_map, stderr, &finfo_list[0]); +} + + +static st_finfo_t * +get_finfo (FILE *file) +{ + st_finfo_t *finfo; + + if (fh_map == NULL) + init_fh_map (); + if ((finfo = (st_finfo_t *) map_get (fh_map, file)) == NULL) + { + fprintf (stderr, "\nINTERNAL ERROR: File pointer was not present in map (%p)\n", file); + map_dump (fh_map); + exit (1); + } + return finfo; +} + + +static fmode2_t +get_fmode (FILE *file) +{ + return get_finfo (file)->fmode; +} + + +int +unzip_get_number_entries (const char *filename) +{ + FILE *file; + unsigned char magic[4] = { 0 }; + +#undef fopen +#undef fread +#undef fclose + if ((file = fopen (filename, "rb")) == NULL) + { + errno = ENOENT; + return -1; + } + fread (magic, 1, sizeof (magic), file); + fclose (file); +#define fopen fopen2 +#define fclose fclose2 +#define fread fread2 + + if (magic[0] == 'P' && magic[1] == 'K' && magic[2] == 0x03 && magic[3] == 0x04) + { + unz_global_info info; + + file = (FILE *) unzOpen (filename); + unzGetGlobalInfo (file, &info); + unzClose (file); + return info.number_entry; + } + else + return -1; +} + + +int +unzip_goto_file (unzFile file, int file_index) +{ + int retval = unzGoToFirstFile (file), n = 0; + + if (file_index > 0) + while (n < file_index) + { + retval = unzGoToNextFile (file); + n++; + } + return retval; +} + + +static int +unzip_seek_helper (FILE *file, int offset) +{ + char buffer[MAXBUFSIZE]; + int n, tmp, pos = unztell (file); // returns ftell() of the "current file" + + if (pos == offset) + return 0; + else if (pos > offset) + { + unzCloseCurrentFile (file); + unzip_goto_file (file, unzip_current_file_nr); + unzOpenCurrentFile (file); + pos = 0; + } + n = offset - pos; + while (n > 0 && !unzeof (file)) + { + tmp = unzReadCurrentFile (file, buffer, n > MAXBUFSIZE ? MAXBUFSIZE : n); + if (tmp < 0) + return -1; + n -= tmp; + } + return n > 0 ? -1 : 0; +} + + +FILE * +fopen2 (const char *filename, const char *mode) +{ +#undef fopen + int n, len = strlen (mode), read = 0, compressed = 0; + fmode2_t fmode = FM_UNDEF; + st_finfo_t *finfo; + FILE *file = NULL; + +// printf ("opening %s", filename); + if (fh_map == NULL) + init_fh_map (); + + for (n = 0; n < len; n++) + { + switch (mode[n]) + { + case 'r': + read = 1; + break; + case 'f': + case 'h': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + fmode = FM_GZIP; + break; + case 'w': + case 'a': + fmode = FM_NORMAL; + break; + case '+': + if (fmode == FM_UNDEF) + fmode = FM_NORMAL; + break; + } + } + + if (read) + { +#undef fread +#undef fclose + unsigned char magic[4] = { 0 }; + FILE *fh; + + // TODO?: check if mode is valid for fopen(), i.e., no 'f', 'h' or number + if ((fh = fopen (filename, mode)) != NULL) + { + fread (magic, sizeof (magic), 1, fh); + if (magic[0] == 0x1f && magic[1] == 0x8b && magic[2] == 0x08) + { // ID1, ID2 and CM. gzip uses Compression Method 8 + fmode = FM_GZIP; + compressed = 1; + } + else if (magic[0] == 'P' && magic[1] == 'K' && + magic[2] == 0x03 && magic[3] == 0x04) + { + fmode = FM_ZIP; + compressed = 1; + } + else + /* + Files that are opened with mode "r+" will probably be written to. + zlib doesn't support mode "r+", so we have to use FM_NORMAL. + Mode "r" doesn't require FM_NORMAL and FM_GZIP works, but we + shouldn't introduce needless overhead. + */ + fmode = FM_NORMAL; + fclose (fh); + } +#define fread fread2 +#define fclose fclose2 + } + + if (fmode == FM_NORMAL) + file = fopen (filename, mode); + else if (fmode == FM_GZIP) + file = (FILE *) gzopen (filename, mode); + else if (fmode == FM_ZIP) + { + file = (FILE *) unzOpen (filename); + if (file != NULL) + { + unzip_goto_file (file, unzip_current_file_nr); + unzOpenCurrentFile (file); + } + } + + if (file == NULL) + return NULL; + + finfo = &finfo_list[fmode * 2 + compressed]; + fh_map = map_put (fh_map, file, finfo); + +/* + printf (", ptr = %p, mode = %s, fmode = %s\n", file, mode, + fmode == FM_NORMAL ? "FM_NORMAL" : + (fmode == FM_GZIP ? "FM_GZIP" : + (fmode == FM_ZIP ? "FM_ZIP" : "FM_UNDEF"))); + map_dump (fh_map); +*/ + return file; +#define fopen fopen2 +} + + +int +fclose2 (FILE *file) +{ +#undef fclose + fmode2_t fmode = get_fmode (file); + + map_del (fh_map, file); + if (fmode == FM_NORMAL) + return fclose (file); + else if (fmode == FM_GZIP) + return gzclose (file); + else if (fmode == FM_ZIP) + { + unzCloseCurrentFile (file); + return unzClose (file); + } + else + return EOF; +#define fclose fclose2 +} + + +int +fseek2 (FILE *file, long offset, int mode) +{ +#undef fseek + st_finfo_t *finfo = get_finfo (file); + +/* +// if (fmode != FM_NORMAL) + printf ("fmode = %s\n", finfo->fmode == FM_NORMAL ? "FM_NORMAL" : + (finfo->fmode == FM_GZIP ? "FM_GZIP" : + (finfo->fmode == FM_ZIP ? "FM_ZIP" : "FM_UNDEF"))); +*/ + + if (finfo->fmode == FM_NORMAL) + return fseek (file, offset, mode); + else if (finfo->fmode == FM_GZIP) + { + if (mode == SEEK_END) // zlib doesn't support SEEK_END + { + // Note that this is _slow_... + while (!gzeof (file)) + { + gzgetc (file); // necessary for _uncompressed_ files in order to set EOF + gzseek (file, 1024 * 1024, SEEK_CUR); + } + offset += gztell (file); + mode = SEEK_SET; + } + /* + FUCKING zlib documentation! It took me around 4 hours of debugging time + to find out that the doc is wrong! From the doc: + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) + That is not true for uncompressed files. gzrewind() doesn't change the + file pointer for uncompressed files in the ports I tested (zlib 1.1.3, + DJGPP, Cygwin & GNU/Linux). It clears the EOF indicator. + */ + if (!finfo->compressed) + gzrewind (file); + return gzseek (file, offset, mode) == -1 ? -1 : 0; + } + else if (finfo->fmode == FM_ZIP) + { + int base; + if (mode != SEEK_SET && mode != SEEK_CUR && mode != SEEK_END) + { + errno = EINVAL; + return -1; + } + if (mode == SEEK_SET) + base = 0; + else if (mode == SEEK_CUR) + base = unztell (file); + else // mode == SEEK_END + { + unz_file_info info; + + unzip_goto_file (file, unzip_current_file_nr); + unzGetCurrentFileInfo (file, &info, NULL, 0, NULL, 0, NULL, 0); + base = info.uncompressed_size; + } + return unzip_seek_helper (file, base + offset); + } + return -1; +#define fseek fseek2 +} + + +size_t +fread2 (void *buffer, size_t size, size_t number, FILE *file) +{ +#undef fread + fmode2_t fmode = get_fmode (file); + + if (size == 0 || number == 0) + return 0; + + if (fmode == FM_NORMAL) + return fread (buffer, size, number, file); + else if (fmode == FM_GZIP) + { + int n = gzread (file, buffer, number * size); + return n / size; + } + else if (fmode == FM_ZIP) + { + int n = unzReadCurrentFile (file, buffer, number * size); + return n / size; + } + return 0; +#define fread fread2 +} + + +int +fgetc2 (FILE *file) +{ +#undef fgetc + fmode2_t fmode = get_fmode (file); + + if (fmode == FM_NORMAL) + return fgetc (file); + else if (fmode == FM_GZIP) + return gzgetc (file); + else if (fmode == FM_ZIP) + { + char c; + int retval = unzReadCurrentFile (file, &c, 1); + return retval <= 0 ? EOF : c & 0xff; // avoid sign bit extension + } + else + return EOF; +#define fgetc fgetc2 +} + + +char * +fgets2 (char *buffer, int maxlength, FILE *file) +{ +#undef fgets + fmode2_t fmode = get_fmode (file); + + if (fmode == FM_NORMAL) + return fgets (buffer, maxlength, file); + else if (fmode == FM_GZIP) + { + char *retval = gzgets (file, buffer, maxlength); + return retval == Z_NULL ? NULL : retval; + } + else if (fmode == FM_ZIP) + { + int n = 0, c = 0; + while (n < maxlength - 1 && (c = fgetc (file)) != EOF) + { + buffer[n] = c; // '\n' must also be stored in buffer + n++; + if (c == '\n') + { + buffer[n] = 0; + break; + } + } + if (n >= maxlength - 1 || c == EOF) + buffer[n] = 0; + return n > 0 ? buffer : NULL; + } + else + return NULL; +#define fgets fgets2 +} + + +int +feof2 (FILE *file) +{ +#undef feof + fmode2_t fmode = get_fmode (file); + + if (fmode == FM_NORMAL) + return feof (file); + else if (fmode == FM_GZIP) + return gzeof (file); + else if (fmode == FM_ZIP) + return unzeof (file); // returns feof() of the "current file" + else + return -1; +#define feof feof2 +} + + +size_t +fwrite2 (const void *buffer, size_t size, size_t number, FILE *file) +{ +#undef fwrite + fmode2_t fmode = get_fmode (file); + + if (size == 0 || number == 0) + return 0; + + if (fmode == FM_NORMAL) + return fwrite (buffer, size, number, file); + else if (fmode == FM_GZIP) + { + int n = gzwrite (file, (void *) buffer, number * size); + return n / size; + } + else + return 0; // writing to zip files is not supported +#define fwrite fwrite2 +} + + +int +fputc2 (int character, FILE *file) +{ +#undef fputc + fmode2_t fmode = get_fmode (file); + + if (fmode == FM_NORMAL) + return fputc (character, file); + else if (fmode == FM_GZIP) + return gzputc (file, character); + else + return EOF; // writing to zip files is not supported +#define fputc fputc2 +} + + +long +ftell2 (FILE *file) +{ +#undef ftell + fmode2_t fmode = get_fmode (file); + + if (fmode == FM_NORMAL) + return ftell (file); + else if (fmode == FM_GZIP) + return gztell (file); + else if (fmode == FM_ZIP) + return unztell (file); // returns ftell() of the "current file" + else + return -1; +#define ftell ftell2 +} + + +void +rewind2 (FILE *file) +{ + fseek2 (file, 0, SEEK_SET); +} + + +FILE * +popen2 (const char *command, const char *mode) +{ +#undef popen +#if defined _WIN32 || defined AMIGA +#define popen _popen +#endif + int compressed = 0; + fmode2_t fmode = FM_NORMAL; + st_finfo_t *finfo; + FILE *file; + + if (fh_map == NULL) + init_fh_map (); + + file = popen (command, mode); + if ((file = popen (command, mode)) == NULL) + return NULL; + + finfo = &finfo_list[fmode * 2 + compressed]; + fh_map = map_put (fh_map, file, finfo); + + return file; +#undef popen +#define popen popen2 +} + + +int +pclose2 (FILE *stream) +{ +#undef pclose +#if defined _WIN32 || defined AMIGA +#define pclose _pclose +#endif + fmode2_t fmode = get_fmode (stream); + + if (fmode == FM_NORMAL) + return pclose (stream); + else if (fmode == FM_GZIP || fmode == FM_ZIP) + return -1; + else + return -1; +#undef pclose +#define pclose pclose2 +} +#endif // USE_ZLIB diff --git a/ucon64/2.0/src/libdiscmage/misc_z.h b/ucon64/2.0/src/libdiscmage/misc_z.h new file mode 100644 index 0000000..ab76f56 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/misc_z.h @@ -0,0 +1,86 @@ +/* +misc_z.h - miscellaneous zlib functions + +Copyright (c) 2001 - 2003 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef MISC_Z_H +#define MISC_Z_H + +#ifdef HAVE_CONFIG_H +#include "config.h" // USE_ZLIB +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef USE_ZLIB +// make sure ZLIB support is enabled everywhere +//#warning USE_ZLIB is defined + +#include +#include +#include "unzip.h" + +extern FILE *fopen2 (const char *filename, const char *mode); +extern int fclose2 (FILE *file); +extern int fseek2 (FILE *file, long offset, int mode); +extern size_t fread2 (void *buffer, size_t size, size_t number, FILE *file); +extern int fgetc2 (FILE *file); +extern char *fgets2 (char *buffer, int maxlength, FILE *file); +extern int feof2 (FILE *file); +extern size_t fwrite2 (const void *buffer, size_t size, size_t number, FILE *file); +extern int fputc2 (int character, FILE *file); +extern long ftell2 (FILE *file); +extern void rewind2 (FILE *file); +extern FILE *popen2 (const char *command, const char *mode); +extern int pclose2 (FILE *stream); + +extern int q_fsize2 (const char *filename); + +#undef feof // necessary on (at least) Cygwin + +#define fopen(FILE, MODE) fopen2(FILE, MODE) +#define fclose(FILE) fclose2(FILE) +#define fseek(FILE, OFFSET, MODE) fseek2(FILE, OFFSET, MODE) +#define fread(BUF, SIZE, NUM, FILE) fread2(BUF, SIZE, NUM, FILE) +#define fgetc(FILE) fgetc2(FILE) +#define fgets(BUF, MAXLEN, FILE) fgets2(BUF, MAXLEN, FILE) +#define feof(FILE) feof2(FILE) +#define fwrite(BUF, SIZE, NUM, FILE) fwrite2(BUF, SIZE, NUM, FILE) +#define fputc(CHAR, FILE) fputc2(CHAR, FILE) +#define ftell(FILE) ftell2(FILE) +#define rewind(FILE) rewind2(FILE) +#undef popen +#define popen(COMMAND, MODE) popen2(COMMAND, MODE) +#undef pclose +#define pclose(FILE) pclose2(FILE) +#define q_fsize(FILENAME) q_fsize2(FILENAME) + +// Returns the number of files in the "central dir of this disk" or -1 if +// filename is not a ZIP file or an error occured. +extern int unzip_get_number_entries (const char *filename); +extern int unzip_goto_file (unzFile file, int file_index); +extern int unzip_current_file_nr; +#endif + +#ifdef __cplusplus +} +#endif + +#endif // MISC_Z_H diff --git a/ucon64/2.0/src/libdiscmage/unzip.c b/ucon64/2.0/src/libdiscmage/unzip.c new file mode 100644 index 0000000..1b84182 --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/unzip.c @@ -0,0 +1,1253 @@ +/* unzip.c -- IO on .zip files using zlib + Version 0.15 beta, Mar 19th, 1998, + + Read unzip.h for more info +*/ + + +#include +#include +#include +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + + +#if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) && \ + !defined(CASESENSITIVITYDEFAULT_NO) +#define CASESENSITIVITYDEFAULT_NO +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +const char unz_copyright[] = + " unzip 0.15 Copyright 1998 Gilles Vollant "; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;/* relative offset of local header 4 bytes */ +} unz_file_info_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + + uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + uLong offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + uLong pos_local_extrafield; /* position in the local extra field in read*/ + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + uLong rest_read_compressed; /* number of byte to be decompressed */ + uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + FILE* file; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ +} file_in_zip_read_info_s; + + +/* unz_s contain internal information about the zipfile +*/ +typedef struct +{ + FILE* file; /* io structore of the zipfile */ + unz_global_info gi; /* public global information */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + uLong num_file; /* number of the current file in the zipfile*/ + uLong pos_in_central_dir; /* pos of the current file in the central dir*/ + uLong current_file_ok; /* flag about the usability of the current file*/ + uLong central_pos; /* position of the beginning of the central dir*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info cur_file_info; /* public info about the current file in zip*/ + unz_file_info_internal cur_file_info_internal; /* private info about it*/ + file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ +} unz_s; + + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unzlocal_getByte(FILE *fin,int *pi) +{ + unsigned char c; + int err = fread(&c, 1, 1, fin); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ferror(fin)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unzlocal_getShort (FILE* fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unzlocal_getLong (FILE* fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (const char* fileName1,const char* fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (const char* fileName1,const char* fileName2, + int iCaseSensitivity) +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#define BUFREADCOMMENT (0x400) + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local uLong unzlocal_SearchCentralDir(FILE *fin) +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + if (fseek(fin,0,SEEK_END) != 0) + return 0; + + + uSizeFile = ftell( fin ); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (fseek(fin,uReadPos,SEEK_SET)!=0) + break; + + if (fread(buf,(uInt)uReadSize,1,fin)!=1) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib109.zip" or on an Unix computer + "zlib/zlib109.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +extern unzFile ZEXPORT unzOpen (const char *path) +{ + unz_s us; + unz_s *s; + uLong central_pos,uL; + FILE * fin ; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + fin=fopen(path,"rb"); + if (fin==NULL) + return NULL; + + central_pos = unzlocal_SearchCentralDir(fin); + if (central_pos==0) + err=UNZ_ERRNO; + + if (fseek(fin,central_pos,SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unzlocal_getLong(fin,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir */ + if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* zipfile comment length */ + if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + fclose(s->file); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo (unzFile file,unz_global_info *pglobal_info) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unzlocal_DosDateToTmuDate (uLong ulDosDate,tm_unz* ptm) +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unzlocal_GetCurrentFileInfoInternal (unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (fseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (fread(szFileName,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extrafile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (fread(extraField,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentfile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (fread(szComment,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) +{ + return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (unzFile file) +{ + int err=UNZ_OK; + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (unzFile file) +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (unzFile file, const char *szFileName, + int iCaseSensitivity) +{ + unz_s* s; + int err; + + + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + return err; +} + + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unzlocal_CheckCurrentFileCoherencyHeader (unz_s* s, + uInt* piSizeVar, + uLong *poffset_local_extrafield, + uInt *psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (fseek(s->file,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile (unzFile file) +{ + int err=UNZ_OK; + int Store; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*) + ALLOC(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if ((s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + Store = s->cur_file_info.compression_method==0; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->file=s->file; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if (!Store) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + + s->pfile_in_zip_read = pfile_in_zip_read_info; + return UNZ_OK; +} + + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ + int err=UNZ_OK; + uInt iRead = 0; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if (len>pfile_in_zip_read_info->rest_read_uncompressed) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (fseek(pfile_in_zip_read_info->file, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + if (fread(pfile_in_zip_read_info->read_buffer,uReadThis,1, + pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if (pfile_in_zip_read_info->compression_method==0) + { + uInt uDoCopy,i ; + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (unzFile file,voidp buf,unsigned len) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (fseek(pfile_in_zip_read_info->file, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (fread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (unzFile file, char *szComment, uLong uSizeBuf) +{ + unz_s* s; + uLong uReadThis ; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (fseek(s->file,s->central_pos+22,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (fread(szComment,(uInt)uReadThis,1,s->file)!=1) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} diff --git a/ucon64/2.0/src/libdiscmage/unzip.h b/ucon64/2.0/src/libdiscmage/unzip.h new file mode 100644 index 0000000..fe3e9fc --- /dev/null +++ b/ucon64/2.0/src/libdiscmage/unzip.h @@ -0,0 +1,275 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 0.15 beta, Mar 19th, 1998, + + Copyright (C) 1998 Gilles Vollant + + This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + Encryption and multi volume ZipFile (span) are not supported. + Old compressions used by old PKZip 1.x are not supported + + THIS IS AN ALPHA VERSION. AT THIS STAGE OF DEVELOPPEMENT, SOMES API OR STRUCTURE + CAN CHANGE IN FUTURE VERSION !! + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ +/* for more info about .ZIP format, see + ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip */ + +#ifndef _unz_H +#define _unz_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\zlib\\zlib111.zip" or on an Unix computer + "zlib/zlib111.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _unz_H */ diff --git a/ucon64/2.0/src/mdntsc.txt b/ucon64/2.0/src/mdntsc.txt new file mode 100644 index 0000000..174807c --- /dev/null +++ b/ucon64/2.0/src/mdntsc.txt @@ -0,0 +1 @@ +# Put your NTSC protection search-and-defeat "codes" (patterns) here \ No newline at end of file diff --git a/ucon64/2.0/src/misc/archive.c b/ucon64/2.0/src/misc/archive.c new file mode 100644 index 0000000..c06c021 --- /dev/null +++ b/ucon64/2.0/src/misc/archive.c @@ -0,0 +1,677 @@ +/* +archive.c - g(un)zip and unzip support + +Copyright (c) 2001 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" // USE_ZLIB +#endif +#ifdef USE_ZLIB +#include +#include +#include +#include +#include +#include +#ifdef HAVE_BYTESWAP_H +#include +#else +#include "bswap.h" +#endif +#include "archive.h" +#include "misc.h" +#include "map.h" +#include "unzip.h" +#if defined DJGPP && defined DLL +#include "dxedll_priv.h" +#endif + + +#ifdef MAXBUFSIZE +#undef MAXBUFSIZE +#endif // MAXBUFSIZE +#define MAXBUFSIZE 32768 + + +extern int errno; + + +int +fsizeof (const char *filename) +// If USE_ZLIB is defined this function is very slow. Please avoid to use +// it much. +{ + FILE *file; + unsigned char magic[4] = { 0 }; + +#undef fopen +#undef fread +#undef fclose + if ((file = fopen (filename, "rb")) == NULL) + { + errno = ENOENT; + return -1; + } + fread (magic, 1, sizeof (magic), file); + fclose (file); +#define fopen fopen2 +#define fclose fclose2 +#define fread fread2 + + if (magic[0] == 0x1f && magic[1] == 0x8b && magic[2] == 0x08) + { // ID1, ID2 and CM. gzip uses Compression Method 8 + int size = 0; + + if ((file = (FILE *) gzopen (filename, "rb")) == NULL) + { // Shouldn't fail because we could open it with fopen() + errno = ENOENT; + return -1; + } +#if 1 + // This is not much faster than the other method + while (!gzeof (file)) + gzseek (file, 1024 * 1024, SEEK_CUR); + size = gztell (file); +#else + // Is there a more efficient way to determine the uncompressed size? + while ((bytesread = gzread (file, buf, MAXBUFSIZE)) > 0) + size += bytesread; +#endif + gzclose (file); + return size; + } + else if (magic[0] == 'P' && magic[1] == 'K' && magic[2] == 0x03 && magic[3] == 0x04) + { + unz_file_info info; + + if ((file = (FILE *) unzOpen (filename)) == NULL) + { + errno = ENOENT; + return -1; + } + unzip_goto_file (file, unzip_current_file_nr); + unzGetCurrentFileInfo (file, &info, NULL, 0, NULL, 0, NULL, 0); + unzClose (file); + + return info.uncompressed_size; + } + else + { + struct stat fstate; + + if (!stat (filename, &fstate)) + return fstate.st_size; + + errno = ENOENT; + return -1; + } +} + + +/* + The zlib functions gzwrite() and gzputc() write compressed data if the file + was opened with a "w" in the mode string, but we want to control whether we + write compressed data. Note that for the mode string "w+", zlib ignores the + '+'. zlib does the same for "r+". + Currently we never write compressed data. That is, no code fopen()s files + with a mode string containing 'f', 'h' or a number, but writing compressed + output does work. +*/ +st_map_t *fh_map = NULL; // associative array: file handle -> file mode + +typedef enum { FM_NORMAL, FM_GZIP, FM_ZIP, FM_UNDEF } fmode2_t; + +typedef struct st_finfo +{ + fmode2_t fmode; + int compressed; +} st_finfo_t; + +static st_finfo_t finfo_list[6] = { {FM_NORMAL, 0}, + {FM_NORMAL, 1}, // should never be used + {FM_GZIP, 0}, + {FM_GZIP, 1}, + {FM_ZIP, 0}, // should never be used + {FM_ZIP, 1} }; + +int unzip_current_file_nr = 0; + + +static void +init_fh_map (void) +{ + fh_map = map_create (20); // 20 simultaneous open files + map_put (fh_map, stdin, &finfo_list[0]); // should be enough to start with + map_put (fh_map, stdout, &finfo_list[0]); + map_put (fh_map, stderr, &finfo_list[0]); +} + + +static st_finfo_t * +get_finfo (FILE *file) +{ + st_finfo_t *finfo; + + if (fh_map == NULL) + init_fh_map (); + if ((finfo = (st_finfo_t *) map_get (fh_map, file)) == NULL) + { + fprintf (stderr, "\nINTERNAL ERROR: File pointer was not present in map (%p)\n", file); + map_dump (fh_map); + exit (1); + } + return finfo; +} + + +static fmode2_t +get_fmode (FILE *file) +{ + return get_finfo (file)->fmode; +} + + +int +unzip_get_number_entries (const char *filename) +{ + FILE *file; + unsigned char magic[4] = { 0 }; + +#undef fopen +#undef fread +#undef fclose + if ((file = fopen (filename, "rb")) == NULL) + { + errno = ENOENT; + return -1; + } + fread (magic, 1, sizeof (magic), file); + fclose (file); +#define fopen fopen2 +#define fclose fclose2 +#define fread fread2 + + if (magic[0] == 'P' && magic[1] == 'K' && magic[2] == 0x03 && magic[3] == 0x04) + { + unz_global_info info; + + file = (FILE *) unzOpen (filename); + unzGetGlobalInfo (file, &info); + unzClose (file); + return info.number_entry; + } + else + return -1; +} + + +int +unzip_goto_file (unzFile file, int file_index) +{ + int retval = unzGoToFirstFile (file), n = 0; + + if (file_index > 0) + while (n < file_index) + { + retval = unzGoToNextFile (file); + n++; + } + return retval; +} + + +static int +unzip_seek_helper (FILE *file, int offset) +{ + char buffer[MAXBUFSIZE]; + int n, tmp, pos = unztell (file); // returns ftell() of the "current file" + + if (pos == offset) + return 0; + else if (pos > offset) + { + unzCloseCurrentFile (file); + unzip_goto_file (file, unzip_current_file_nr); + unzOpenCurrentFile (file); + pos = 0; + } + n = offset - pos; + while (n > 0 && !unzeof (file)) + { + tmp = unzReadCurrentFile (file, buffer, n > MAXBUFSIZE ? MAXBUFSIZE : n); + if (tmp < 0) + return -1; + n -= tmp; + } + return n > 0 ? -1 : 0; +} + + +FILE * +fopen2 (const char *filename, const char *mode) +{ +#undef fopen + int n, len = strlen (mode), read = 0, compressed = 0; + fmode2_t fmode = FM_UNDEF; + st_finfo_t *finfo; + FILE *file = NULL; + +// printf ("opening %s", filename); + if (fh_map == NULL) + init_fh_map (); + + for (n = 0; n < len; n++) + { + switch (mode[n]) + { + case 'r': + read = 1; + break; + case 'f': + case 'h': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + fmode = FM_GZIP; + break; + case 'w': + case 'a': + fmode = FM_NORMAL; + break; + case '+': + if (fmode == FM_UNDEF) + fmode = FM_NORMAL; + break; + } + } + + if (read) + { +#undef fread +#undef fclose + unsigned char magic[4] = { 0 }; + FILE *fh; + + // TODO?: check if mode is valid for fopen(), i.e., no 'f', 'h' or number + if ((fh = fopen (filename, mode)) != NULL) + { + fread (magic, sizeof (magic), 1, fh); + if (magic[0] == 0x1f && magic[1] == 0x8b && magic[2] == 0x08) + { // ID1, ID2 and CM. gzip uses Compression Method 8 + fmode = FM_GZIP; + compressed = 1; + } + else if (magic[0] == 'P' && magic[1] == 'K' && + magic[2] == 0x03 && magic[3] == 0x04) + { + fmode = FM_ZIP; + compressed = 1; + } + else + /* + Files that are opened with mode "r+" will probably be written to. + zlib doesn't support mode "r+", so we have to use FM_NORMAL. + Mode "r" doesn't require FM_NORMAL and FM_GZIP works, but we + shouldn't introduce needless overhead. + */ + fmode = FM_NORMAL; + fclose (fh); + } +#define fread fread2 +#define fclose fclose2 + } + + if (fmode == FM_NORMAL) + file = fopen (filename, mode); + else if (fmode == FM_GZIP) + file = (FILE *) gzopen (filename, mode); + else if (fmode == FM_ZIP) + { + file = (FILE *) unzOpen (filename); + if (file != NULL) + { + unzip_goto_file (file, unzip_current_file_nr); + unzOpenCurrentFile (file); + } + } + + if (file == NULL) + return NULL; + + finfo = &finfo_list[fmode * 2 + compressed]; + fh_map = map_put (fh_map, file, finfo); + +/* + printf (", ptr = %p, mode = %s, fmode = %s\n", file, mode, + fmode == FM_NORMAL ? "FM_NORMAL" : + (fmode == FM_GZIP ? "FM_GZIP" : + (fmode == FM_ZIP ? "FM_ZIP" : "FM_UNDEF"))); + map_dump (fh_map); +*/ + return file; +#define fopen fopen2 +} + + +int +fclose2 (FILE *file) +{ +#undef fclose + fmode2_t fmode = get_fmode (file); + + map_del (fh_map, file); + if (fmode == FM_NORMAL) + return fclose (file); + else if (fmode == FM_GZIP) + return gzclose (file); + else if (fmode == FM_ZIP) + { + unzCloseCurrentFile (file); + return unzClose (file); + } + else + return EOF; +#define fclose fclose2 +} + + +int +fseek2 (FILE *file, long offset, int mode) +{ +#undef fseek + st_finfo_t *finfo = get_finfo (file); + +/* +// if (fmode != FM_NORMAL) + printf ("fmode = %s\n", finfo->fmode == FM_NORMAL ? "FM_NORMAL" : + (finfo->fmode == FM_GZIP ? "FM_GZIP" : + (finfo->fmode == FM_ZIP ? "FM_ZIP" : "FM_UNDEF"))); +*/ + + if (finfo->fmode == FM_NORMAL) + return fseek (file, offset, mode); + else if (finfo->fmode == FM_GZIP) + { + if (mode == SEEK_END) // zlib doesn't support SEEK_END + { + // Note that this is _slow_... + while (!gzeof (file)) + { + gzgetc (file); // necessary for _uncompressed_ files in order to set EOF + gzseek (file, 1024 * 1024, SEEK_CUR); + } + offset += gztell (file); + mode = SEEK_SET; + } + /* + FUCKING zlib documentation! It took me around 4 hours of debugging time + to find out that the doc is wrong! From the doc: + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) + That is not true for uncompressed files. gzrewind() doesn't change the + file pointer for uncompressed files in the ports I tested (zlib 1.1.3, + DJGPP, Cygwin & GNU/Linux). It clears the EOF indicator. + */ + if (!finfo->compressed) + gzrewind (file); + return gzseek (file, offset, mode) == -1 ? -1 : 0; + } + else if (finfo->fmode == FM_ZIP) + { + int base; + if (mode != SEEK_SET && mode != SEEK_CUR && mode != SEEK_END) + { + errno = EINVAL; + return -1; + } + if (mode == SEEK_SET) + base = 0; + else if (mode == SEEK_CUR) + base = unztell (file); + else // mode == SEEK_END + { + unz_file_info info; + + unzip_goto_file (file, unzip_current_file_nr); + unzGetCurrentFileInfo (file, &info, NULL, 0, NULL, 0, NULL, 0); + base = info.uncompressed_size; + } + return unzip_seek_helper (file, base + offset); + } + return -1; +#define fseek fseek2 +} + + +size_t +fread2 (void *buffer, size_t size, size_t number, FILE *file) +{ +#undef fread + fmode2_t fmode = get_fmode (file); + + if (size == 0 || number == 0) + return 0; + + if (fmode == FM_NORMAL) + return fread (buffer, size, number, file); + else if (fmode == FM_GZIP) + { + int n = gzread (file, buffer, number * size); + return n / size; + } + else if (fmode == FM_ZIP) + { + int n = unzReadCurrentFile (file, buffer, number * size); + return n / size; + } + return 0; +#define fread fread2 +} + + +int +fgetc2 (FILE *file) +{ +#undef fgetc + fmode2_t fmode = get_fmode (file); + + if (fmode == FM_NORMAL) + return fgetc (file); + else if (fmode == FM_GZIP) + return gzgetc (file); + else if (fmode == FM_ZIP) + { + char c; + int retval = unzReadCurrentFile (file, &c, 1); + return retval <= 0 ? EOF : c & 0xff; // avoid sign bit extension + } + else + return EOF; +#define fgetc fgetc2 +} + + +char * +fgets2 (char *buffer, int maxlength, FILE *file) +{ +#undef fgets + fmode2_t fmode = get_fmode (file); + + if (fmode == FM_NORMAL) + return fgets (buffer, maxlength, file); + else if (fmode == FM_GZIP) + { + char *retval = gzgets (file, buffer, maxlength); + return retval == Z_NULL ? NULL : retval; + } + else if (fmode == FM_ZIP) + { + int n = 0, c = 0; + while (n < maxlength - 1 && (c = fgetc (file)) != EOF) + { + buffer[n] = c; // '\n' must also be stored in buffer + n++; + if (c == '\n') + { + buffer[n] = 0; + break; + } + } + if (n >= maxlength - 1 || c == EOF) + buffer[n] = 0; + return n > 0 ? buffer : NULL; + } + else + return NULL; +#define fgets fgets2 +} + + +int +feof2 (FILE *file) +{ +#undef feof + fmode2_t fmode = get_fmode (file); + + if (fmode == FM_NORMAL) + return feof (file); + else if (fmode == FM_GZIP) + return gzeof (file); + else if (fmode == FM_ZIP) + return unzeof (file); // returns feof() of the "current file" + else + return -1; +#define feof feof2 +} + + +size_t +fwrite2 (const void *buffer, size_t size, size_t number, FILE *file) +{ +#undef fwrite + fmode2_t fmode = get_fmode (file); + + if (size == 0 || number == 0) + return 0; + + if (fmode == FM_NORMAL) + return fwrite (buffer, size, number, file); + else if (fmode == FM_GZIP) + { + int n = gzwrite (file, (void *) buffer, number * size); + return n / size; + } + else + return 0; // writing to zip files is not supported +#define fwrite fwrite2 +} + + +int +fputc2 (int character, FILE *file) +{ +#undef fputc + fmode2_t fmode = get_fmode (file); + + if (fmode == FM_NORMAL) + return fputc (character, file); + else if (fmode == FM_GZIP) + return gzputc (file, character); + else + return EOF; // writing to zip files is not supported +#define fputc fputc2 +} + + +long +ftell2 (FILE *file) +{ +#undef ftell + fmode2_t fmode = get_fmode (file); + + if (fmode == FM_NORMAL) + return ftell (file); + else if (fmode == FM_GZIP) + return gztell (file); + else if (fmode == FM_ZIP) + return unztell (file); // returns ftell() of the "current file" + else + return -1; +#define ftell ftell2 +} + + +void +rewind2 (FILE *file) +{ + fseek2 (file, 0, SEEK_SET); +} + + +FILE * +popen2 (const char *command, const char *mode) +{ +#undef popen +#if defined _WIN32 || defined AMIGA +#define popen _popen +#endif + int compressed = 0; + fmode2_t fmode = FM_NORMAL; + st_finfo_t *finfo; + FILE *file; + + if (fh_map == NULL) + init_fh_map (); + + file = popen (command, mode); + if ((file = popen (command, mode)) == NULL) + return NULL; + + finfo = &finfo_list[fmode * 2 + compressed]; + fh_map = map_put (fh_map, file, finfo); + + return file; +#undef popen +#define popen popen2 +} + + +int +pclose2 (FILE *stream) +{ +#undef pclose +#if defined _WIN32 || defined AMIGA +#define pclose _pclose +#endif + fmode2_t fmode = get_fmode (stream); + + if (fmode == FM_NORMAL) + return pclose (stream); + else if (fmode == FM_GZIP || fmode == FM_ZIP) + return -1; + else + return -1; +#undef pclose +#define pclose pclose2 +} +#endif // USE_ZLIB diff --git a/ucon64/2.0/src/misc/archive.h b/ucon64/2.0/src/misc/archive.h new file mode 100644 index 0000000..78bff5e --- /dev/null +++ b/ucon64/2.0/src/misc/archive.h @@ -0,0 +1,85 @@ +/* +archive.h - g(un)zip and unzip support + +Copyright (c) 2001 - 2003 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef MISC_ARCHIVE_H +#define MISC_ARCHIVE_H + +#ifdef HAVE_CONFIG_H +#include "config.h" // USE_ZLIB +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef USE_ZLIB +// make sure ZLIB support is enabled everywhere +//#warning USE_ZLIB is defined + +#include +#include +#include "unzip.h" + +extern FILE *fopen2 (const char *filename, const char *mode); +extern int fclose2 (FILE *file); +extern int fseek2 (FILE *file, long offset, int mode); +extern size_t fread2 (void *buffer, size_t size, size_t number, FILE *file); +extern int fgetc2 (FILE *file); +extern char *fgets2 (char *buffer, int maxlength, FILE *file); +extern int feof2 (FILE *file); +extern size_t fwrite2 (const void *buffer, size_t size, size_t number, FILE *file); +extern int fputc2 (int character, FILE *file); +extern long ftell2 (FILE *file); +extern void rewind2 (FILE *file); +extern FILE *popen2 (const char *command, const char *mode); +extern int pclose2 (FILE *stream); + +extern int fsizeof2 (const char *filename); + +#undef feof // necessary on (at least) Cygwin + +#define fopen(FILE, MODE) fopen2(FILE, MODE) +#define fclose(FILE) fclose2(FILE) +#define fseek(FILE, OFFSET, MODE) fseek2(FILE, OFFSET, MODE) +#define fread(BUF, SIZE, NUM, FILE) fread2(BUF, SIZE, NUM, FILE) +#define fgetc(FILE) fgetc2(FILE) +#define fgets(BUF, MAXLEN, FILE) fgets2(BUF, MAXLEN, FILE) +#define feof(FILE) feof2(FILE) +#define fwrite(BUF, SIZE, NUM, FILE) fwrite2(BUF, SIZE, NUM, FILE) +#define fputc(CHAR, FILE) fputc2(CHAR, FILE) +#define ftell(FILE) ftell2(FILE) +#define rewind(FILE) rewind2(FILE) +#undef popen +#define popen(COMMAND, MODE) popen2(COMMAND, MODE) +#undef pclose +#define pclose(FILE) pclose2(FILE) +#define fsizeof(FILENAME) fsizeof2(FILENAME) + +// Returns the number of files in the "central dir of this disk" or -1 if +// filename is not a ZIP file or an error occured. +extern int unzip_get_number_entries (const char *filename); +extern int unzip_goto_file (unzFile file, int file_index); +extern int unzip_current_file_nr; +#endif // USE_ZLIB + +#ifdef __cplusplus +} +#endif +#endif // MISC_ARCHIVE_H diff --git a/ucon64/2.0/src/misc/bswap.h b/ucon64/2.0/src/misc/bswap.h new file mode 100644 index 0000000..918722a --- /dev/null +++ b/ucon64/2.0/src/misc/bswap.h @@ -0,0 +1,207 @@ +/* +bswap.h - bswap functions + +Copyright (c) 1999 - 2004 NoisyB +Copyright (c) 2001 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef MISC_BSWAP_H +#define MISC_BSWAP_H + +#ifdef HAVE_CONFIG_H +#include "config.h" // HAVE_BYTESWAP_H +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_INTTYPES_H +#include +#else // __MSDOS__, _WIN32 (VC++) +#include "itypes.h" +#endif + + +/* + bswap_16() 12 21 + bswap_32() 1234 4321 + bswap_64() 12345678 87654321 + + wswap_32() 1234 3412 + wswap_64() 12345678 78563412 +*/ + +#ifdef _MSC_VER +// Visual C++ doesn't allow inline in C source code +#define inline __inline +#endif + +#ifdef HAVE_BYTESWAP_H +#include +#else + + +static inline uint16_t +bswap_16 (uint16_t x) +{ +#if 1 + uint8_t *ptr = (uint8_t *) &x, tmp; + tmp = ptr[0]; + ptr[0] = ptr[1]; + ptr[1] = tmp; + return x; +#else + return ((((x) & 0xff00) >> 8) | + (((x) & 0x00ff) << 8)); +#endif +} + + +static inline uint32_t +bswap_32 (uint32_t x) +{ +#if 1 + uint8_t *ptr = (uint8_t *) &x, tmp; + tmp = ptr[0]; + ptr[0] = ptr[3]; + ptr[3] = tmp; + tmp = ptr[1]; + ptr[1] = ptr[2]; + ptr[2] = tmp; + return x; +#else + return ((((x) & 0xff000000) >> 24) | + (((x) & 0x00ff0000) >> 8) | + (((x) & 0x0000ff00) << 8) | + (((x) & 0x000000ff) << 24)); +#endif +} + + +static inline uint64_t +bswap_64 (uint64_t x) +{ +#if 1 + uint8_t *ptr = (uint8_t *) &x, tmp; + tmp = ptr[0]; + ptr[0] = ptr[7]; + ptr[7] = tmp; + tmp = ptr[1]; + ptr[1] = ptr[6]; + ptr[6] = tmp; + tmp = ptr[2]; + ptr[2] = ptr[5]; + ptr[5] = tmp; + tmp = ptr[3]; + ptr[3] = ptr[4]; + ptr[4] = tmp; + return x; +#else + return ((((x) & 0xff00000000000000ull) >> 56) | + (((x) & 0x00ff000000000000ull) >> 40) | + (((x) & 0x0000ff0000000000ull) >> 24) | + (((x) & 0x000000ff00000000ull) >> 8) | + (((x) & 0x00000000ff000000ull) << 8) | + (((x) & 0x0000000000ff0000ull) << 24) | + (((x) & 0x000000000000ff00ull) << 40) | + (((x) & 0x00000000000000ffull) << 56)); +#endif +} +#endif // HAVE_BYTESWAP_H + + +#ifdef WORDS_BIGENDIAN +#undef WORDS_BIGENDIAN +#endif + +#if defined _LIBC || defined __GLIBC__ +#include +#if __BYTE_ORDER == __BIG_ENDIAN +#define WORDS_BIGENDIAN 1 +#endif +#elif defined AMIGA || defined __sparc__ || defined __BIG_ENDIAN__ || \ + defined __APPLE__ +#define WORDS_BIGENDIAN 1 +#endif + +#ifdef WORDS_BIGENDIAN +#define me2be_16 +#define me2be_32 +#define me2be_64 +#define me2le_16 bswap_16 +#define me2le_32 bswap_32 +#define me2le_64 bswap_64 +#else +#define me2be_16 bswap_16 +#define me2be_32 bswap_32 +#define me2be_64 bswap_64 +#define me2le_16 +#define me2le_32 +#define me2le_64 +#endif +#define be2me_16 me2be_16 +#define be2me_32 me2be_32 +#define be2me_64 me2be_64 +#define le2me_16 me2le_16 +#define le2me_32 me2le_32 +#define le2me_64 me2le_64 + + +static inline uint32_t +wswap_32 (uint32_t x) +{ +#if 0 + uint16_t *ptr = (uint16_t *) &x, tmp; + tmp = ptr[0]; + ptr[0] = ptr[1]; + ptr[1] = tmp; + return x; +#else + return ((((x) & 0xffff0000) >> 16) | + (((x) & 0x0000ffff) << 16)); +#endif +} + + +#if 0 +static inline uint64_t +wswap_64 (uint64_t x) +{ +#if 1 + uint16_t *ptr = (uint16_t *) &x, tmp; + tmp = ptr[0]; + ptr[0] = ptr[3]; + ptr[3] = tmp; + tmp = ptr[1]; + ptr[1] = ptr[2]; + ptr[2] = tmp; + return x; +#else + return ((((x) & 0xffff000000000000ull) >> 48) | + (((x) & 0x0000ffff00000000ull) >> 16) | + (((x) & 0x00000000ffff0000ull) << 16) | + (((x) & 0x000000000000ffffull) << 48)); +#endif +} +#endif + + +#ifdef __cplusplus +} +#endif +#endif // MISC_BSWAP_H diff --git a/ucon64/2.0/src/misc/chksum.c b/ucon64/2.0/src/misc/chksum.c new file mode 100644 index 0000000..e6ce926 --- /dev/null +++ b/ucon64/2.0/src/misc/chksum.c @@ -0,0 +1,556 @@ +/* +chksum.c - miscellaneous checksum functions + +Copyright (c) 1999 - 2004 NoisyB +Copyright (c) 2001 - 2004 dbjh + +sha1 - Copyright (c) 2002, Dr Brian Gladman , Worcester, UK. + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + +MD5 - Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. + License to copy and use this software is granted provided that + it is identified as the "RSA Data Security, Inc. MD5 Message + Digest Algorithm" in all material mentioning or referencing this + software or this function. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" // USE_ZLIB +#endif +#include +#include +#include +//#ifdef HAVE_BYTESWAP_H +//#include +//#else +#include "bswap.h" +//#endif +#ifdef USE_ZLIB +#include +#include "unzip.h" +#endif +#include "misc.h" +#include "chksum.h" + + +#if (!defined TRUE || !defined FALSE) +#define FALSE 0 +#define TRUE (!FALSE) +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifdef WORDS_BIGENDIAN +#undef WORDS_BIGENDIAN +#endif + + +#if defined _LIBC || defined __GLIBC__ + #include + #if __BYTE_ORDER == __BIG_ENDIAN + #define WORDS_BIGENDIAN 1 + #endif +#elif defined AMIGA || defined __sparc__ || defined __BIG_ENDIAN__ || \ + defined __APPLE__ + #define WORDS_BIGENDIAN 1 +#endif + + +#ifndef MAXBUFSIZE +#define MAXBUFSIZE 32768 +#endif // MAXBUFSIZE + + +#define ROTL32(x,n) (((x) << (n)) | ((x) >> (32 - (n)))) + +#define SHA1_BLOCK_SIZE 64 +#define SHA1_DIGEST_SIZE 20 +#define SHA2_GOOD 0 +#define SHA2_BAD 1 +#define SHA1_MASK (SHA1_BLOCK_SIZE - 1) + + +void +sha1_compile (s_sha1_ctx_t ctx[1]) +{ +#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) +#define PARITY(x,y,z) ((x) ^ (y) ^ (z)) +#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + + uint32_t w[80], i, a, b, c, d, e, t; + + /* + note that words are compiled from the buffer into 32-bit + words in big-endian order so an order reversal is needed + here on little endian machines + */ + for (i = 0; i < SHA1_BLOCK_SIZE / 4; ++i) + w[i] = me2be_32 (ctx->wbuf[i]); + + for (i = SHA1_BLOCK_SIZE / 4; i < 80; ++i) + w[i] = ROTL32 (w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1); + + a = ctx->hash[0]; + b = ctx->hash[1]; + c = ctx->hash[2]; + d = ctx->hash[3]; + e = ctx->hash[4]; + + for (i = 0; i < 80; i++) + { + t = a; + a = ROTL32 (a, 5) + e + w[i]; + if (i < 20) + a += CH (b, c, d) + 0x5a827999; + else if (i < 40) + a += PARITY (b, c, d) + 0x6ed9eba1; + else if (i < 60) + a += MAJ (b, c, d) + 0x8f1bbcdc; + else if (i < 80) + a += PARITY (b, c, d) + 0xca62c1d6; + e = d; + d = c; + c = ROTL32 (b, 30); + b = t; + } + + ctx->hash[0] += a; + ctx->hash[1] += b; + ctx->hash[2] += c; + ctx->hash[3] += d; + ctx->hash[4] += e; +} + + +void +sha1_begin (s_sha1_ctx_t ctx[1]) +{ + ctx->count[0] = ctx->count[1] = 0; + ctx->hash[0] = 0x67452301; + ctx->hash[1] = 0xefcdab89; + ctx->hash[2] = 0x98badcfe; + ctx->hash[3] = 0x10325476; + ctx->hash[4] = 0xc3d2e1f0; +} + + +void +sha1 (s_sha1_ctx_t ctx[1], const unsigned char data[], unsigned int len) +{ + uint32_t pos = (uint32_t) (ctx->count[0] & SHA1_MASK), + space = SHA1_BLOCK_SIZE - pos; + const unsigned char *sp = data; + + if ((ctx->count[0] += len) < len) + ++(ctx->count[1]); + + while (len >= space) // transfer whole blocks while possible + { + memcpy (((unsigned char *) ctx->wbuf) + pos, sp, space); + sp += space; + len -= space; + space = SHA1_BLOCK_SIZE; + pos = 0; + sha1_compile (ctx); + } + + memcpy (((unsigned char *) ctx->wbuf) + pos, sp, len); +} + + +void +sha1_end (unsigned char hval[], s_sha1_ctx_t ctx[1]) +{ +#ifdef WORDS_BIGENDIAN + const uint32_t mask[4] = { 0x00000000, 0xff000000, 0xffff0000, 0xffffff00 }; + const uint32_t bits[4] = { 0x80000000, 0x00800000, 0x00008000, 0x00000080 }; +#else + const uint32_t mask[4] = { 0x00000000, 0x000000ff, 0x0000ffff, 0x00ffffff }; + const uint32_t bits[4] = { 0x00000080, 0x00008000, 0x00800000, 0x80000000 }; +#endif + uint32_t i = (uint32_t) (ctx->count[0] & SHA1_MASK); + + /* + mask out the rest of any partial 32-bit word and then set + the next byte to 0x80. On big-endian machines any bytes in + the buffer will be at the top end of 32 bit words, on little + endian machines they will be at the bottom. Hence the AND + and OR masks above are reversed for little endian systems + Note that we can always add the first padding byte at this + because the buffer always contains at least one empty slot + */ + ctx->wbuf[i >> 2] = (ctx->wbuf[i >> 2] & mask[i & 3]) | bits[i & 3]; + + /* + we need 9 or more empty positions, one for the padding byte + (above) and eight for the length count. If there is not + enough space pad and empty the buffer + */ + if (i > SHA1_BLOCK_SIZE - 9) + { + if (i < 60) + ctx->wbuf[15] = 0; + sha1_compile (ctx); + i = 0; + } + else // compute a word index for the empty buffer positions + i = (i >> 2) + 1; + + while (i < 14) // and zero pad all but last two positions + ctx->wbuf[i++] = 0; + + // assemble the eight byte counter in big-endian format + ctx->wbuf[14] = me2be_32 ((ctx->count[1] << 3) | (ctx->count[0] >> 29)); + ctx->wbuf[15] = me2be_32 (ctx->count[0] << 3); + + sha1_compile (ctx); + + // extract the hash value as bytes in case the hash buffer is + // misaligned for 32-bit words + for (i = 0; i < SHA1_DIGEST_SIZE; ++i) + hval[i] = (unsigned char) (ctx->hash[i >> 2] >> 8 * (~i & 3)); +} + + +// MD5 +static void md5_transform (uint32_t *buf, uint32_t *in); + + +// Padding +static unsigned char md5_padding[64] = + { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + +// MD5_F, MD5_G and MD5_H are basic MD5 functions: selection, majority, parity +#define MD5_F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define MD5_G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define MD5_H(x, y, z) ((x) ^ (y) ^ (z)) +#define MD5_I(x, y, z) ((y) ^ ((x) | (~z))) + +// MD5_FF, MD5_GG, MD5_HH, and MD5_II transformations for rounds 1, 2, 3, and 4 +// Rotation is separate from addition to prevent recomputation +#define MD5_FF(a, b, c, d, x, s, ac) {(a) += MD5_F ((b), (c), (d)) + (x) + (uint32_t)(ac); (a) = ROTL32 ((a), (s)); (a) += (b); } +#define MD5_GG(a, b, c, d, x, s, ac) {(a) += MD5_G ((b), (c), (d)) + (x) + (uint32_t)(ac); (a) = ROTL32 ((a), (s)); (a) += (b); } +#define MD5_HH(a, b, c, d, x, s, ac) {(a) += MD5_H ((b), (c), (d)) + (x) + (uint32_t)(ac); (a) = ROTL32 ((a), (s)); (a) += (b); } +#define MD5_II(a, b, c, d, x, s, ac) {(a) += MD5_I ((b), (c), (d)) + (x) + (uint32_t)(ac); (a) = ROTL32 ((a), (s)); (a) += (b); } + +// constants for transformation +#define MD5_S11 7 // round 1 +#define MD5_S12 12 +#define MD5_S13 17 +#define MD5_S14 22 +#define MD5_S21 5 // round 2 +#define MD5_S22 9 +#define MD5_S23 14 +#define MD5_S24 20 +#define MD5_S31 4 // round 3 +#define MD5_S32 11 +#define MD5_S33 16 +#define MD5_S34 23 +#define MD5_S41 6 // round 4 +#define MD5_S42 10 +#define MD5_S43 15 +#define MD5_S44 21 + + +// basic MD5 step, md5_transform buf based on in +static void +md5_transform (uint32_t *buf, uint32_t *in) +{ + uint32_t a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + // round 1 + MD5_FF (a, b, c, d, in[0], MD5_S11, (uint32_t) 3614090360u); // 1 + MD5_FF (d, a, b, c, in[1], MD5_S12, (uint32_t) 3905402710u); // 2 + MD5_FF (c, d, a, b, in[2], MD5_S13, (uint32_t) 606105819u); // 3 + MD5_FF (b, c, d, a, in[3], MD5_S14, (uint32_t) 3250441966u); // 4 + MD5_FF (a, b, c, d, in[4], MD5_S11, (uint32_t) 4118548399u); // 5 + MD5_FF (d, a, b, c, in[5], MD5_S12, (uint32_t) 1200080426u); // 6 + MD5_FF (c, d, a, b, in[6], MD5_S13, (uint32_t) 2821735955u); // 7 + MD5_FF (b, c, d, a, in[7], MD5_S14, (uint32_t) 4249261313u); // 8 + MD5_FF (a, b, c, d, in[8], MD5_S11, (uint32_t) 1770035416u); // 9 + MD5_FF (d, a, b, c, in[9], MD5_S12, (uint32_t) 2336552879u); // 10 + MD5_FF (c, d, a, b, in[10], MD5_S13, (uint32_t) 4294925233u); // 11 + MD5_FF (b, c, d, a, in[11], MD5_S14, (uint32_t) 2304563134u); // 12 + MD5_FF (a, b, c, d, in[12], MD5_S11, (uint32_t) 1804603682u); // 13 + MD5_FF (d, a, b, c, in[13], MD5_S12, (uint32_t) 4254626195u); // 14 + MD5_FF (c, d, a, b, in[14], MD5_S13, (uint32_t) 2792965006u); // 15 + MD5_FF (b, c, d, a, in[15], MD5_S14, (uint32_t) 1236535329u); // 16 + + // round 2 + MD5_GG (a, b, c, d, in[1], MD5_S21, (uint32_t) 4129170786u); // 17 + MD5_GG (d, a, b, c, in[6], MD5_S22, (uint32_t) 3225465664u); // 18 + MD5_GG (c, d, a, b, in[11], MD5_S23, (uint32_t) 643717713u); // 19 + MD5_GG (b, c, d, a, in[0], MD5_S24, (uint32_t) 3921069994u); // 20 + MD5_GG (a, b, c, d, in[5], MD5_S21, (uint32_t) 3593408605u); // 21 + MD5_GG (d, a, b, c, in[10], MD5_S22, (uint32_t) 38016083u); // 22 + MD5_GG (c, d, a, b, in[15], MD5_S23, (uint32_t) 3634488961u); // 23 + MD5_GG (b, c, d, a, in[4], MD5_S24, (uint32_t) 3889429448u); // 24 + MD5_GG (a, b, c, d, in[9], MD5_S21, (uint32_t) 568446438u); // 25 + MD5_GG (d, a, b, c, in[14], MD5_S22, (uint32_t) 3275163606u); // 26 + MD5_GG (c, d, a, b, in[3], MD5_S23, (uint32_t) 4107603335u); // 27 + MD5_GG (b, c, d, a, in[8], MD5_S24, (uint32_t) 1163531501u); // 28 + MD5_GG (a, b, c, d, in[13], MD5_S21, (uint32_t) 2850285829u); // 29 + MD5_GG (d, a, b, c, in[2], MD5_S22, (uint32_t) 4243563512u); // 30 + MD5_GG (c, d, a, b, in[7], MD5_S23, (uint32_t) 1735328473u); // 31 + MD5_GG (b, c, d, a, in[12], MD5_S24, (uint32_t) 2368359562u); // 32 + + // round 3 + MD5_HH (a, b, c, d, in[5], MD5_S31, (uint32_t) 4294588738u); // 33 + MD5_HH (d, a, b, c, in[8], MD5_S32, (uint32_t) 2272392833u); // 34 + MD5_HH (c, d, a, b, in[11], MD5_S33, (uint32_t) 1839030562u); // 35 + MD5_HH (b, c, d, a, in[14], MD5_S34, (uint32_t) 4259657740u); // 36 + MD5_HH (a, b, c, d, in[1], MD5_S31, (uint32_t) 2763975236u); // 37 + MD5_HH (d, a, b, c, in[4], MD5_S32, (uint32_t) 1272893353u); // 38 + MD5_HH (c, d, a, b, in[7], MD5_S33, (uint32_t) 4139469664u); // 39 + MD5_HH (b, c, d, a, in[10], MD5_S34, (uint32_t) 3200236656u); // 40 + MD5_HH (a, b, c, d, in[13], MD5_S31, (uint32_t) 681279174u); // 41 + MD5_HH (d, a, b, c, in[0], MD5_S32, (uint32_t) 3936430074u); // 42 + MD5_HH (c, d, a, b, in[3], MD5_S33, (uint32_t) 3572445317u); // 43 + MD5_HH (b, c, d, a, in[6], MD5_S34, (uint32_t) 76029189u); // 44 + MD5_HH (a, b, c, d, in[9], MD5_S31, (uint32_t) 3654602809u); // 45 + MD5_HH (d, a, b, c, in[12], MD5_S32, (uint32_t) 3873151461u); // 46 + MD5_HH (c, d, a, b, in[15], MD5_S33, (uint32_t) 530742520u); // 47 + MD5_HH (b, c, d, a, in[2], MD5_S34, (uint32_t) 3299628645u); // 48 + + // round 4 + MD5_II (a, b, c, d, in[0], MD5_S41, (uint32_t) 4096336452u); // 49 + MD5_II (d, a, b, c, in[7], MD5_S42, (uint32_t) 1126891415u); // 50 + MD5_II (c, d, a, b, in[14], MD5_S43, (uint32_t) 2878612391u); // 51 + MD5_II (b, c, d, a, in[5], MD5_S44, (uint32_t) 4237533241u); // 52 + MD5_II (a, b, c, d, in[12], MD5_S41, (uint32_t) 1700485571u); // 53 + MD5_II (d, a, b, c, in[3], MD5_S42, (uint32_t) 2399980690u); // 54 + MD5_II (c, d, a, b, in[10], MD5_S43, (uint32_t) 4293915773u); // 55 + MD5_II (b, c, d, a, in[1], MD5_S44, (uint32_t) 2240044497u); // 56 + MD5_II (a, b, c, d, in[8], MD5_S41, (uint32_t) 1873313359u); // 57 + MD5_II (d, a, b, c, in[15], MD5_S42, (uint32_t) 4264355552u); // 58 + MD5_II (c, d, a, b, in[6], MD5_S43, (uint32_t) 2734768916u); // 59 + MD5_II (b, c, d, a, in[13], MD5_S44, (uint32_t) 1309151649u); // 60 + MD5_II (a, b, c, d, in[4], MD5_S41, (uint32_t) 4149444226u); // 61 + MD5_II (d, a, b, c, in[11], MD5_S42, (uint32_t) 3174756917u); // 62 + MD5_II (c, d, a, b, in[2], MD5_S43, (uint32_t) 718787259u); // 63 + MD5_II (b, c, d, a, in[9], MD5_S44, (uint32_t) 3951481745u); // 64 + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + + +// set pseudoRandomNumber to zero for RFC MD5 implementation +void +md5_init (s_md5_ctx_t *mdContext, unsigned long pseudoRandomNumber) +{ + mdContext->i[0] = mdContext->i[1] = (uint32_t) 0; + + // Load magic initialization constants + mdContext->buf[0] = (uint32_t) 0x67452301 + (pseudoRandomNumber * 11); + mdContext->buf[1] = (uint32_t) 0xefcdab89 + (pseudoRandomNumber * 71); + mdContext->buf[2] = (uint32_t) 0x98badcfe + (pseudoRandomNumber * 37); + mdContext->buf[3] = (uint32_t) 0x10325476 + (pseudoRandomNumber * 97); +} + + +void +md5_update (s_md5_ctx_t *mdContext, unsigned char *inBuf, unsigned int inLen) +{ + uint32_t in[16]; + int mdi = 0; + unsigned int i = 0, ii = 0; + + // Compute number of bytes mod 64 + mdi = (int) ((mdContext->i[0] >> 3) & 0x3F); + + // Update number of bits + if ((mdContext->i[0] + ((uint32_t) inLen << 3)) < mdContext->i[0]) + mdContext->i[1]++; + mdContext->i[0] += ((uint32_t) inLen << 3); + mdContext->i[1] += ((uint32_t) inLen >> 29); + + while (inLen--) + { + // Add new character to buffer, increment mdi + mdContext->in[mdi++] = *inBuf++; + + // Transform if necessary + if (mdi == 0x40) + { + for (i = 0, ii = 0; i < 16; i++, ii += 4) + in[i] = (((uint32_t) mdContext->in[ii + 3]) << 24) | + (((uint32_t) mdContext->in[ii + 2]) << 16) | + (((uint32_t) mdContext->in[ii + 1]) << 8) | + ((uint32_t) mdContext->in[ii]); + + md5_transform (mdContext->buf, in); + mdi = 0; + } + } +} + + +void +md5_final (s_md5_ctx_t *mdContext) +{ + uint32_t in[16]; + int mdi = 0; + unsigned int i = 0, ii = 0, padLen = 0; + + // Save number of bits + in[14] = mdContext->i[0]; + in[15] = mdContext->i[1]; + + // Compute number of bytes mod 64 + mdi = (int) ((mdContext->i[0] >> 3) & 0x3F); + + // Pad out to 56 mod 64 + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + md5_update (mdContext, md5_padding, padLen); + + // Append length in bits and transform + for (i = 0, ii = 0; i < 14; i++, ii += 4) + in[i] = (((uint32_t) mdContext->in[ii + 3]) << 24) | + (((uint32_t) mdContext->in[ii + 2]) << 16) | + (((uint32_t) mdContext->in[ii + 1]) << 8) | ((uint32_t) mdContext->in[ii]); + + md5_transform (mdContext->buf, in); + + // Store buffer in digest + for (i = 0, ii = 0; i < 4; i++, ii += 4) + { + mdContext->digest[ii] = (unsigned char) (mdContext->buf[i] & 0xFF); + mdContext->digest[ii + 1] = + (unsigned char) ((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii + 2] = + (unsigned char) ((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii + 3] = + (unsigned char) ((mdContext->buf[i] >> 24) & 0xFF); + } +} + + +// CRC16 +//#define WITH_CRC16 // currently not used + +#if !defined USE_ZLIB || defined WITH_CRC16 + +#define CRC16_POLYNOMIAL 0xa001 +#define CRC32_POLYNOMIAL 0xedb88320 + + +void +init_crc_table (void *table, unsigned int polynomial) +// works for crc16 and crc32 +{ + unsigned int crc, i, j; + + for (i = 0; i < 256; i++) + { + crc = i; + for (j = 8; j > 0; j--) + if (crc & 1) + crc = (crc >> 1) ^ polynomial; + else + crc >>= 1; + + if (polynomial == CRC32_POLYNOMIAL) + ((unsigned int *) table)[i] = crc; + else + ((unsigned short *) table)[i] = (unsigned short) crc; + } +} +#endif + + +#ifdef WITH_CRC16 + +static unsigned short *crc16_table = NULL; + + +static void +free_crc16_table (void) +{ + free (crc16_table); + crc16_table = NULL; +} + + +unsigned short +chksum_crc16 (unsigned short crc, const void *buffer, unsigned int size) +{ + unsigned char *p = (unsigned char *) buffer; + + if (!crc16_table) + { + crc16_table = (unsigned short *) malloc (256 * 2); + register_func (free_crc16_table); + init_crc_table (crc16_table, CRC16_POLYNOMIAL); + } + + crc = ~crc; + while (size--) + crc = (crc >> 8) ^ crc16_table[(crc ^ *p++) & 0xff]; + return ~crc; +} +#endif + + +// CRC32 +#ifndef USE_ZLIB + +static unsigned int *crc32_table = NULL; + + +static void +free_crc32_table (void) +{ + free (crc32_table); + crc32_table = NULL; +} + + +unsigned int +crc32 (unsigned int crc, const void *buffer, unsigned int size) +{ + unsigned char *p = (unsigned char *) buffer; + + if (!crc32_table) + { + crc32_table = (unsigned int *) malloc (256 * 4); + register_func (free_crc32_table); + init_crc_table (crc32_table, CRC32_POLYNOMIAL); + } + + crc = ~crc; + while (size--) + crc = (crc >> 8) ^ crc32_table[(crc ^ *p++) & 0xff]; + return ~crc; +} +#endif diff --git a/ucon64/2.0/src/misc/chksum.h b/ucon64/2.0/src/misc/chksum.h new file mode 100644 index 0000000..f7aab29 --- /dev/null +++ b/ucon64/2.0/src/misc/chksum.h @@ -0,0 +1,105 @@ +/* +chksum.h - miscellaneous checksum functions + SHA1, MD5, CRC16 and CRC32 + +Copyright (c) 1999 - 2004 NoisyB +Copyright (c) 2001 - 2004 dbjh + +sha1 - Copyright (c) 2002, Dr Brian Gladman , Worcester, UK. + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + +MD5 - Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. + License to copy and use this software is granted provided that + it is identified as the "RSA Data Security, Inc. MD5 Message + Digest Algorithm" in all material mentioning or referencing this + software or this function. +*/ +#ifndef MISC_CHKSUM_H +#define MISC_CHKSUM_H + +#ifdef HAVE_CONFIG_H +#include "config.h" // USE_ZLIB +#endif +#ifdef __cplusplus +extern "C" { +#endif +#ifdef HAVE_INTTYPES_H +#include +#else // __MSDOS__, _WIN32 (VC++) +#include "itypes.h" +#endif + + +/* + s_sha1_ctx_t + sha1_begin() start sha1 + sha1() process data + sha1_end() stop sha1 + + s_md5_ctx_t + md5_init() start md5 + md5_update() process data + md5_final() stop md5 + + chksum_crc16() + + crc32() a crc32() clone (if no ZLIB is used) + use zlib's crc32() if USE_ZLIB is defined... + ... but make it possible to link against a library + that uses zlib while this code does not use it +*/ +typedef struct +{ + uint32_t count[2]; + uint32_t hash[5]; + uint32_t wbuf[16]; +} s_sha1_ctx_t; + +extern void sha1_begin (s_sha1_ctx_t ctx[1]); +extern void sha1 (s_sha1_ctx_t ctx[1], const unsigned char data[], unsigned int len); +extern void sha1_end (unsigned char hval[], s_sha1_ctx_t ctx[1]); + + +// data structure for MD5 (Message Digest) computation +typedef struct +{ + uint32_t i[2]; // number of _bits_ handled mod 2^64 + uint32_t buf[4]; // scratch buffer + unsigned char in[64]; // input buffer + unsigned char digest[16]; // actual digest after md5_final call +} s_md5_ctx_t; + +extern void md5_init (s_md5_ctx_t *mdContext, unsigned long pseudoRandomNumber); +extern void md5_update (s_md5_ctx_t *mdContext, unsigned char *inBuf, unsigned int inLen); +extern void md5_final (s_md5_ctx_t *mdContext); + + +#ifdef WITH_CRC16 +extern unsigned short chksum_crc16 (unsigned short crc, const void *buffer, unsigned int size); +#endif + +#ifndef USE_ZLIB +extern unsigned int crc32 (unsigned int crc, const void *buffer, unsigned int size); +#endif + + +#ifdef __cplusplus +} +#endif + +#endif // MISC_CHKSUM_H diff --git a/ucon64/2.0/src/misc/dlopen.c b/ucon64/2.0/src/misc/dlopen.c new file mode 100644 index 0000000..1a6f65e --- /dev/null +++ b/ucon64/2.0/src/misc/dlopen.c @@ -0,0 +1,294 @@ +/* +dlopen.c - DLL support code + +Copyright (c) 2002 - 2004 dbjh + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef DJGPP +#include +#elif defined __unix__ || defined __APPLE__ // Mac OS X actually; __unix__ is also +#include // defined under Cygwin (and DJGPP) +#elif defined _WIN32 +#include +#elif defined __BEOS__ +#include +#include +#endif + +#include +#include +#include + + +#ifdef DJGPP +#include "dxedll_pub.h" +#include "map.h" + + +#define INITIAL_HANDLE 1 +static st_map_t *dxe_map; +extern int errno; + + +void +uninit_func (void) +{ + fprintf (stderr, "An uninitialized member of the import/export structure was called!\n" + "Update dlopen.c/open_module()\n"); + exit (1); +} +#endif + + +void * +open_module (char *module_name) +{ + void *handle; +#ifdef DJGPP + static int new_handle = INITIAL_HANDLE; + int n, m; + st_symbol_t *sym = _dxe_load (module_name); + /* + _dxe_load() doesn't really return a handle. It returns a pointer to the one + symbol a DXE module can export. + */ + if (sym == 0) + { + fprintf (stderr, "Error while loading DXE module: %s\n", module_name); + exit (1); + } + + if (sym->size != sizeof (st_symbol_t)) + { + fprintf (stderr, "Incompatible DXE module: %s\n", module_name); + exit (1); + } + + // initialize the import/export structure + + /* + Catch calls to uninitialized members in case a new function was added to + st_symbol_t, but forgotten to initialize here. + */ + m = sizeof (st_symbol_t) / sizeof (void (*) (void)); + for (n = 0; n < m && ((void (**) (void)) sym)[n] != 0; n++) // Don't overwrite values initialized by DXE + ; + for (; n < m; n++) + ((void (**) (void)) sym)[n] = uninit_func; + + // initialize functions + sym->printf = printf; + sym->fprintf = fprintf; + sym->vfprintf = vfprintf; + sym->sprintf = sprintf; + sym->vsprintf = vsprintf; + sym->puts = puts; + sym->fputs = fputs; + sym->sscanf = sscanf; + sym->vsscanf = vsscanf; + sym->fopen = fopen; + sym->fdopen = fdopen; + sym->popen = popen; + sym->fclose = fclose; + sym->pclose = pclose; + sym->fseek = fseek; + sym->ftell = ftell; + sym->rewind = rewind; + sym->fread = fread; + sym->fwrite = fwrite; + sym->fgetc = fgetc; + sym->fgets = fgets; + sym->feof = feof; + sym->fputc = fputc; + sym->fflush = fflush; + sym->ferror = ferror; + sym->rename = rename; + sym->remove = remove; + + sym->free = free; + sym->malloc = malloc; + sym->calloc = calloc; + sym->realloc = realloc; + sym->exit = exit; + sym->strtol = strtol; + sym->getenv = getenv; + sym->srand = srand; + sym->rand = rand; + sym->atoi = atoi; + + sym->memcpy = memcpy; + sym->memset = memset; + sym->strcmp = strcmp; + sym->strcpy = strcpy; + sym->strncpy = strncpy; + sym->strcat = strcat; + sym->strncat = strncat; + sym->strcasecmp = strcasecmp; + sym->strncasecmp = strncasecmp; + sym->strchr = strchr; + sym->strrchr = strrchr; + sym->strpbrk = strpbrk; + sym->strspn = strspn; + sym->strcspn = strcspn; + sym->strlen = strlen; + sym->strstr = strstr; + sym->strdup = strdup; + sym->strtok = strtok; + + sym->tolower = tolower; + sym->toupper = toupper; + sym->isupper = isupper; + + sym->opendir = opendir; + sym->readdir = readdir; + sym->closedir = closedir; + + sym->access = access; + sym->rmdir = rmdir; + sym->isatty = isatty; + sym->chdir = chdir; + sym->getcwd = getcwd; + sym->getuid = getuid; + sym->sync = sync; + sym->truncate = truncate; + + sym->stat = stat; + sym->chmod = chmod; + sym->mkdir = mkdir; + sym->time = time; + sym->delay = delay; + sym->__dpmi_int = __dpmi_int; + + // initialize variables + sym->__dj_stdin = __dj_stdin; + sym->__dj_stdout = __dj_stdout; + sym->__dj_stderr = __dj_stderr; + sym->__dj_ctype_flags = __dj_ctype_flags; + sym->__dj_ctype_tolower = __dj_ctype_tolower; + sym->__dj_ctype_toupper = __dj_ctype_toupper; + sym->errno = errno; + + // initialize the DXE module + sym->dxe_init (); + + if (new_handle == INITIAL_HANDLE) + dxe_map = map_create (10); + dxe_map = map_put (dxe_map, (void *) new_handle, sym); + handle = (void *) new_handle++; +#elif defined __unix__ || defined __APPLE__ // Mac OS X actually + /* + We use dlcompat under Mac OS X simply because it's there. I (dbjh) don't + want to add extra code only because "using the native api's is the supported + method of loading dynamically on Mac OS X" (Peter O'Gorman, maintainer of + dlcompat). Besides, dlcompat has been tested while any new code we add, not. + RTLD_NOW is ignored by dlcompat (7-12-2003). + */ + if ((handle = dlopen (module_name, RTLD_LAZY)) == NULL) + { + fputs (dlerror (), stderr); + fputc ('\n', stderr); + exit (1); + } +#elif defined _WIN32 + if ((handle = LoadLibrary (module_name)) == NULL) + { + LPTSTR strptr; + + FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError (), + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &strptr, 0, NULL); + // Note the construct with strptr. You wouldn't believe what a bunch of + // fucking morons those guys at Microsoft are! + fputs (strptr, stderr); + LocalFree (strptr); + exit (1); + } +#elif defined __BEOS__ + if ((int) (handle = (void *) load_add_on (module_name)) < B_NO_ERROR) + { + fprintf (stderr, "Error while loading add-on image: %s\n", module_name); + exit (1); + } +#endif + + return handle; +} + + +void * +get_symbol (void *handle, char *symbol_name) +{ + void *symptr; +#ifdef DJGPP + st_symbol_t *sym = map_get (dxe_map, handle); + if (sym == NULL) + { + fprintf (stderr, "Invalid handle: %x\n", (int) handle); + exit (1); + } + + symptr = sym->dxe_symbol (symbol_name); + if (symptr == NULL) + { + fprintf (stderr, "Could not find symbol: %s\n", symbol_name); + exit (1); + } +#elif defined __unix__ || defined __APPLE__ // Mac OS X actually, see + char *strptr; // comment in open_module() + + symptr = dlsym (handle, symbol_name); + if ((strptr = (char *) dlerror ()) != NULL) // this is "the correct way" + { // according to the info page + fputs (strptr, stderr); + fputc ('\n', stderr); + exit (1); + } +#elif defined _WIN32 + symptr = (void *) GetProcAddress ((HINSTANCE) handle, symbol_name); + if (symptr == NULL) + { + LPTSTR strptr; + + FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError (), + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &strptr, 0, NULL); + fputs (strptr, stderr); + LocalFree (strptr); + exit (1); + } +#elif defined __BEOS__ + int status = get_image_symbol ((int) handle, symbol_name, + B_SYMBOL_TYPE_TEXT, &symptr); // B_SYMBOL_TYPE_DATA/B_SYMBOL_TYPE_ANY + if (status != B_OK) + { + fprintf (stderr, "Could not find symbol: %s\n", symbol_name); + exit (1); + } +#endif + + return symptr; +} diff --git a/ucon64/2.0/src/misc/dlopen.h b/ucon64/2.0/src/misc/dlopen.h new file mode 100644 index 0000000..7dbcb0a --- /dev/null +++ b/ucon64/2.0/src/misc/dlopen.h @@ -0,0 +1,27 @@ +/* +dlopen.h - DLL support code + +Copyright (c) 2002 dbjh + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef DLOPEN_H +#define DLOPEN_H + +void *open_module (char *module_name); +void *get_symbol (void *handle, char *symbol_name); + +#endif // DLOPEN_H diff --git a/ucon64/2.0/src/misc/dxedll_pub.h b/ucon64/2.0/src/misc/dxedll_pub.h new file mode 100644 index 0000000..e23baae --- /dev/null +++ b/ucon64/2.0/src/misc/dxedll_pub.h @@ -0,0 +1,159 @@ +/* +dxedll_pub.h - DXE client support code + +Copyright (c) 2002 - 2004 dbjh + + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef DXEDLL_PUB_H +#define DXEDLL_PUB_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_DIRENT_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct st_symbol +{ + // functions exported by the DXE module + int (*dxe_init) (void); + void *(*dxe_symbol) (char *symbol_name); + + // variables exported by the DXE module + int size; // yes, this var needs to be here + // (or it could be a function) + /* + functions imported by the DXE module + Note that most functions used by the DXE module and not defined in it + should be listed here. That includes standard C library functions and + variables. + */ + int (*printf) (const char *, ...); + int (*fprintf) (FILE *, const char *, ...); + int (*vfprintf) (FILE *, const char *, va_list); + int (*sprintf) (char *, const char *, ...); + int (*vsprintf) (char *, const char *, va_list); + int (*puts) (const char *); + int (*fputs) (const char *, FILE *); + int (*sscanf) (const char *, const char *, ...); + int (*vsscanf) (const char *, const char *, va_list); + FILE *(*fopen) (const char *, const char *); + FILE *(*fdopen) (int, const char *); + FILE *(*popen) (const char *, const char *); + int (*fclose) (FILE *); + int (*pclose) (FILE *); + int (*fseek) (FILE *, long, int); + long (*ftell) (FILE *); + void (*rewind) (FILE *); + size_t (*fread) (void *, size_t, size_t, FILE *); + size_t (*fwrite) (const void *, size_t, size_t, FILE *); + int (*fgetc) (FILE *); + char *(*fgets) (char *, int, FILE *); + int (*feof) (FILE *); + int (*fputc) (int, FILE *); + int (*fflush) (FILE *); + int (*ferror) (FILE *); + int (*rename) (const char *, const char *); + int (*remove) (const char *); + + void (*free) (void *); + void *(*malloc) (size_t); + void *(*calloc) (size_t, size_t); + void *(*realloc) (void *, size_t); + void (*exit) (int) __attribute__ ((noreturn)); + long (*strtol) (const char *, char **, int); + char *(*getenv) (const char *); + void (*srand) (unsigned); + int (*rand) (void); + int (*atoi) (const char *); + + void *(*memcpy) (void *, const void *, size_t); + void *(*memset) (void *, int, size_t); + int (*strcmp) (const char *, const char *); + char *(*strcpy) (char *, const char *); + char *(*strncpy) (char *, const char *, size_t); + char *(*strcat) (char *, const char *); + char *(*strncat) (char *, const char *, size_t); + int (*strcasecmp) (const char *, const char *); + int (*strncasecmp) (const char *, const char *, size_t); + char *(*strchr) (const char *, int); + char *(*strrchr) (const char *, int); + char *(*strpbrk) (const char *, const char *); + size_t (*strspn) (const char *, const char *); + size_t (*strcspn) (const char *, const char *); + size_t (*strlen) (const char *); + char *(*strstr) (const char *, const char *); + char *(*strdup) (const char *); + char *(*strtok) (char *, const char *); + + int (*tolower) (int); + int (*toupper) (int); + int (*isupper) (int); + + DIR *(*opendir) (const char *); + struct dirent *(*readdir) (DIR *); + int (*closedir) (DIR *); + + // va_start(), va_arg() and va_end() are macros + + int (*access) (const char *, int); + int (*rmdir) (const char *); + int (*isatty) (int); + int (*chdir) (const char *); + char *(*getcwd) (char *, size_t); + int (*getuid) (void); + int (*sync) (void); + int (*truncate) (const char *, off_t); + + int (*stat) (const char *, struct stat *); + int (*chmod) (const char *, mode_t); + int (*mkdir) (const char *, mode_t); + time_t (*time) (time_t *); + void (*delay) (unsigned); + int (*__dpmi_int) (int, __dpmi_regs *); + + // Put all variables AFTER the functions. This makes it easy to catch + // uninitialized function pointers. + FILE __dj_stdin, __dj_stdout, __dj_stderr; + // WARNING: actually the __dj_ctype_X variables are arrays + unsigned short *__dj_ctype_flags; + unsigned char *__dj_ctype_tolower, *__dj_ctype_toupper; + int errno; +} st_symbol_t; + +#ifdef __cplusplus +} +#endif + +#endif // DXEDLL_PUB_H diff --git a/ucon64/2.0/src/misc/file.c b/ucon64/2.0/src/misc/file.c new file mode 100644 index 0000000..033c867 --- /dev/null +++ b/ucon64/2.0/src/misc/file.c @@ -0,0 +1,1009 @@ +/* +file.c - miscellaneous file functions + +Copyright (c) 1999 - 2004 NoisyB +Copyright (c) 2001 - 2004 dbjh +Copyright (c) 2002 - 2004 Jan-Erik Karlsson (Amiga) + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" // USE_ZLIB +#endif +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include // va_arg() +#include // for S_IFLNK + +#ifdef __MSDOS__ +#include // delay(), milliseconds +#elif defined __unix__ +#include // usleep(), microseconds +#elif defined __BEOS__ +#include // snooze(), microseconds +// Include OS.h before misc.h, because OS.h includes StorageDefs.h which +// includes param.h which unconditionally defines MIN and MAX. +#elif defined AMIGA +#include +#include +#include +#include +#include // GetKey() +#include +#include +#elif defined _WIN32 +#include // Sleep(), milliseconds +#endif + +#ifdef HAVE_INTTYPES_H +#include +#else // __MSDOS__, _WIN32 (VC++) +#include "itypes.h" +#endif +#ifdef USE_ZLIB +#include "archive.h" +#endif +#include "file.h" +#include "misc.h" // getenv2() +#include "getopt.h" // struct option + +#ifdef DJGPP +#ifdef DLL +#include "dxedll_priv.h" +#endif +#endif + +#ifndef MAXBUFSIZE +#define MAXBUFSIZE 32768 +#endif // MAXBUFSIZE + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifdef _MSC_VER +// Visual C++ doesn't allow inline in C source code +#define inline __inline +#endif + + +extern int errno; + + +int +isfname (int c) +{ + // characters that are allowed in filenames + return ((isalnum (c) || (c && strchr (".,'+- ()[]!&_", c)))); +} + + +int +tofname (int c) +{ + return isfname (c) ? c : '_'; +} + + +#ifndef HAVE_REALPATH +#undef realpath +char * +realpath (const char *path, char *full_path) +{ +#if defined __unix__ || defined __BEOS__ || defined __MSDOS__ +/* + Keep the "defined _WIN32"'s in this code in case GetFullPathName() turns out + to have some unexpected problems. This code works for Visual C++, but it + doesn't return the same paths as GetFullPathName() does. Most notably, + GetFullPathName() expands :. to the current directory of + : while this code doesn't. +*/ +#define MAX_READLINKS 32 + char copy_path[FILENAME_MAX], got_path[FILENAME_MAX], *new_path = got_path, + *max_path; +#if defined __MSDOS__ || defined _WIN32 || defined __CYGWIN__ + char c; +#endif +#ifdef S_IFLNK + char link_path[FILENAME_MAX]; + int readlinks = 0; +#endif + int n; + + // Make a copy of the source path since we may need to modify it + n = strlen (path); + if (n >= FILENAME_MAX - 2) + return NULL; + else if (n == 0) + return NULL; + + strcpy (copy_path, path); + path = copy_path; + max_path = copy_path + FILENAME_MAX - 2; +#if defined __MSDOS__ || defined _WIN32 || defined __CYGWIN__ + c = toupper (*path); + if (c >= 'A' && c <= 'Z' && path[1] == ':') + { + *new_path++ = *path++; + *new_path++ = *path++; + if (*path == FILE_SEPARATOR) + *new_path++ = *path++; + } + else +#endif + if (*path != FILE_SEPARATOR) + { + getcwd (new_path, FILENAME_MAX - 1); +#ifdef DJGPP + // DJGPP's getcwd() returns a path with forward slashes + { + int l = strlen (new_path); + for (n = 0; n < l; n++) + if (new_path[n] == '/') + new_path[n] = FILE_SEPARATOR; + } +#endif + new_path += strlen (new_path); + if (*(new_path - 1) != FILE_SEPARATOR) + *new_path++ = FILE_SEPARATOR; + } + else + { + *new_path++ = FILE_SEPARATOR; + path++; + } + + // Expand each (back)slash-separated pathname component + while (*path != 0) + { + // Ignore stray FILE_SEPARATOR + if (*path == FILE_SEPARATOR) + { + path++; + continue; + } + if (*path == '.') + { + // Ignore "." + if (path[1] == 0 || path[1] == FILE_SEPARATOR) + { + path++; + continue; + } + if (path[1] == '.') + { + if (path[2] == 0 || path[2] == FILE_SEPARATOR) + { + path += 2; + // Ignore ".." at root + if (new_path == got_path + 1) + continue; + // Handle ".." by backing up + while (*((--new_path) - 1) != FILE_SEPARATOR) + ; + continue; + } + } + } + // Safely copy the next pathname component + while (*path != 0 && *path != FILE_SEPARATOR) + { + if (path > max_path) + return NULL; + + *new_path++ = *path++; + } +#ifdef S_IFLNK + // Protect against infinite loops + if (readlinks++ > MAX_READLINKS) + return NULL; + + // See if latest pathname component is a symlink + *new_path = 0; + n = readlink (got_path, link_path, FILENAME_MAX - 1); + if (n < 0) + { + // EINVAL means the file exists but isn't a symlink + if (errno != EINVAL +#ifdef __BEOS__ + // Make this function work for a mounted ext2 fs ("/:") + && errno != B_NAME_TOO_LONG +#endif + ) + { + // Make sure it's null terminated + *new_path = 0; + strcpy (full_path, got_path); + return NULL; + } + } + else + { + // NOTE: readlink() doesn't add the null byte + link_path[n] = 0; + if (*link_path == FILE_SEPARATOR) + // Start over for an absolute symlink + new_path = got_path; + else + // Otherwise back up over this component + while (*(--new_path) != FILE_SEPARATOR) + ; + if (strlen (path) + n >= FILENAME_MAX - 2) + return NULL; + // Insert symlink contents into path + strcat (link_path, path); + strcpy (copy_path, link_path); + path = copy_path; + } +#endif // S_IFLNK + *new_path++ = FILE_SEPARATOR; + } + // Delete trailing slash but don't whomp a lone slash + if (new_path != got_path + 1 && *(new_path - 1) == FILE_SEPARATOR) + { +#if defined __MSDOS__ || defined _WIN32 || defined __CYGWIN__ + if (new_path >= got_path + 3) + { + if (*(new_path - 2) == ':') + { + c = toupper (*(new_path - 3)); + if (!(c >= 'A' && c <= 'Z')) + new_path--; + } + else + new_path--; + } + else + new_path--; +#else + new_path--; +#endif + } + // Make sure it's null terminated + *new_path = 0; + strcpy (full_path, got_path); + + return full_path; +#elif defined _WIN32 + char *p, c; + int n; + + if (GetFullPathName (path, FILENAME_MAX, full_path, &p) == 0) + return NULL; + + c = toupper (full_path[0]); + n = strlen (full_path) - 1; + // Remove trailing separator if full_path is not the root dir of a drive, + // because Visual C++'s run-time system is *really* stupid + if (full_path[n] == FILE_SEPARATOR && + !(c >= 'A' && c <= 'Z' && full_path[1] == ':' && full_path[3] == 0)) // && full_path[2] == FILE_SEPARATOR + full_path[n] = 0; + + return full_path; +#elif defined AMIGA + strcpy (full_path, path); + return full_path; +#endif +} +#endif + + +#if 0 +st_strpath_t * +strpath (st_strpath_t *path, const char *path_s) +{ +// TODO: compare this with splitpath() + if (path_s == NULL) + return NULL; + + realpath2 (path_s, path->realpath); + +#if defined DJGPP || defined __CYGWIN__ || defined _WIN32 + if (isalpha (path->realpath[0]) && + path->realpath[1] == ':' && + path->realpath[2] == FILE_SEPARATOR) + sprintf (path->drive, "%c:\\", toupper (path->realpath[0])); +#else + strcpy (path->drive, FILE_SEPARATOR_S); +#endif + + dirname2 (path_s, path->dirname); + strcpy (path->basename, basename2 (path_s)); + strcpy (path->suffix, get_suffix (path_s)); + + return path; +} +#endif + + +char * +realpath2 (const char *path, char *full_path) +// enhanced realpath() which returns the absolute path of a file +{ + char path1[FILENAME_MAX]; + const char *path2; + + if (path[0] == '~') + { + if (path[1] == FILE_SEPARATOR +#ifdef __CYGWIN__ + || path[1] == '\\' +#endif + ) + sprintf (path1, "%s"FILE_SEPARATOR_S"%s", getenv2 ("HOME"), &path[2]); + else if (path[1] == 0) + strcpy (path1, getenv2 ("HOME")); + path2 = path1; + } + else + path2 = path; + + return realpath (path2, full_path); +} + + +char * +dirname2 (const char *path, char *dir) +{ + char *p1; +#if defined DJGPP || defined __CYGWIN__ + char *p2; +#endif + + if (path == NULL) + return NULL; + + strcpy (dir, path); +#if defined DJGPP || defined __CYGWIN__ + // Yes, DJGPP, not __MSDOS__, because DJGPP's dirname() behaves the same + // Cygwin has no dirname() + p1 = strrchr (dir, '/'); + p2 = strrchr (dir, '\\'); + if (p2 > p1) // use the last separator in path + p1 = p2; +#else + p1 = strrchr (dir, FILE_SEPARATOR); +#endif + +#if defined DJGPP || defined __CYGWIN__ || defined _WIN32 + if (p1 == NULL) // no slash, perhaps a drive? + { + if ((p1 = strrchr (dir, ':'))) + { + p1[1] = '.'; + p1 += 2; + } + } +#endif + + while (p1 > dir && // find first of last separators (we have to strip trailing ones) +#if defined DJGPP || defined __CYGWIN__ + ((*(p1 - 1) == '/' && (*p1 == '/' || *p1 == '\\')) + || + (*(p1 - 1) == '\\' && (*p1 == '\\' || *p1 == '/')))) +#else + (*(p1 - 1) == FILE_SEPARATOR && *p1 == FILE_SEPARATOR)) +#endif + p1--; + + if (p1 == dir) + p1++; // don't overwrite single separator (root dir) +#if defined DJGPP || defined __CYGWIN__ || defined _WIN32 + else if (p1 > dir) + if (*(p1 - 1) == ':') + p1++; // we must not overwrite the last separator if +#endif // it was directly preceded by a drive letter + + if (p1) + *p1 = 0; // terminate string (overwrite the separator) + else + { + dir[0] = '.'; + dir[1] = 0; + } + + return dir; +} + + +const char * +basename2 (const char *path) +{ + char *p1; +#if defined DJGPP || defined __CYGWIN__ + char *p2; +#endif + + if (path == NULL) + return NULL; + +#if defined DJGPP || defined __CYGWIN__ + // Yes, DJGPP, not __MSDOS__, because DJGPP's basename() behaves the same + // Cygwin has no basename() + p1 = strrchr (path, '/'); + p2 = strrchr (path, '\\'); + if (p2 > p1) // use the last separator in path + p1 = p2; +#else + p1 = strrchr (path, FILE_SEPARATOR); +#endif + +#if defined DJGPP || defined __CYGWIN__ || defined _WIN32 + if (p1 == NULL) // no slash, perhaps a drive? + p1 = strrchr (path, ':'); +#endif + + return p1 ? p1 + 1 : path; +} + + +const char * +get_suffix (const char *filename) +// Note that get_suffix() never returns NULL. Other code relies on that! +{ + const char *p, *s; + + if (!(p = basename2 (filename))) + p = filename; + if (!(s = strrchr (p, '.'))) + s = strchr (p, 0); // strchr(p, 0) and NOT "" is the + if (s == p) // suffix of a file without suffix + s = strchr (p, 0); // files can start with '.' + + return s; +} + + +char * +set_suffix (char *filename, const char *suffix) +{ + // always use set_suffix() and NEVER the code below + strcpy ((char *) get_suffix (filename), suffix); + + return filename; +} + + +int +one_file (const char *filename1, const char *filename2) +// returns 1 if filename1 and filename2 refer to one file, 0 if not (or error) +{ +#ifndef _WIN32 + struct stat finfo1, finfo2; + + /* + Not the name, but the combination inode & device identify a file. + Note that stat() doesn't need any access rights except search rights for + the directories in the path to the file. + */ + if (stat (filename1, &finfo1) != 0) + return 0; + if (stat (filename2, &finfo2) != 0) + return 0; + if (finfo1.st_dev == finfo2.st_dev && finfo1.st_ino == finfo2.st_ino) + return 1; + else + return 0; +#else + HANDLE file1, file2; + BY_HANDLE_FILE_INFORMATION finfo1, finfo2; + + file1 = CreateFile (filename1, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (file1 == INVALID_HANDLE_VALUE) + return 0; + file2 = CreateFile (filename2, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (file2 == INVALID_HANDLE_VALUE) + { + CloseHandle (file1); + return 0; + } + GetFileInformationByHandle (file1, &finfo1); + GetFileInformationByHandle (file2, &finfo2); + CloseHandle (file1); + CloseHandle (file2); + if (finfo1.dwVolumeSerialNumber == finfo2.dwVolumeSerialNumber && + (finfo1.nFileIndexHigh << 16 | finfo1.nFileIndexLow) == + (finfo2.nFileIndexHigh << 16 | finfo2.nFileIndexLow)) + return 1; + else + return 0; +#endif +} + + +int +one_filesystem (const char *filename1, const char *filename2) +// returns 1 if filename1 and filename2 reside on one file system, 0 if not +// (or an error occurred) +{ +#ifndef _WIN32 + struct stat finfo1, finfo2; + + if (stat (filename1, &finfo1) != 0) + return 0; + if (stat (filename2, &finfo2) != 0) + return 0; + if (finfo1.st_dev == finfo2.st_dev) + return 1; + else + return 0; +#else + DWORD fattrib1, fattrib2; + char path1[FILENAME_MAX], path2[FILENAME_MAX], *p, d1, d2; + HANDLE file1, file2; + BY_HANDLE_FILE_INFORMATION finfo1, finfo2; + + if ((fattrib1 = GetFileAttributes (filename1)) == (DWORD) -1) + return 0; + if ((fattrib2 = GetFileAttributes (filename2)) == (DWORD) -1) + return 0; + if (fattrib1 & FILE_ATTRIBUTE_DIRECTORY || fattrib2 & FILE_ATTRIBUTE_DIRECTORY) + /* + We special-case directories, because we can't use + FILE_FLAG_BACKUP_SEMANTICS as argument to CreateFile() under + Windows 9x/ME. There seems to be no Win32 function other than + CreateFile() to obtain a handle to a directory. + */ + { + if (GetFullPathName (filename1, FILENAME_MAX, path1, &p) == 0) + return 0; + if (GetFullPathName (filename2, FILENAME_MAX, path2, &p) == 0) + return 0; + d1 = toupper (path1[0]); + d2 = toupper (path2[0]); + if (d1 == d2 && d1 >= 'A' && d1 <= 'Z' && d2 >= 'A' && d2 <= 'Z') + if (strlen (path1) >= 2 && strlen (path2) >= 2) + // We don't handle unique volume names + if (path1[1] == ':' && path2[1] == ':') + return 1; + return 0; + } + + file1 = CreateFile (filename1, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (file1 == INVALID_HANDLE_VALUE) + return 0; + file2 = CreateFile (filename2, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (file2 == INVALID_HANDLE_VALUE) + { + CloseHandle (file1); + return 0; + } + GetFileInformationByHandle (file1, &finfo1); + GetFileInformationByHandle (file2, &finfo2); + CloseHandle (file1); + CloseHandle (file2); + if (finfo1.dwVolumeSerialNumber == finfo2.dwVolumeSerialNumber) + return 1; + else + return 0; +#endif +} + + +int +rename2 (const char *oldname, const char *newname) +{ + int retval; + char dir1[FILENAME_MAX], dir2[FILENAME_MAX]; + struct stat fstate; + + dirname2 (oldname, dir1); + dirname2 (newname, dir2); + + // We should use dirname{2}() in case oldname or newname doesn't exist yet + if (one_filesystem (dir1, dir2)) + { + if (access (newname, F_OK) == 0 && !one_file (oldname, newname)) + { + stat (newname, &fstate); + chmod (newname, fstate.st_mode | S_IWUSR); + remove (newname); // *try* to remove or rename() will fail + } + retval = rename (oldname, newname); + } + else + { + retval = fcopy_raw (oldname, newname); + // don't remove unless the file can be copied + if (retval == 0) + { + stat (oldname, &fstate); + chmod (oldname, fstate.st_mode | S_IWUSR); + remove (oldname); + } + } + + return retval; +} + + +int +truncate2 (const char *filename, off_t new_size) +{ + int size = fsizeof (filename); + struct stat fstate; + + stat (filename, &fstate); + if (chmod (filename, fstate.st_mode | S_IWUSR)) + return -1; + + if (size < new_size) + { + FILE *file; + unsigned char padbuffer[MAXBUFSIZE]; + int n_bytes; + + if ((file = fopen (filename, "ab")) == NULL) + return -1; + + memset (padbuffer, 0, MAXBUFSIZE); + + while (size < new_size) + { + n_bytes = new_size - size > MAXBUFSIZE ? MAXBUFSIZE : new_size - size; + fwrite (padbuffer, 1, n_bytes, file); + size += n_bytes; + } + + fclose (file); + } + else + truncate (filename, new_size); + + return 0; // success +} + + +char * +tmpnam2 (char *temp) +// tmpnam() clone +{ + char *p = getenv2 ("TEMP"); + + srand (time (0)); + + *temp = 0; + while (!(*temp) || !access (temp, F_OK)) // must work for files AND dirs + sprintf (temp, "%s%s%08x.tmp", p, FILE_SEPARATOR_S, rand()); + + return temp; +} + + +static inline int +fcopy_func (void *buffer, int n, void *object) +{ + fwrite (buffer, 1, n, (FILE *) object); + return n; +} + + +int +fcopy (const char *src, size_t start, size_t len, const char *dest, const char *mode) +{ + FILE *output; + int result = 0; + + if (one_file (dest, src)) // other code depends on this + return -1; // behaviour! + + if (!(output = fopen (dest, mode))) + { + errno = ENOENT; + return -1; + } + + fseek (output, 0, SEEK_END); + + result = quick_io_func (fcopy_func, MAXBUFSIZE, output, start, len, src, "rb"); + + fclose (output); + sync (); + + return result == -1 ? result : 0; +} + + +int +fcopy_raw (const char *src, const char *dest) +// Raw file copy function. Raw, because it will copy the file data as it is, +// unlike fcopy(). Don't merge fcopy_raw() with fcopy(). They have both their +// uses. +{ +#ifdef USE_ZLIB +#undef fopen +#undef fread +#undef fwrite +#undef fclose +#endif + FILE *fh, *fh2; + int seg_len; + char buf[MAXBUFSIZE]; + + if (one_file (dest, src)) + return -1; + + if (!(fh = fopen (src, "rb"))) + return -1; + if (!(fh2 = fopen (dest, "wb"))) + { + fclose (fh); + return -1; + } + while ((seg_len = fread (buf, 1, MAXBUFSIZE, fh))) + fwrite (buf, 1, seg_len, fh2); + + fclose (fh); + fclose (fh2); + return 0; +#ifdef USE_ZLIB +#define fopen fopen2 +#define fread fread2 +#define fwrite fwrite2 +#define fclose fclose2 +#endif +} + + +#ifndef USE_ZLIB +int +fsizeof (const char *filename) +{ + struct stat fstate; + + if (!stat (filename, &fstate)) + return fstate.st_size; + + errno = ENOENT; + return -1; +} +#endif + + +static FILE * +quick_io_open (const char *filename, const char *mode) +{ + FILE *fh = NULL; + + if (*mode == 'w' || *mode == 'a' || mode[1] == '+') // will we write to it? + if (!access (filename, F_OK)) // exists? + { + struct stat fstate; + // First (try to) change the file mode or we won't be able to write to + // it if it's a read-only file. + stat (filename, &fstate); + if (chmod (filename, fstate.st_mode | S_IWUSR)) + { + errno = EACCES; + return NULL; + } + } + + if ((fh = fopen (filename, (const char *) mode)) == NULL) + { +#ifdef DEBUG + fprintf (stderr, "ERROR: Could not open \"%s\" in mode \"%s\"\n" + "CAUSE: %s\n", filename, mode, strerror (errno)); +#endif + return NULL; + } + +#ifdef DEBUG + fprintf (stderr, "\"%s\": \"%s\"\n", filename, (char *) mode); +#endif + + return fh; +} + + +int +quick_io_c (int value, size_t pos, const char *filename, const char *mode) +{ + int result; + FILE *fh; + + if (!(fh = quick_io_open (filename, (const char *) mode))) + return -1; + + fseek (fh, pos, SEEK_SET); + + if (*mode == 'r' && mode[1] != '+') // "r+b" always writes + result = fgetc (fh); + else + result = fputc (value, fh); + + fclose (fh); + return result; +} + + +int +quick_io (void *buffer, size_t start, size_t len, const char *filename, + const char *mode) +{ + int result; + FILE *fh; + + if (!(fh = quick_io_open (filename, (const char *) mode))) + return -1; + + fseek (fh, start, SEEK_SET); + + // Note the order of arguments of fread() and fwrite(). Now quick_io() + // returns the number of characters read or written. Some code relies on + // this behaviour! + if (*mode == 'r' && mode[1] != '+') // "r+b" always writes + result = (int) fread (buffer, 1, len, fh); + else + result = (int) fwrite (buffer, 1, len, fh); + + fclose (fh); + return result; +} + + +static inline int +quick_io_func_inline (int (*func) (void *, int, void *), int func_maxlen, + void *object, void *buffer, int buffer_len) +{ + int i = 0, func_size = MIN (func_maxlen, buffer_len), func_result = 0; + + for (; i < buffer_len; i += func_size) + { + func_size = MIN (func_size, buffer_len - i); + func_result = func ((char *) buffer + i, func_size, object); + if (func_result < func_size) + break; + } + + return i + func_result; +} + + +int +quick_io_func (int (*func) (void *, int, void *), int func_maxlen, void *object, + size_t start, size_t len, const char *filename, const char *mode) +// func() takes buffer, length and object (optional), func_maxlen is maximum +// length passed to func() +{ + void *buffer = NULL; + int buffer_maxlen = 0, buffer_len = 0, func_len = 0; + size_t len_done = 0; + FILE *fh = NULL; + + if (len <= 5 * 1024 * 1024) // files up to 5 MB are loaded + if ((buffer = malloc (len))) // in their entirety + buffer_maxlen = len; + if (!buffer) + { + if ((buffer = malloc (func_maxlen))) + buffer_maxlen = func_maxlen; + else + return -1; + } + + if (!(fh = quick_io_open (filename, (const char *) mode))) + { + free (buffer); + return -1; + } + + fseek (fh, start, SEEK_SET); + + for (len_done = 0; len_done < len; len_done += buffer_len) + { + if (!(buffer_len = fread (buffer, 1, buffer_maxlen, fh))) + break; + + func_len = quick_io_func_inline (func, func_maxlen, object, buffer, buffer_len); + + if (func_len < buffer_len) // less than buffer_len? this must be the end + break; // or a problem (if write mode) + + if (*mode == 'w' || *mode == 'a' || mode[1] == '+') + { + fseek (fh, -buffer_len, SEEK_CUR); + fwrite (buffer, 1, buffer_len, fh); + /* + This appears to be a bug in DJGPP and Solaris (for ecample, when + called from ucon64_fbswap16()). Without an extra call to fseek() a + part of the file won't be written (DJGPP: after 8 MB, Solaris: after + 12 MB). + */ + fseek (fh, 0, SEEK_CUR); + } + } + + fclose (fh); + free (buffer); + sync (); + + // returns total bytes processed or if (func() < 0) it returns that error value + return func_len < 0 ? func_len : ((int) len_done + func_len); +} + + +char * +mkbak (const char *filename, backup_t type) +{ + static char buf[FILENAME_MAX]; + + if (access (filename, R_OK) != 0) + return (char *) filename; + + strcpy (buf, filename); + set_suffix (buf, ".bak"); + if (strcmp (filename, buf) != 0) + { + remove (buf); // *try* to remove or rename() will fail + if (rename (filename, buf)) // keep file attributes like date, etc. + { + fprintf (stderr, "ERROR: Can't rename \"%s\" to \"%s\"\n", filename, buf); + exit (1); + } + } + else // handle the case where filename has the suffix ".bak". + { + char buf2[FILENAME_MAX]; + + if (!dirname2 (filename, buf)) + { + fprintf (stderr, "INTERNAL ERROR: dirname2() returned NULL\n"); + exit (1); + } + if (buf[0] != 0) + if (buf[strlen (buf) - 1] != FILE_SEPARATOR) + strcat (buf, FILE_SEPARATOR_S); + + strcat (buf, basename2 (tmpnam2 (buf2))); + if (rename (filename, buf)) + { + fprintf (stderr, "ERROR: Can't rename \"%s\" to \"%s\"\n", filename, buf); + exit (1); + } + } + + switch (type) + { + case BAK_MOVE: + return buf; + + case BAK_DUPE: + default: + if (fcopy (buf, 0, fsizeof (buf), filename, "wb")) + { + fprintf (stderr, "ERROR: Can't open \"%s\" for writing\n", filename); + exit (1); + } + sync (); + return buf; + } +} diff --git a/ucon64/2.0/src/misc/file.h b/ucon64/2.0/src/misc/file.h new file mode 100644 index 0000000..824ebb5 --- /dev/null +++ b/ucon64/2.0/src/misc/file.h @@ -0,0 +1,134 @@ +/* +file.h - miscellaneous file functions + +Copyright (c) 1999 - 2004 NoisyB +Copyright (c) 2001 - 2004 dbjh +Copyright (c) 2002 - 2004 Jan-Erik Karlsson (Amiga) + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef MISC_FILE_H +#define MISC_FILE_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include // off_t +#ifdef HAVE_INTTYPES_H +#include +#else // __MSDOS__, _WIN32 (VC++) +#include "itypes.h" +#endif + + +#if (defined __unix__ && !defined __MSDOS__) || defined __BEOS__ || \ + defined AMIGA || defined __APPLE__ // Mac OS X actually +// GNU/Linux, Solaris, FreeBSD, OpenBSD, Cygwin, BeOS, Amiga, Mac (OS X) +#define FILE_SEPARATOR '/' +#define FILE_SEPARATOR_S "/" +#else // DJGPP, Win32 +#define FILE_SEPARATOR '\\' +#define FILE_SEPARATOR_S "\\" +#endif + + +/* + Miscellaneous file operations + + isfname() test if char could be used for filenames + tofname() replaces char that can not be used for filenames + strpath() a general routine to parse paths + set_suffix() set/replace suffix of filename with suffix + suffix means in this case the suffix INCLUDING the dot '.' + get_suffix() get suffix of filename + basename2() basename() replacement + dirname2() dirname() replacement + realpath2() realpath() replacement + one_file() returns 1 if two filenames refer to one file, otherwise it + returns 0 + one_filesystem() returns 1 if two filenames refer to files on one file + system, otherwise it returns 0 + rename2() renames oldname to newname even if oldname and newname are not + on one file system + truncate2() don't use truncate() to enlarge files, because the result is + undefined (by POSIX) use truncate2() instead which does both + tmpnam2() replacement for tmpnam() temp must have the size of FILENAME_MAX + mkbak() modes + BAK_DUPE (default) + rename file to keep attributes and copy it back to old name + and return new name + filename -> rename() -> buf -> f_cpy() -> filename -> return buf + BAK_MOVE + just rename file and return new name (static) + filename -> rename() -> buf -> return buf + fcopy() copy src from start for len to dest with mode + fcopy_raw() copy src to dest without looking at the file data (no + decompression like with fcopy()) + fsizeof() returns size of a file in bytes + quick_io() returns number of bytes read or written + quick_io_c() returns byte read or fputc()'s status + quick_io_func() + runs func() everytime it loads a new buffer + write modes will overwrite the file specified + NOTE: modes of all quick_*() are similar to fopen() modes + mode: "r.." + func(void *, int) must always return the number of bytes or a + negative value if a problem occured + mode: "a.." or "w.." + func(void *, int) must always return the exact number of bytes + (int) or the buffer won't be written +*/ +extern int isfname (int c); +extern int tofname (int c); +#if 0 +typedef struct +{ + char realpath[FILENAME_MAX]; // absolute path + char drive[FILENAME_MAX]; // "/", "C:\\", etc.. (depending on platform) + char dirname[FILENAME_MAX]; // (relative) dirname + char basename[FILENAME_MAX]; // filename + char suffix[FILENAME_MAX]; // suffix, extension (including dot) +} st_strpath_t; + +extern st_strpath_t *strpath (st_strpath_t *path, const char *path_s); +#endif +extern char *realpath2 (const char *path, char *full_path); +extern char *dirname2 (const char *path, char *dir); +extern const char *basename2 (const char *path); +extern const char *get_suffix (const char *filename); +extern char *set_suffix (char *filename, const char *suffix); +extern int one_file (const char *filename1, const char *filename2); +extern int one_filesystem (const char *filename1, const char *filename2); +extern int rename2 (const char *oldname, const char *newname); +extern int truncate2 (const char *filename, off_t size); +extern char *tmpnam2 (char *temp); +typedef enum { BAK_DUPE, BAK_MOVE } backup_t; +extern char *mkbak (const char *filename, backup_t type); +extern int fcopy (const char *src, size_t start, size_t len, const char *dest, + const char *dest_mode); +extern int fcopy_raw (const char *src, const char *dest); +#ifndef USE_ZLIB +// archive.h's definition gets higher "precedence" +extern int fsizeof (const char *filename); +#endif +extern int quick_io (void *buffer, size_t start, size_t len, const char *fname, + const char *mode); +extern int quick_io_c (int value, size_t pos, const char *fname, const char *mode); +extern int quick_io_func (int (*callback_func) (void *, int, void *), + int func_maxlen, void *object, size_t start, + size_t len, const char *fname, const char *mode); + +#endif // MISC_FILE_H diff --git a/ucon64/2.0/src/misc/getopt.c b/ucon64/2.0/src/misc/getopt.c new file mode 100644 index 0000000..d7d5546 --- /dev/null +++ b/ucon64/2.0/src/misc/getopt.c @@ -0,0 +1,1060 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97 + Free Software Foundation, Inc. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if !defined (__STDC__) || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include +#include +#endif /* GNU C library. */ + +#ifdef VMS +#include +#if HAVE_STRING_H - 0 +#include +#endif +#endif + +#if defined (_WIN32) && !defined (__CYGWIN32__) +/* It's not Unix, really. See? Capital letters. */ +#include +#define getpid() GetCurrentProcessId() +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +#ifdef HAVE_LIBINTL_H +# include +# define _(msgid) gettext (msgid) +#else +# define _(msgid) (msgid) +#endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} +ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include +#define my_index strchr +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#ifdef __cplusplus +extern "C" +#endif +char *getenv (const char *); + +static char * +my_index (const char *str, int chr) +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +#if !defined (__STDC__) || !__STDC__ +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +#endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; + +static int original_argc; +static char *const *original_argv; + +extern pid_t __libc_pid; + +/* Make sure the environment variable bash 2.0 puts in the environment + is valid for the getopt call we must make sure that the ARGV passed + to getopt is that one passed to the process. */ +static void + __attribute__ ((unused)) store_args_and_env (int argc, char *const *argv) +{ + /* XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ + original_argc = argc; + original_argv = argv; +} + +text_set_element (__libc_subinit, store_args_and_env); + +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined (__STDC__) && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (char **argv) +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#ifdef _LIBC + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len); + memset (&new_str[nonoption_flags_max_len], '\0', + top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined (__STDC__) && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (int argc, char *const *argv, const char *optstring) +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#ifdef _LIBC + if (posixly_correct == NULL + && argc == original_argc && argv == original_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + { + memcpy (__getopt_nonoption_flags, orig_str, len); + memset (&__getopt_nonoption_flags[len], '\0', + nonoption_flags_max_len - len); + } + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#else + (void) argc; // warning remover + (void) argv; // warning remover +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (int argc, char *const *argv, const char *optstring, + const struct option *longopts, int *longind, int long_only) +{ + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#ifdef _LIBC +#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only + && (argv[optind][2] + || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _ + ("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + _ + ("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + } + + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; + nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (int argc, char *const *argv, const char *optstring) +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, (int *) 0, 0); +} + +int +getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind) +{ + return _getopt_internal (argc, argv, shortopts, + longopts, longind, 0); +} + +int +getopt_long_only (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind) +{ + return _getopt_internal (argc, argv, shortopts, + longopts, longind, 1); +} + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (int argc, char **argv) +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/ucon64/2.0/src/misc/getopt.h b/ucon64/2.0/src/misc/getopt.h new file mode 100644 index 0000000..596cccc --- /dev/null +++ b/ucon64/2.0/src/misc/getopt.h @@ -0,0 +1,132 @@ +/* Declarations for getopt. + Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + + extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + + extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + + extern int opterr; + +/* Set to an option character which was unrecognized. */ + + extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + + struct option + { +#if defined (__STDC__) && __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; + }; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if defined (__STDC__) && __STDC__ +#if defined __GNU_LIBRARY__ || defined __cplusplus +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ + extern int getopt (int argc, char *const *argv, const char *shortopts); +#else /* not __GNU_LIBRARY__ */ + extern int getopt (); +#endif /* __GNU_LIBRARY__ */ + extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); + extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ + extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ + extern int getopt (); + extern int getopt_long (); + extern int getopt_long_only (); + + extern int _getopt_internal (); +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ diff --git a/ucon64/2.0/src/misc/getopt2.c b/ucon64/2.0/src/misc/getopt2.c new file mode 100644 index 0000000..9d2679b --- /dev/null +++ b/ucon64/2.0/src/misc/getopt2.c @@ -0,0 +1,469 @@ +/* +getopt2.c - getopt1() extension + +Copyright (c) 2004 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include "getopt.h" // struct option +#include "getopt2.h" + + +#ifdef MAXBUFSIZE +#undef MAXBUFSIZE +#endif // MAXBUFSIZE +#define MAXBUFSIZE 32768 + + +#ifdef DEBUG +static void +getopt2_sanity_check (const st_getopt2_t *option) +{ + int x, y = 0; + + for (x = 0; option[x].name || option[x].help; x++) + if (option[x].name) + for (y = 0; option[y].name || option[y].help; y++) + if (option[y].name) + if (!strcmp (option[x].name, option[y].name)) + if (option[x].val != option[y].val || + option[x].has_arg != option[y].has_arg) + { + fprintf (stderr, "ERROR: getopt2_sanity_check(): found dupe %s%s with different has_arg, or val\n", + option[x].name[1] ? OPTION_LONG_S : OPTION_S, option[x].name); + } +} + + +void +getopt2_parse_usage (const char *usage_output) +// parse usage output into st_getopt2_t array (for development) +{ + int i = 0, count = 0; + char buf[MAXBUFSIZE], *s = NULL, *d = NULL; + FILE *fh = fopen (usage_output, "r"); + + if (!fh) + return; + + while (fgets (buf, MAXBUFSIZE, fh)) + { + st_getopt2_t usage; + int value = 0; + + if (*buf == '\n') + continue; + + memset (&usage, 0, sizeof (st_getopt2_t)); + +#ifdef DEBUG + printf (buf); +#endif + s = d = buf; + d = strstr (s, " " OPTION_S); + if (d && (d - s) < 10) + { + s = (d + strspn (++d, OPTION_S)); + + for (i = 0; s[i] && s[i] != ' '; i++) + if (s[i] == OPTARG) + { + value = 1; + d = strtok (s, OPTARG_S); + break; + } + + if (!value) + d = strtok (s, " "); + + if (d) + usage.name = d; + + if (value) // parse =VALUE + { + d = strtok (NULL, " "); + + if (d) + usage.arg_name = d; + } + } + + + if (usage.name) + { + printf ("{\"%s\", ", usage.name); + + if (usage.arg_name) + printf ("1, \"%s\", ", usage.arg_name); + else + printf ("0, NULL, "); + + printf ("\"%s\", NULL},", strtrimr (strtriml (strtok (NULL, "\n")))); + + } + else + printf ("{NULL, 0, NULL, \"%s\", NULL},", strtrimr (strtriml (strtok (s, "\n")))); + + count++; + if (!(count % 10)) + printf (" // %d", count); + fputc ('\n', stdout); + } +} +#endif // DEBUG + + +#ifdef DEBUG +static inline char * +string_code (char *d, const char *s) +{ + char *p = d; + + *p = 0; + for (; *s; s++) + switch (*s) + { + case '\n': + strcat (p, "\\n\"\n \""); + break; + + case '\"': + strcat (p, "\\\""); + break; + + default: + p = strchr (p, 0); + *p = *s; + *(++p) = 0; + } + + return d; +} + + +static void +getopt2_usage_code (const st_getopt2_t *usage) +{ + int i = 0; + char buf[MAXBUFSIZE]; + +#ifdef DEBUG + getopt2_sanity_check (usage); +#endif + + for (; usage[i].name || usage[i].help; i++) + { + printf ("{\n %s%s%s, %d, 0, %d, // %d\n %s%s%s, %s%s%s,\n (void *) %d\n},\n", + usage[i].name ? "\"" : "", + usage[i].name ? usage[i].name : "NULL", + usage[i].name ? "\"" : "", + usage[i].has_arg, + usage[i].val, + i, + usage[i].arg_name ? "\"" : "", + usage[i].arg_name ? usage[i].arg_name : "NULL", + usage[i].arg_name ? "\"" : "", + usage[i].help ? "\"" : "", + usage[i].help ? string_code (buf, usage[i].help) : "NULL", + usage[i].help ? "\"" : "", + (int) usage[i].object); + } +} +#endif // DEBUG + + +void +getopt2_usage (const st_getopt2_t *usage) +{ +#ifdef DEBUG + getopt2_usage_code (usage); +#else + int i = 0; + char buf[MAXBUFSIZE]; + + for (i = 0; usage[i].name || usage[i].help; i++) + if (usage[i].help) // hidden options ARE allowed + { + if (usage[i].name) + { + sprintf (buf, "%s%s%s%s%s%s ", + // long or short name? + (usage[i].name[1] ? " " OPTION_LONG_S : " " OPTION_S), + usage[i].name, + usage[i].has_arg == 2 ? "[" : "", // == 2 arg is optional + usage[i].arg_name ? OPTARG_S : "", + usage[i].arg_name ? usage[i].arg_name : "", + usage[i].has_arg == 2 ? "]" : ""); // == 2 arg is optional + + if (strlen (buf) < 16) + { + strcat (buf, " "); + buf[16] = 0; + } + fputs (buf, stdout); + } + + if (usage[i].help) + { + char c, *p = buf, *p2 = NULL; + + strcpy (buf, usage[i].help); + + if (usage[i].name) + for (; (p2 = strchr (p, '\n')); p = p2 + 1) + { + c = p2[1]; + p2[1] = 0; + fputs (p, stdout); + fputs (" ", stdout); + p2[1] = c; + } + + fputs (p, stdout); + fputc ('\n', stdout); + } + } +#endif // DEBUG +} + + +static int +getopt2_long_internal (struct option *long_option, const st_getopt2_t *option, + int n, int long_only) +{ + int i = 0, j = 0, x = 0; + +#ifdef DEBUG + getopt2_sanity_check (option); +#endif + + memset (long_option, 0, sizeof (struct option) * n); + + for (; option[i].name || option[i].help; i++) + if (option[i].name) // IS option + if (long_only || option[i].name[1]) // IS long + // if (long_only) also one char options are long + { + for (j = 0; j < i; j++) + if (option[j].name) + if (!strcmp (option[i].name, option[j].name)) + break; // no dupes + + if (j == i && x < n) + { +#ifdef _MSC_VER + (char *) +#endif + long_option[x].name = +#ifdef _MSC_VER + (char *) +#endif + option[i].name; + long_option[x].has_arg = option[i].has_arg; + long_option[x].flag = option[i].flag; + long_option[x++].val = option[i].val; + } + } + + return x < n ? x + 1 : 0; +} + + +int +getopt2_long (struct option *long_option, const st_getopt2_t *option, int n) +{ + return getopt2_long_internal (long_option, option, n, 0); +} + + +int +getopt2_long_only (struct option *long_option, const st_getopt2_t *option, int n) +{ + return getopt2_long_internal (long_option, option, n, 1); +} + + +int +getopt2_short (char *short_option, const st_getopt2_t *option, int n) +{ + int i = 0; + char *p = short_option; + +#ifdef DEBUG + getopt2_sanity_check (option); +#endif + + *p = 0; + for (; option[i].name || option[i].help; i++) + if ((int) strlen (short_option) + 3 < n && option[i].name) // IS option + if (!option[i].name[1]) // IS short + if (!strchr (short_option, option[i].name[0])) // no dupes + { + *p++ = option[i].name[0]; + switch (option[i].has_arg) + { + case 2: + *p++ = ':'; + case 1: // falling through + *p++ = ':'; + case 0: + break; +#ifdef DEBUG + default: + fprintf (stderr, "ERROR: getopt2_short(): unexpected has_arg value (%d)\n", option[i].has_arg); +#endif // DEBUG + } + *p = 0; + } +#ifdef DEBUG + printf ("%s\n", short_option); + fflush (stdout); +#endif + + return (int) strlen (short_option) + 3 < n ? (int) strlen (short_option) : 0; +} + + +const st_getopt2_t * +getopt2_get_index_by_val (const st_getopt2_t *option, int val) +{ + int i = 0; + + for (; option[i].name || option[i].help; i++) + if (option[i].name && // it IS an option + option[i].val == val) + return &option[i]; + + return NULL; +} + + +#if TEST +// compile with -DTEST to build an executable +// WTF? - dbjh + +enum +{ + OPTION_HELP = 1, + OPTION_A, + OPTION_BBB, + OPTION_C, + OPTION_D +}; + + +#define MAX_OPTIONS 256 + + +int +main (int argc, char **argv) +{ + const char *str = "test"; + int c, digit_optind = 0; + st_getopt2_t option[] = + { + { + "help", 0, 0, OPTION_HELP, + NULL, "show this output and exit", + NULL + }, + { + "a", 0, 0, OPTION_A, + NULL, "option a", + (void *) str + }, + { + "bbb", 0, 0, OPTION_BBB, + NULL, "option bbb", + NULL + }, + { + "c", 1, 0, OPTION_C, + "ARG", "option c with required ARG", + NULL + }, + { + "d", 2, 0, OPTION_D, + "ARG", "option d with optional ARG", + NULL + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + struct option option_long[MAX_OPTIONS]; + char option_short[MAX_OPTIONS*3]; + + // turn st_getopt2_t into struct option + getopt2_long (option_long, option, MAX_OPTIONS); + getopt2_short (option_short, option, MAX_OPTIONS); + +// printf ("option_short: \"%s\"\n", option_short); + + optind = 0; + while ((c = getopt_long (argc, argv, option_short, option_long, NULL)) != -1) + { + if (c == '?') // getopt() returns 0x3f ('?') when an unknown option was given + { + printf ("Try '%s " OPTION_LONG_S "help' for more information.\n", + argv[0]); + exit (1); + } + + if (c == -1) + break; + + switch (c) + { + case OPTION_A: + printf ("option a with object '%s'\n", (const char *) getopt2_get_index_by_val (option, OPTION_A)->object); + break; + + case OPTION_BBB: + printf ("option bbb\n"); + break; + + case OPTION_C: + printf ("option c with required value '%s'\n", optarg); + break; + + case OPTION_D: + printf ("option d with optional value '%s'\n", optarg); + break; + + case OPTION_HELP: + getopt2_usage (option); + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} +#endif // TEST diff --git a/ucon64/2.0/src/misc/getopt2.h b/ucon64/2.0/src/misc/getopt2.h new file mode 100644 index 0000000..39545b7 --- /dev/null +++ b/ucon64/2.0/src/misc/getopt2.h @@ -0,0 +1,85 @@ +/* +getopt2.h - getopt1() extension + +Copyright (c) 2004 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef MISC_GETOPT2_H +#define MISC_GETOPT2_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif +#include "getopt.h" // getopt2 needs struct option from getopt1 + + +/* + Extended getopt1(), usage and workflow handling + + getopt2_usage() render usage output from st_getopt2_t array + getopt2_parse_usage() parse usage output into st_getopt2_t array (for dev) + getopt2_long() turn st_getopt2_t into struct option for getopt1_long() + getopt2_long_only() turn st_getopt2_t into struct option for getopt1_long_only() + getopt2_short() turn st_getopt2_t into short options string for getopt1_*() + getopt2_get_index_by_val() return single st_getopt2_t by val + + OPTION option marker (default: '-') + OPTION_S option marker as string (default: "-") + OPTION_LONG_S long option marker as string (default: "--") + OPTARG optarg separator (default: '=') + OPTARG_S optarg separator as string (default: "=") + + you will use THESE everywhere and you will NEVER change them + +*/ +#define OPTION '-' +#define OPTION_S "-" +#define OPTION_LONG_S "--" +#define OPTARG '=' +#define OPTARG_S "=" + +typedef struct +{ + const char *name; // see getopt() + int has_arg; // see getopt() + int *flag; // see getopt() + int val; // see getopt() + const char *arg_name; // name of the options arg as it should be + // displayed in the --help output + // "--name=arg_name" if has_arg == 1 + // "--name[=arg_name]" if has_arg == 2 + const char *help; // --help, -h, -? output for the current option + void *object; // could be used for workflow objects +} st_getopt2_t; + +extern void getopt2_usage (const st_getopt2_t *option); +#ifdef DEBUG +extern void getopt2_parse_usage (const char *usage_output); +#endif +extern int getopt2_long (struct option *long_option, const st_getopt2_t *option, int n); +extern int getopt2_long_only (struct option *long_option, const st_getopt2_t *option, int n); +extern int getopt2_short (char *short_option, const st_getopt2_t *option, int n); +extern const st_getopt2_t *getopt2_get_index_by_val (const st_getopt2_t *option, int val); + +#ifdef __cplusplus +} +#endif +#endif // MISC_GETOPT2_H diff --git a/ucon64/2.0/src/misc/itypes.h b/ucon64/2.0/src/misc/itypes.h new file mode 100644 index 0000000..3f6b651 --- /dev/null +++ b/ucon64/2.0/src/misc/itypes.h @@ -0,0 +1,57 @@ +/* +itypes.h - integer types + +Copyright (c) 1999 - 2004 NoisyB +Copyright (c) 2001 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef MISC_ITYPES_H +#define MISC_ITYPES_H + +#ifdef HAVE_CONFIG_H +#include "config.h" // HAVE_INTTYPES_H +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_INTTYPES_H +#include +#else // __MSDOS__, _WIN32 (VC++) +#ifndef OWN_INTTYPES +#define OWN_INTTYPES // signal that these are defined +typedef unsigned char uint8_t; +typedef signed char int8_t; +typedef unsigned short int uint16_t; +typedef signed short int int16_t; +typedef unsigned int uint32_t; +typedef signed int int32_t; +#ifndef _WIN32 +typedef unsigned long long int uint64_t; +typedef signed long long int int64_t; +#else +typedef unsigned __int64 uint64_t; +typedef signed __int64 int64_t; +#endif +#endif // OWN_INTTYPES +#endif + +#ifdef __cplusplus +} +#endif +#endif // MISC_ITYPES_H diff --git a/ucon64/2.0/src/misc/map.c b/ucon64/2.0/src/misc/map.c new file mode 100644 index 0000000..5eea896 --- /dev/null +++ b/ucon64/2.0/src/misc/map.c @@ -0,0 +1,129 @@ +/* +map.c - a map (associative array) implementation + +Copyright (c) 2002 - 2003 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#include +#include +#include +#include "map.h" +#if defined DJGPP && defined DLL +#include "dxedll_priv.h" +#endif + + +st_map_t * +map_create (int n_elements) +{ + st_map_t *map; + int size = sizeof (st_map_t) + n_elements * sizeof (st_map_element_t); + + if ((map = (st_map_t *) malloc (size)) == NULL) + { + fprintf (stderr, "ERROR: Not enough memory for buffer (%d bytes)\n", size); + exit (1); + } + map->data = (st_map_element_t *) (((unsigned char *) map) + sizeof (st_map_t)); + memset (map->data, MAP_FREE_KEY, n_elements * sizeof (st_map_element_t)); + map->size = n_elements; + map->cmp_key = map_cmp_key_def; + return map; +} + + +void +map_copy (st_map_t *dest, st_map_t *src) +{ + memcpy (dest->data, src->data, src->size * sizeof (st_map_element_t)); + dest->cmp_key = src->cmp_key; +} + + +int +map_cmp_key_def (void *key1, void *key2) +{ + return key1 != key2; +} + + +st_map_t * +map_put (st_map_t *map, void *key, void *object) +{ + int n = 0; + + while (n < map->size && map->data[n].key != MAP_FREE_KEY && + map->cmp_key (map->data[n].key, key)) + n++; + + if (n == map->size) // current map is full + { + int new_size = map->size + 20; + st_map_t *map2; + + map2 = map_create (new_size); + map_copy (map2, map); + free (map); + map = map2; + } + + map->data[n].key = key; + map->data[n].object = object; + + return map; +} + + +void * +map_get (st_map_t *map, void *key) +{ + int n = 0; + + while (n < map->size && map->cmp_key (map->data[n].key, key)) + n++; + + if (n == map->size) + return NULL; + + return map->data[n].object; +} + + +void +map_del (st_map_t *map, void *key) +{ + int n = 0; + + while (n < map->size && map->cmp_key (map->data[n].key, key)) + n++; + + if (n < map->size) + map->data[n].key = MAP_FREE_KEY; +} + + +void +map_dump (st_map_t *map) +{ + int n = 0; + + while (n < map->size) + { + printf ("%p -> %p\n", map->data[n].key, map->data[n].object); + n++; + } +} diff --git a/ucon64/2.0/src/misc/map.h b/ucon64/2.0/src/misc/map.h new file mode 100644 index 0000000..28b695d --- /dev/null +++ b/ucon64/2.0/src/misc/map.h @@ -0,0 +1,72 @@ +/* +map.h - a map (associative array) implementation + +Copyright (c) 2002 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef MAP_H +#define MAP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + map_create() create a new map (associative array) + map_copy() copy map src to map dest + dest must be a larger map than src + Note that this function also copies the member cmp_key. + map_put() put object in map under key + Callers should always reset the passed map pointer with the one + this function returns. This is necessary in case the map had to + be resized. + map_get() get object from map stored under key + returns NULL if there is no object with key in map + map_del() remove the object stored under key from map + map_dump() display the current contents of map + + The value MAP_FREE_KEY is reserved as a special key value. Don't use that + value. +*/ +#define MAP_FREE_KEY 0 + +typedef struct st_map_element +{ + void *key; + void *object; +} st_map_element_t; + +typedef struct st_map +{ + st_map_element_t *data; + int size; + int (*cmp_key) (void *key1, void *key2); +} st_map_t; + +extern st_map_t *map_create (int n_elements); +extern void map_copy (st_map_t *dest, st_map_t *src); +extern st_map_t *map_put (st_map_t *map, void *key, void *object); +extern int map_cmp_key_def (void *key1, void *key2); +extern void *map_get (st_map_t *map, void *key); +extern void map_del (st_map_t *map, void *key); +extern void map_dump (st_map_t *map); + +#ifdef __cplusplus +} +#endif + +#endif // MAP_H diff --git a/ucon64/2.0/src/misc/misc.c b/ucon64/2.0/src/misc/misc.c new file mode 100644 index 0000000..02b3308 --- /dev/null +++ b/ucon64/2.0/src/misc/misc.c @@ -0,0 +1,1456 @@ +/* +misc.c - miscellaneous functions + +Copyright (c) 1999 - 2004 NoisyB +Copyright (c) 2001 - 2004 dbjh +Copyright (c) 2002 - 2004 Jan-Erik Karlsson (Amiga code) + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" // USE_ZLIB +#endif +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include // va_arg() +#include // for S_IFLNK + +#ifdef __MSDOS__ +#include // delay(), milliseconds +#elif defined __unix__ +#include // usleep(), microseconds +#elif defined __BEOS__ +#include // snooze(), microseconds +// Include OS.h before misc.h, because OS.h includes StorageDefs.h which +// includes param.h which unconditionally defines MIN and MAX. +#elif defined AMIGA +#include +#include +#include +#include +#include // GetKey() +#include +#include +#elif defined _WIN32 +#include // Sleep(), milliseconds +#endif + +#ifdef USE_ZLIB +#include "archive.h" +#endif +#include "file.h" +#include "misc.h" +#include "getopt.h" // struct option + +#ifdef __CYGWIN__ // under Cygwin (gcc for Windows) we +#define USE_POLL // need poll() for kbhit(). poll() +#include // is available on Linux, not on +#endif // BeOS. DOS already has kbhit() + +#if (defined __unix__ && !defined __MSDOS__) || defined __BEOS__ || \ + defined __APPLE__ // Mac OS X actually +#include +typedef struct termios tty_t; +#endif + + +#ifdef DJGPP +#include // needed for __dpmi_int() by ansi_init() +#ifdef DLL +#include "dxedll_priv.h" +#endif +#endif + + +#ifdef MAXBUFSIZE +#undef MAXBUFSIZE +#endif // MAXBUFSIZE +#define MAXBUFSIZE 32768 + + +extern int errno; + +typedef struct st_func_node +{ + void (*func) (void); + struct st_func_node *next; +} st_func_node_t; + +static st_func_node_t func_list = { NULL, NULL }; +static int func_list_locked = 0; +static int misc_ansi_color = 0; + +#if (defined __unix__ && !defined __MSDOS__) || defined __BEOS__ || \ + defined __APPLE__ // Mac OS X actually +static void set_tty (tty_t *param); +#endif + + +#if defined _WIN32 && defined USE_ANSI_COLOR +int +vprintf2 (const char *format, va_list argptr) +// Cheap hack to get the Visual C++ and MinGW ports support "ANSI colors". +// Cheap, because it only supports the ANSI escape sequences uCON64 uses. +{ +#undef printf +#undef fprintf + int n_chars = 0, n_ctrl = 0, n_print, done = 0; + char output[MAXBUFSIZE], *ptr, *ptr2; + HANDLE stdout_handle; + CONSOLE_SCREEN_BUFFER_INFO info; + WORD org_attr, new_attr = 0; + + n_chars = _vsnprintf (output, MAXBUFSIZE, format, argptr); + if (n_chars == -1) + { + fprintf (stderr, "INTERNAL ERROR: Output buffer in vprintf2() is too small (%d bytes).\n" + " Please send a bug report\n", MAXBUFSIZE); + exit (1); + } + + if ((ptr = strchr (output, 0x1b)) == NULL) + fputs (output, stdout); + else + { + stdout_handle = GetStdHandle (STD_OUTPUT_HANDLE); + GetConsoleScreenBufferInfo (stdout_handle, &info); + org_attr = info.wAttributes; + + if (ptr > output) + { + *ptr = 0; + fputs (output, stdout); + *ptr = 0x1b; + } + while (!done) + { + if (memcmp (ptr, "\x1b[0m", 4) == 0) + { + new_attr = org_attr; + n_ctrl = 4; + } + else if (memcmp (ptr, "\x1b[01;31m", 8) == 0) + { + new_attr = FOREGROUND_INTENSITY | FOREGROUND_RED; + n_ctrl = 8; + } + else if (memcmp (ptr, "\x1b[01;32m", 8) == 0) + { + new_attr = FOREGROUND_INTENSITY | FOREGROUND_GREEN; + n_ctrl = 8; + } + else if (memcmp (ptr, "\x1b[01;33m", 8) == 0) // bright yellow + { + new_attr = FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN; + n_ctrl = 8; + } + else if (memcmp (ptr, "\x1b[31;41m", 8) == 0) + { + new_attr = FOREGROUND_RED | BACKGROUND_RED; + n_ctrl = 8; + } + else if (memcmp (ptr, "\x1b[32;42m", 8) == 0) + { + new_attr = FOREGROUND_GREEN | BACKGROUND_GREEN; + n_ctrl = 8; + } + else if (memcmp (ptr, "\x1b[30;41m", 8) == 0) // 30 = foreground black + { + new_attr = BACKGROUND_RED; + n_ctrl = 8; + } + else if (memcmp (ptr, "\x1b[30;42m", 8) == 0) + { + new_attr = BACKGROUND_GREEN; + n_ctrl = 8; + } + else if (*ptr == 0x1b) + { + new_attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + SetConsoleTextAttribute (stdout_handle, new_attr); + printf ("\n" + "INTERNAL WARNING: vprintf2() encountered an unsupported ANSI escape sequence\n" + " Please send a bug report\n"); + n_ctrl = 0; + } + SetConsoleTextAttribute (stdout_handle, new_attr); + + ptr2 = strchr (ptr + 1, 0x1b); + if (ptr2) + n_print = ptr2 - ptr; + else + { + n_print = strlen (ptr); + done = 1; + } + + ptr[n_print] = 0; + ptr += n_ctrl; + fputs (ptr, stdout); + (ptr - n_ctrl)[n_print] = 0x1b; + ptr = ptr2; + } + } + return n_chars; +#define printf printf2 +#define fprintf fprintf2 +} + + +int +printf2 (const char *format, ...) +{ + va_list argptr; + int n_chars; + + va_start (argptr, format); + n_chars = vprintf2 (format, argptr); + va_end (argptr); + return n_chars; +} + + +int +fprintf2 (FILE *file, const char *format, ...) +{ + va_list argptr; + int n_chars; + + va_start (argptr, format); + if (file != stdout) + n_chars = vfprintf (file, format, argptr); + else + n_chars = vprintf2 (format, argptr); + va_end (argptr); + return n_chars; +} +#endif // defined _WIN32 && defined USE_ANSI_COLOR + + +void +clear_line (void) +/* + This function is used to fix a problem when using the MinGW or Visual C++ + port under Windows 98 (probably Windows 95 too) while ANSI.SYS is not loaded. + If a line contains colors, printed with printf() or fprintf() (actually + printf2() or fprintf2()), it cannot be cleared by printing spaces on the same + line. A solution is using SetConsoleTextAttribute(). + The problem doesn't occur if ANSI.SYS is loaded. It also doesn't occur under + Windows XP, even if ANSI.SYS isn't loaded. + We print 79 spaces (not 80), because under command.com the cursor advances to + the next line if we print something on the 80th column (in 80 column mode). + This doesn't happen under xterm. +*/ +{ +#if !defined _WIN32 || !defined USE_ANSI_COLOR + fputs ("\r \r", stdout); +#else + WORD org_attr; + CONSOLE_SCREEN_BUFFER_INFO info; + HANDLE stdout_handle = GetStdHandle (STD_OUTPUT_HANDLE); + GetConsoleScreenBufferInfo (stdout_handle, &info); + org_attr = info.wAttributes; + SetConsoleTextAttribute (stdout_handle, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + fputs ("\r \r", stdout); + SetConsoleTextAttribute (stdout_handle, org_attr); +#endif +} + + +int +ansi_init (void) +{ + int result = isatty (STDOUT_FILENO); + +#ifdef DJGPP + if (result) + { + // Don't use __MSDOS__, because __dpmi_regs and __dpmi_int are DJGPP specific + __dpmi_regs reg; + + reg.x.ax = 0x1a00; // DOS 4.0+ ANSI.SYS installation check + __dpmi_int (0x2f, ®); + if (reg.h.al != 0xff) // AL == 0xff if installed + result = 0; + } +#endif + + misc_ansi_color = result; + + return result; +} + + +void +dumper (FILE *output, const void *buffer, size_t bufferlen, int virtual_start, + unsigned int flags) +// Do NOT use DUMPER_PRINT in uCON64 code - dbjh +{ +#define DUMPER_REPLACER ('.') + size_t pos; + char buf[17]; + const unsigned char *p = (const unsigned char *) buffer; + + memset (buf, 0, sizeof (buf)); + for (pos = 0; pos < bufferlen; pos++, p++) + if (flags & DUMPER_PRINT) + { + fprintf (output, "%c", isprint (*p) || +#ifdef USE_ANSI_COLOR + *p == 0x1b || // ESC +#endif + isspace (*p) ? *p : DUMPER_REPLACER); + } + else if (flags & DUMPER_DUAL) + { + if (!(pos & 3)) + fprintf (output, (flags & DUMPER_DEC_COUNT ? "%010d " : "%08x "), + (int) (pos + virtual_start)); + + fprintf (output, "%02x %08d ", + *p, + ((*p >> 7) & 1) * 10000000 + + ((*p >> 6) & 1) * 1000000 + + ((*p >> 5) & 1) * 100000 + + ((*p >> 4) & 1) * 10000 + + ((*p >> 3) & 1) * 1000 + + ((*p >> 2) & 1) * 100 + + ((*p >> 1) & 1) * 10 + + (*p & 1)); + + *(buf + (pos & 3)) = isprint (*p) ? *p : DUMPER_REPLACER; + if (!((pos + 1) & 3)) + fprintf (output, "%s\n", buf); + } + else if (flags & DUMPER_CODE) + { + fprintf (output, "0x%02x, ", *p); + + if (!((pos + 1) & 7)) + fprintf (output, (flags & DUMPER_DEC_COUNT ? "// (%d) 0x%x\n" : "// 0x%x (%d)\n"), + (int) (pos + virtual_start + 1), + (int) (pos + virtual_start + 1)); + } + else // if (flags & DUMPER_HEX) // default + { + if (!(pos & 15)) + fprintf (output, (flags & DUMPER_DEC_COUNT ? "%08d " : "%08x "), + (int) (pos + virtual_start)); + + fprintf (output, (pos + 1) & 3 ? "%02x " : "%02x ", *p); + + *(buf + (pos & 15)) = isprint (*p) ? *p : DUMPER_REPLACER; + if (!((pos + 1) & 15)) + fprintf (output, "%s\n", buf); + } + + if (flags & DUMPER_PRINT) + return; + else if (flags & DUMPER_DUAL) + { + if (pos & 3) + { + *(buf + (pos & 3)) = 0; + fprintf (output, "%s\n", buf); + } + } + else if (flags & DUMPER_CODE) + return; + else // if (flags & DUMPER_HEX) // default + { + if (pos & 15) + { + *(buf + (pos & 15)) = 0; + fprintf (output, "%s\n", buf); + } + } +} + + +int +change_mem (char *buf, int bufsize, char *searchstr, int strsize, + char wc, char esc, char *newstr, int newsize, int offset, ...) +// convenience wrapper for change_mem2() +{ + va_list argptr; + int i, n_esc = 0, retval; + st_cm_set_t *sets; + + va_start (argptr, offset); + for (i = 0; i < strsize; i++) + if (searchstr[i] == esc) + n_esc++; + + sets = (st_cm_set_t *) malloc (n_esc * sizeof (st_cm_set_t)); + va_start (argptr, offset); + for (i = 0; i < n_esc; i++) + { + sets[i].data = va_arg (argptr, char *); // get next set of characters + sets[i].size = va_arg (argptr, int); // get set size + } + va_end (argptr); + retval = change_mem2 (buf, bufsize, searchstr, strsize, wc, esc, newstr, + newsize, offset, sets); + free (sets); + return retval; +} + + +int +change_mem2 (char *buf, int bufsize, char *searchstr, int strsize, char wc, + char esc, char *newstr, int newsize, int offset, st_cm_set_t *sets) +/* + Search for all occurrences of string searchstr in buf and replace newsize + bytes in buf by copying string newstr to the end of the found search string + in buf plus offset. + If searchstr contains wildcard characters wc, then n wildcard characters in + searchstr match any n characters in buf. + If searchstr contains escape characters esc, sets must point to an array of + sets. sets must contain as many elements as there are escape characters in + searchstr. searchstr matches for an escape character if one of the characters + in sets[i]->data matches. + Note that searchstr is not necessarily a C string; it may contain one or more + zero bytes as strsize indicates the length. + offset is the relative offset from the last character in searchstring and may + have a negative value. + The return value is the number of times a match was found. + This function was written to patch SNES ROM dumps. It does basically the same + as the old uCON does, with one exception, the line with: + bufpos -= n_wc; + + As stated in the comment, this causes the search to restart at the first + wildcard character of the sequence of wildcards that was most recently + skipped if the current character in buf didn't match the current character + in searchstr. This makes change_mem() behave a bit more intuitive. For + example + char str[] = "f foobar means..."; + change_mem (str, strlen (str), "f**bar", 6, '*', '!', "XXXXXXXX", 8, 2, NULL); + finds and changes "foobar means..." into "foobar XXXXXXXX", while with uCON's + algorithm it would not (but does the job good enough for patching SNES ROMs). + + One example of using sets: + char str[] = "fu-bar is the same as foobar "; + st_cm_set_t sets[] = {{"o-", 2}, {"uo", 2}}; + change_mem (str, strlen (str), "f!!", 3, '*', '!', "fighter", 7, 1, sets); + This changes str into "fu-fighter is the same as foofighter". +*/ +{ + char *set; + int bufpos, strpos = 0, pos_1st_esc = -1, setsize, i, n_wc, n_matches = 0, + setindex = 0; + + for (bufpos = 0; bufpos < bufsize; bufpos++) + { + if (strpos == 0 && searchstr[0] != esc && searchstr[0] != wc) + while (bufpos < bufsize && searchstr[0] != buf[bufpos]) + bufpos++; + + // handle escape character in searchstr + while (searchstr[strpos] == esc && bufpos < bufsize) + { + if (strpos == pos_1st_esc) + setindex = 0; // reset argument pointer + if (pos_1st_esc == -1) + pos_1st_esc = strpos; + + set = sets[setindex].data; // get next set of characters + setsize = sets[setindex].size; // get set size + setindex++; + i = 0; + // see if buf[bufpos] matches with any character in current set + while (i < setsize && buf[bufpos] != set[i]) + i++; + if (i == setsize) + break; // buf[bufpos] didn't match with any char + + if (strpos == strsize - 1) // check if we are at the end of searchstr + { + memcpy (buf + bufpos + offset, newstr, newsize); + n_matches++; + break; + } + + strpos++; + bufpos++; + } + if (searchstr[strpos] == esc) + { + strpos = 0; + continue; + } + + // skip wildcards in searchstr + n_wc = 0; + while (searchstr[strpos] == wc && bufpos < bufsize) + { + if (strpos == strsize - 1) // check if at end of searchstr + { + memcpy (buf + bufpos + offset, newstr, newsize); + n_matches++; + break; + } + + strpos++; + bufpos++; + n_wc++; + } + if (bufpos == bufsize) + break; + if (searchstr[strpos] == wc) + { + strpos = 0; + continue; + } + + if (searchstr[strpos] == esc) + { + bufpos--; // current char has to be checked, but `for' + continue; // increments bufpos + } + + // no escape char, no wildcard -> normal character + if (searchstr[strpos] == buf[bufpos]) + { + if (strpos == strsize - 1) // check if at end of searchstr + { + memcpy (buf + bufpos + offset, newstr, newsize); + n_matches++; + strpos = 0; + } + else + strpos++; + } + else + { + bufpos -= n_wc; // scan the most recent wildcards too if + if (strpos > 0) // the character didn't match + { + bufpos--; // current char has to be checked, but `for' + strpos = 0; // increments bufpos + } + } + } + + return n_matches; +} + + +int +build_cm_patterns (st_cm_pattern_t **patterns, const char *filename, int verbose) +/* + This function goes a bit over the top what memory allocation technique + concerns, but at least it's stable. + Note the important difference between (*patterns)[0].n_sets and + patterns[0]->n_sets (not especially that member). I (dbjh) am too ashamed to + tell how long it took me to finally realise that... +*/ +{ + char src_name[FILENAME_MAX], line[MAXBUFSIZE], buffer[MAXBUFSIZE], + *token, *last, *ptr; + int line_num = 0, n_sets, n_codes = 0, n, currentsize1, requiredsize1, + currentsize2, requiredsize2, currentsize3, requiredsize3; + FILE *srcfile; + + strcpy (src_name, filename); + if (access (src_name, F_OK | R_OK)) + return -1; // NOT an error, it's optional + + if ((srcfile = fopen (src_name, "r")) == NULL) // open in text mode + { + fprintf (stderr, "ERROR: Can't open \"%s\" for reading\n", src_name); + return -1; + } + + *patterns = NULL; + currentsize1 = requiredsize1 = 0; + while (fgets (line, sizeof line, srcfile) != NULL) + { + line_num++; + n_sets = 0; + + ptr = line + strspn (line, "\t "); + if (*ptr == '#' || *ptr == '\n' || *ptr == '\r') + continue; + if ((ptr = strpbrk (line, "\n\r#"))) // text after # is comment + *ptr = 0; + + requiredsize1 += sizeof (st_cm_pattern_t); + if (requiredsize1 > currentsize1) + { + currentsize1 = requiredsize1 + 10 * sizeof (st_cm_pattern_t); + if (!(*patterns = (st_cm_pattern_t *) realloc (*patterns, currentsize1))) + { + fprintf (stderr, "ERROR: Not enough memory for buffer (%d bytes)\n", currentsize1); + return -1; + } + } + + (*patterns)[n_codes].search = NULL; + currentsize2 = 0; + requiredsize2 = 1; // for string terminator + n = 0; + strcpy (buffer, line); + token = strtok (buffer, ":"); + token = strtok (token, " "); +// printf ("token: \"%s\"\n", token); + last = token; + // token is never NULL here (yes, tested with empty files and such) + do + { + requiredsize2++; + if (requiredsize2 > currentsize2) + { + currentsize2 = requiredsize2 + 10; + if (!((*patterns)[n_codes].search = + (char *) realloc ((*patterns)[n_codes].search, currentsize2))) + { + fprintf (stderr, "ERROR: Not enough memory for buffer (%d bytes)\n", currentsize2); + free (*patterns); + *patterns = NULL; + return -1; + } + } + (*patterns)[n_codes].search[n] = (unsigned char) strtol (token, NULL, 16); + n++; + } + while ((token = strtok (NULL, " "))); + (*patterns)[n_codes].search_size = n; // size in bytes + + strcpy (buffer, line); + token = strtok (last, ":"); + token = strtok (NULL, ":"); + token = strtok (token, " "); + last = token; + if (!token) + { + printf ("WARNING: Line %d is invalid, no wildcard value is specified\n", + line_num); + continue; + } + (*patterns)[n_codes].wildcard = (char) strtol (token, NULL, 16); + + strcpy (buffer, line); + token = strtok (last, ":"); + token = strtok (NULL, ":"); + token = strtok (token, " "); + last = token; + if (!token) + { + printf ("WARNING: Line %d is invalid, no escape value is specified\n", + line_num); + continue; + } + (*patterns)[n_codes].escape = (char) strtol (token, NULL, 16); + + strcpy (buffer, line); + token = strtok (last, ":"); + token = strtok (NULL, ":"); + token = strtok (token, " "); + last = token; + if (!token) + { + printf ("WARNING: Line %d is invalid, no replacement is specified\n", line_num); + continue; + } + (*patterns)[n_codes].replace = NULL; + currentsize2 = 0; + requiredsize2 = 1; // for string terminator + n = 0; + do + { + requiredsize2++; + if (requiredsize2 > currentsize2) + { + currentsize2 = requiredsize2 + 10; + if (!((*patterns)[n_codes].replace = + (char *) realloc ((*patterns)[n_codes].replace, currentsize2))) + { + fprintf (stderr, "ERROR: Not enough memory for buffer (%d bytes)\n", currentsize2); + free ((*patterns)[n_codes].search); + free (*patterns); + *patterns = NULL; + return -1; + } + } + (*patterns)[n_codes].replace[n] = (unsigned char) strtol (token, NULL, 16); + n++; + } + while ((token = strtok (NULL, " "))); + (*patterns)[n_codes].replace_size = n; // size in bytes + + strcpy (buffer, line); + token = strtok (last, ":"); + token = strtok (NULL, ":"); + token = strtok (token, " "); + last = token; + if (!token) + { + printf ("WARNING: Line %d is invalid, no offset is specified\n", line_num); + continue; + } + (*patterns)[n_codes].offset = strtol (token, NULL, 10); // yes, offset is decimal + + if (verbose) + { + printf ("\n" + "line: %d\n" + "searchstring: ", + line_num); + for (n = 0; n < (*patterns)[n_codes].search_size; n++) + printf ("%02x ", (unsigned char) (*patterns)[n_codes].search[n]); + printf ("(%d)\n" + "wildcard: %02x\n" + "escape: %02x\n" + "replacement: ", + (*patterns)[n_codes].search_size, + (unsigned char) (*patterns)[n_codes].wildcard, + (unsigned char) (*patterns)[n_codes].escape); + for (n = 0; n < (*patterns)[n_codes].replace_size; n++) + printf ("%02x ", (unsigned char) (*patterns)[n_codes].replace[n]); + printf ("(%d)\n" + "offset: %d\n", + (*patterns)[n_codes].replace_size, + (*patterns)[n_codes].offset); + } + + (*patterns)[n_codes].sets = NULL; + currentsize2 = 0; + requiredsize2 = 1; // for string terminator + strcpy (buffer, line); + token = strtok (last, ":"); + token = strtok (NULL, ":"); + last = token; + while (token) + { + requiredsize2 += sizeof (st_cm_set_t); + if (requiredsize2 > currentsize2) + { + currentsize2 = requiredsize2 + 10 * sizeof (st_cm_set_t); + if (!((*patterns)[n_codes].sets = (st_cm_set_t *) + realloc ((*patterns)[n_codes].sets, currentsize2))) + { + fprintf (stderr, "ERROR: Not enough memory for buffer (%d bytes)\n", currentsize2); + free ((*patterns)[n_codes].replace); + free ((*patterns)[n_codes].search); + free (*patterns); + *patterns = NULL; + return -1; + } + } + + (*patterns)[n_codes].sets[n_sets].data = NULL; + currentsize3 = 0; + requiredsize3 = 1; // for string terminator + n = 0; + token = strtok (token, " "); + do + { + requiredsize3++; + if (requiredsize3 > currentsize3) + { + currentsize3 = requiredsize3 + 10; + if (!((*patterns)[n_codes].sets[n_sets].data = (char *) + realloc ((*patterns)[n_codes].sets[n_sets].data, currentsize3))) + { + fprintf (stderr, "ERROR: Not enough memory for buffer (%d bytes)\n", currentsize3); + free ((*patterns)[n_codes].sets); + free ((*patterns)[n_codes].replace); + free ((*patterns)[n_codes].search); + free (*patterns); + *patterns = NULL; + return -1; + } + } + (*patterns)[n_codes].sets[n_sets].data[n] = + (unsigned char) strtol (token, NULL, 16); + n++; + } + while ((token = strtok (NULL, " "))); + (*patterns)[n_codes].sets[n_sets].size = n; + + if (verbose) + { + printf ("set: "); + for (n = 0; n < (*patterns)[n_codes].sets[n_sets].size; n++) + printf ("%02x ", (unsigned char) (*patterns)[n_codes].sets[n_sets].data[n]); + printf ("(%d)\n", (*patterns)[n_codes].sets[n_sets].size); + } + + strcpy (buffer, line); + token = strtok (last, ":"); + token = strtok (NULL, ":"); + last = token; + + n_sets++; + } + (*patterns)[n_codes].n_sets = n_sets; + + n_codes++; + } + fclose (srcfile); + return n_codes; +} + + +void +cleanup_cm_patterns (st_cm_pattern_t **patterns, int n_patterns) +{ + int n, m; + for (n = 0; n < n_patterns; n++) + { + free ((*patterns)[n].search); + (*patterns)[n].search = NULL; + free ((*patterns)[n].replace); + (*patterns)[n].replace = NULL; + for (m = 0; m < (*patterns)[n].n_sets; m++) + { + free ((*patterns)[n].sets[m].data); + (*patterns)[n].sets[m].data = NULL; + } + free ((*patterns)[n].sets); + (*patterns)[n].sets = NULL; + } + free (*patterns); + *patterns = NULL; +} + + +#if 1 +int +gauge (FILE *output, time_t start_time, int pos, int size, unsigned int flags) +{ +#define GAUGE_LENGTH ((int64_t) 24) + int curr, bps, left, p, percentage; + char progress[1024]; + + if (pos > size || !size) + return -1; + + percentage = (int) ((((int64_t) 100) * pos) / size); + + if (/* output != stdout && */ flags & GAUGE_PERCENT) + { + fprintf (output, "%u\n", percentage); + fflush (output); + + return 0; + } + + if ((curr = time (0) - start_time) == 0) + curr = 1; // `round up' to at least 1 sec (no division + // by zero below) + bps = pos / curr; // # bytes/second (average transfer speed) + left = size - pos; + left /= bps ? bps : 1; + + p = (int) ((GAUGE_LENGTH * pos) / size); + *progress = 0; + strncat (progress, "========================", p); + +// if (flags & GAUGE_ANSI) + if (misc_ansi_color) + { + progress[p] = 0; + if (p < GAUGE_LENGTH) + strcat (progress, "\x1b[31;41m"); + } + + strncat (&progress[p], "------------------------", (int) (GAUGE_LENGTH - p)); + + fprintf (output, +// (flags & GAUGE_ANSI) ? + misc_ansi_color ? + "\r%10d Bytes [\x1b[32;42m%s\x1b[0m] %d%%, BPS=%d, " : + "\r%10d Bytes [%s] %d%%, BPS=%d, ", pos, progress, percentage, bps); + + if (pos == size) + fprintf (output, "TOTAL=%02d:%02d", curr / 60, curr % 60); // DON'T print a newline + else if (pos) // -> gauge can be cleared + fprintf (output, "ETA=%02d:%02d ", left / 60, left % 60); + else // don't display a nonsense ETA + fputs ("ETA=? ", stdout); + + fflush (output); + + return 0; +} +#else +int +gauge (time_t init_time, int pos, int size, unsigned int flags) +{ +#define GAUGE_LENGTH ((int64_t) 24) + + int curr, bps, left, p, percentage; + char progress[MAXBUFSIZE]; + + if (pos > size || !size) + return -1; + + if ((curr = time (0) - init_time) == 0) + curr = 1; // `round up' to at least 1 sec (no division + // by zero below) + bps = pos / curr; // # bytes/second (average transfer speed) + left = size - pos; + left /= bps ? bps : 1; + + p = (int) ((GAUGE_LENGTH * pos) / size); + *progress = 0; + strncat (progress, "========================", p); + + if (misc_ansi_color) + { + progress[p] = 0; + if (p < GAUGE_LENGTH) + strcat (progress, "\x1b[31;41m"); + } + + strncat (&progress[p], "------------------------", (int) (GAUGE_LENGTH - p)); + + percentage = (int) ((((int64_t) 100) * pos) / size); + + printf ( + misc_ansi_color ? "\r%10d Bytes [\x1b[32;42m%s\x1b[0m] %d%%, BPS=%d, " : + "\r%10d Bytes [%s] %d%%, BPS=%d, ", pos, progress, percentage, bps); + + if (pos == size) + printf ("TOTAL=%02d:%02d", curr / 60, curr % 60); // DON'T print a newline + else if (pos) // -> gauge can be cleared + printf ("ETA=%02d:%02d ", left / 60, left % 60); + else // don't display a nonsense ETA + fputs ("ETA=? ", stdout); + + fflush (stdout); + + return 0; +} +#endif + + +#ifdef __CYGWIN__ +/* + Weird problem with combination Cygwin uCON64 exe and cmd.exe (Bash is ok): + When a string with "e (e with diaeresis, one character) is read from an + environment variable, the character isn't the right character for accessing + the file system. We fix this. + TODO: fix the same problem for other non-ASCII characters (> 127). +*/ +char * +fix_character_set (char *str) +{ + int n, l = strlen (str); + unsigned char *ptr = (unsigned char *) str; + + for (n = 0; n < l; n++) + { + if (ptr[n] == 0x89) // e diaeresis + ptr[n] = 0xeb; + else if (ptr[n] == 0x84) // a diaeresis + ptr[n] = 0xe4; + else if (ptr[n] == 0x8b) // i diaeresis + ptr[n] = 0xef; + else if (ptr[n] == 0x94) // o diaeresis + ptr[n] = 0xf6; + else if (ptr[n] == 0x81) // u diaeresis + ptr[n] = 0xfc; + } + + return str; +} +#endif + + +char * +getenv2 (const char *variable) +/* + getenv() suitable for enviroments w/o HOME, TMP or TEMP variables. + The caller should copy the returned string to it's own memory, because this + function will overwrite that memory on the next call. + Note that this function never returns NULL. +*/ +{ + char *tmp; + static char value[MAXBUFSIZE]; +#if defined __CYGWIN__ || defined __MSDOS__ +/* + Under DOS and Windows the environment variables are not stored in a case + sensitive manner. The run-time systems of DJGPP and Cygwin act as if they are + stored in upper case. Their getenv() however *is* case sensitive. We fix this + by changing all characters of the search string (variable) to upper case. + + Note that under Cygwin's Bash environment variables *are* stored in a case + sensitive manner. +*/ + char tmp2[MAXBUFSIZE]; + + strcpy (tmp2, variable); + variable = strupr (tmp2); // DON'T copy the string into variable +#endif // (variable itself is local) + + *value = 0; + + if ((tmp = getenv (variable)) != NULL) + strcpy (value, tmp); + else + { + if (!strcmp (variable, "HOME")) + { + if ((tmp = getenv ("USERPROFILE")) != NULL) + strcpy (value, tmp); + else if ((tmp = getenv ("HOMEDRIVE")) != NULL) + { + strcpy (value, tmp); + tmp = getenv ("HOMEPATH"); + strcat (value, tmp ? tmp : FILE_SEPARATOR_S); + } + else + /* + Don't just use C:\\ under DOS, the user might not have write access + there (Windows NT DOS-box). Besides, it would make uCON64 behave + differently on DOS than on the other platforms. + Returning the current directory when none of the above environment + variables are set can be seen as a feature. A frontend could execute + uCON64 with an environment without any of the environment variables + set, so that the directory from where uCON64 starts will be used. + */ + { + char c; + getcwd (value, FILENAME_MAX); + c = toupper (*value); + // if current dir is root dir strip problematic ending slash (DJGPP) + if (c >= 'A' && c <= 'Z' && + value[1] == ':' && value[2] == '/' && value[3] == 0) + value[2] = 0; + } + } + + if (!strcmp (variable, "TEMP") || !strcmp (variable, "TMP")) + { +#if defined __MSDOS__ || defined __CYGWIN__ + /* + DJGPP and (yet another) Cygwin quirck + A trailing backslash is used to check for a directory. Normally + DJGPP's run-time system is able to handle forward slashes in paths, + but access() won't differentiate between files and dirs if a + forward slash is used. Cygwin's run-time system seems to handle + paths with forward slashes quite different from paths with + backslashes. This trick seems to work only if a backslash is used. + */ + if (access ("\\tmp\\", R_OK | W_OK) == 0) +#else + // trailing file separator to force it to be a directory + if (access (FILE_SEPARATOR_S"tmp"FILE_SEPARATOR_S, R_OK | W_OK) == 0) +#endif + strcpy (value, FILE_SEPARATOR_S"tmp"); + else + getcwd (value, FILENAME_MAX); + } + } + +#ifdef __CYGWIN__ + /* + Under certain circumstances Cygwin's run-time system returns "/" as value + of HOME while that var has not been set. To specify a root dir a path like + /cygdrive/ or simply a drive letter should be used. + */ + if (!strcmp (variable, "HOME") && !strcmp (value, "/")) + getcwd (value, FILENAME_MAX); + + return fix_character_set (value); +#else + return value; +#endif +} + + +long int +strtol2 (const char *str, char **tail) +{ + long int i; + + return ((i = strtol (str, tail, 10))) ? i : strtol (str, tail, 16); +} + + +#if (defined __unix__ && !defined __MSDOS__) || defined __BEOS__ || \ + defined __APPLE__ // Mac OS X actually +static int oldtty_set = 0, stdin_tty = 1; // 1 => stdin is a tty, 0 => it's not +static tty_t oldtty, newtty; + + +void +set_tty (tty_t *param) +{ + if (stdin_tty && tcsetattr (STDIN_FILENO, TCSANOW, param) == -1) + { + fprintf (stderr, "ERROR: Could not set tty parameters\n"); + exit (100); + } +} + + +/* + This code compiles with DJGPP, but is not neccesary. Our kbhit() conflicts + with DJGPP's one, so it won't be used for that function. Perhaps it works + for making getchar() behave like getch(), but that's a bit pointless. +*/ +void +init_conio (void) +{ + if (!isatty (STDIN_FILENO)) + { + stdin_tty = 0; + return; // rest is nonsense if not a tty + } + + if (tcgetattr (STDIN_FILENO, &oldtty) == -1) + { + fprintf (stderr, "ERROR: Could not get tty parameters\n"); + exit (101); + } + oldtty_set = 1; + + if (register_func (deinit_conio) == -1) + { + fprintf (stderr, "ERROR: Could not register function with register_func()\n"); + exit (102); + } + + newtty = oldtty; + newtty.c_lflag &= ~(ICANON | ECHO); + newtty.c_lflag |= ISIG; + newtty.c_cc[VMIN] = 1; // if VMIN != 0, read calls + newtty.c_cc[VTIME] = 0; // block (wait for input) + + set_tty (&newtty); +} + + +void +deinit_conio (void) +{ + if (oldtty_set) + { + tcsetattr (STDIN_FILENO, TCSAFLUSH, &oldtty); + oldtty_set = 0; + } +} + + +#if defined __CYGWIN__ && !defined USE_POLL +#warning kbhit() does not work properly in Cygwin executable if USE_POLL is not defined +#endif +// this kbhit() conflicts with DJGPP's one +int +kbhit (void) +{ +#ifdef USE_POLL + struct pollfd fd; + + fd.fd = STDIN_FILENO; + fd.events = POLLIN; + fd.revents = 0; + + return poll (&fd, 1, 0) > 0; +#else + tty_t tmptty = newtty; + int ch, key_pressed; + + tmptty.c_cc[VMIN] = 0; // doesn't work as expected under + set_tty (&tmptty); // Cygwin (define USE_POLL) + + if ((ch = fgetc (stdin)) != EOF) + { + key_pressed = 1; + ungetc (ch, stdin); + } + else + key_pressed = 0; + + set_tty (&newtty); + + return key_pressed; +#endif +} +#elif defined AMIGA // (__unix__ && !__MSDOS__) || +int // __BEOS__ ||__APPLE__ +kbhit (void) +{ + return GetKey () != 0xff ? 1 : 0; +} + + +int +getch (void) +{ + BPTR con_fileh; + int temp; + + con_fileh = Input (); + // put the console into RAW mode which makes getchar() behave like getch()? + if (con_fileh) + SetMode (con_fileh, 1); + temp = getchar (); + // put the console out of RAW mode (might make sense) + if (con_fileh) + SetMode (con_fileh, 0); + + return temp; +} +#endif // AMIGA + + +#if defined __unix__ && !defined __MSDOS__ +int +drop_privileges (void) +{ + uid_t uid; + gid_t gid; + + uid = getuid (); + if (setuid (uid) == -1) + { + fprintf (stderr, "ERROR: Could not set uid\n"); + return 1; + } + gid = getgid (); // This shouldn't be necessary + if (setgid (gid) == -1) // if `make install' was + { // used, but just in case + fprintf (stderr, "ERROR: Could not set gid\n"); // (root did `chmod +s') + return 1; + } + + return 0; +} +#endif + + +int +register_func (void (*func) (void)) +{ + st_func_node_t *func_node = &func_list, *new_node; + + while (func_node->next != NULL) + func_node = func_node->next; + + if ((new_node = (st_func_node_t *) malloc (sizeof (st_func_node_t))) == NULL) + return -1; + + new_node->func = func; + new_node->next = NULL; + func_node->next = new_node; + return 0; +} + + +int +unregister_func (void (*func) (void)) +{ + st_func_node_t *func_node = &func_list, *prev_node = &func_list; + + while (func_node->next != NULL && func_node->func != func) + { + prev_node = func_node; + func_node = func_node->next; + } + if (func_node->func != func) + return -1; + + if (!func_list_locked) + { + prev_node->next = func_node->next; + free (func_node); + return 0; + } + else + return -1; +} + + +void +handle_registered_funcs (void) +{ + st_func_node_t *func_node = &func_list; + + func_list_locked = 1; + while (func_node->next != NULL) + { + func_node = func_node->next; // first node contains no valid address + if (func_node->func != NULL) + func_node->func (); + } + func_list_locked = 0; +} + + +void +wait2 (int nmillis) +{ +#ifdef __MSDOS__ + delay (nmillis); +#elif defined __unix__ || defined __APPLE__ // Mac OS X actually + usleep (nmillis * 1000); +#elif defined __BEOS__ + snooze (nmillis * 1000); +#elif defined AMIGA + Delay (nmillis * 1000); +#elif defined _WIN32 + Sleep (nmillis); +#else +#ifdef __GNUC__ +#warning Please provide a wait2() implementation +#else +#pragma message ("Please provide a wait2() implementation") +#endif + volatile int n; + for (n = 0; n < nmillis * 65536; n++) + ; +#endif +} + + +#ifdef _WIN32 +int +truncate (const char *path, off_t size) +{ + int retval; + HANDLE file = CreateFile (path, GENERIC_WRITE, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (file == INVALID_HANDLE_VALUE) + return -1; + + SetFilePointer (file, size, 0, FILE_BEGIN); + retval = SetEndOfFile (file); // returns nonzero on success + CloseHandle (file); + + return retval ? 0 : -1; // truncate() returns zero on success +} + + +int +sync (void) +{ + _commit (fileno (stdout)); + _commit (fileno (stderr)); + fflush (NULL); // flushes all streams opened for output + return 0; +} + + +#if defined __MINGW32__ && defined DLL +// Ugly hack in order to fix something in zlib +FILE * +fdopen (int fd, const char *mode) +{ + return _fdopen (fd, mode); +} +#endif + + +#elif defined AMIGA // _WIN32 +int +truncate (const char *path, off_t size) +{ + BPTR fh; + ULONG newsize; + + if (!(fh = Open (path, MODE_OLDFILE))) + return -1; + + newsize = SetFileSize (fh, size, OFFSET_BEGINNING); + Close (fh); + + return newsize == (ULONG) size ? 0 : -1; // truncate() returns zero on success +} + + +int +chmod (const char *path, mode_t mode) +{ + if (!SetProtection ((STRPTR) path, + ((mode & S_IRUSR ? 0 : FIBF_READ) | + (mode & S_IWUSR ? 0 : FIBF_WRITE | FIBF_DELETE) | + (mode & S_IXUSR ? 0 : FIBF_EXECUTE) | + (mode & S_IRGRP ? FIBF_GRP_READ : 0) | + (mode & S_IWGRP ? FIBF_GRP_WRITE | FIBF_GRP_DELETE : 0) | + (mode & S_IXGRP ? FIBF_GRP_EXECUTE : 0) | + (mode & S_IROTH ? FIBF_OTR_READ : 0) | + (mode & S_IWOTH ? FIBF_OTR_WRITE | FIBF_OTR_DELETE : 0) | + (mode & S_IXOTH ? FIBF_OTR_EXECUTE : 0)))) + return -1; + else + return 0; +} + + +void +sync (void) +{ +} + + +int +readlink (const char *path, char *buf, int bufsize) +{ + (void) path; // warning remover + (void) buf; // idem + (void) bufsize; // idem + // always return -1 as if anything passed to it isn't a soft link + return -1; +} + + +// custom _popen() and _pclose(), because the standard ones (named popen() and +// pclose()) are buggy +FILE * +_popen (const char *path, const char *mode) +{ + int fd; + BPTR fh; + long fhflags; + char *apipe = malloc (strlen (path) + 7); + + if (!apipe) + return NULL; + + strcpy (apipe, "APIPE:"); + strcat (apipe, path); + + if (*mode == 'w') + fhflags = MODE_NEWFILE; + else + fhflags = MODE_OLDFILE; + + if (!(fh = Open (apipe, fhflags))) + return NULL; + + return fdopen (fd, mode); +} + + +int +_pclose (FILE *stream) +{ + return fclose (stream); +} +#endif // AMIGA diff --git a/ucon64/2.0/src/misc/misc.h b/ucon64/2.0/src/misc/misc.h new file mode 100644 index 0000000..6a16fe9 --- /dev/null +++ b/ucon64/2.0/src/misc/misc.h @@ -0,0 +1,344 @@ +/* +misc.h - miscellaneous functions + +Copyright (c) 1999 - 2004 NoisyB +Copyright (c) 2001 - 2004 dbjh +Copyright (c) 2002 - 2004 Jan-Erik Karlsson (Amiga code) + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef MISC_H +#define MISC_H + +#ifdef HAVE_CONFIG_H +#include "config.h" // USE_ZLIB, USE_ANSI_COLOR support +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include // gauge() prototype contains time_t +#include + + +#ifdef __sun +#ifdef __SVR4 +#define __solaris__ +#endif +#endif + +#ifdef WORDS_BIGENDIAN +#undef WORDS_BIGENDIAN +#endif + +#if defined _LIBC || defined __GLIBC__ + #include + #if __BYTE_ORDER == __BIG_ENDIAN + #define WORDS_BIGENDIAN 1 + #endif +#elif defined AMIGA || defined __sparc__ || defined __BIG_ENDIAN__ || \ + defined __APPLE__ + #define WORDS_BIGENDIAN 1 +#endif + +#ifdef __MSDOS__ // __MSDOS__ must come before __unix__, + #define CURRENT_OS_S "MSDOS" // because DJGPP defines both +#elif defined __unix__ + #ifdef __CYGWIN__ + #define CURRENT_OS_S "Win32 (Cygwin)" + #elif defined __FreeBSD__ + #define CURRENT_OS_S "Unix (FreeBSD)" + #elif defined __OpenBSD__ + #define CURRENT_OS_S "Unix (OpenBSD)" + #elif defined __linux__ + #define CURRENT_OS_S "Unix (Linux)" + #elif defined __solaris__ + #ifdef __sparc__ + #define CURRENT_OS_S "Unix (Solaris/Sparc)" + #else + #define CURRENT_OS_S "Unix (Solaris/i386)" + #endif + #else + #define CURRENT_OS_S "Unix" + #endif +#elif defined _WIN32 + #ifdef __MINGW32__ + #define CURRENT_OS_S "Win32 (MinGW)" + #else + #define CURRENT_OS_S "Win32 (Visual C++)" + #endif +#elif defined __APPLE__ + #if defined __POWERPC__ || defined __ppc__ + #define CURRENT_OS_S "Apple (PPC)" + #else + #define CURRENT_OS_S "Apple" + #endif +#elif defined __BEOS__ + #define CURRENT_OS_S "BeOS" +#elif defined AMIGA + #if defined __PPC__ + #define CURRENT_OS_S "Amiga (PPC)" + #else + #define CURRENT_OS_S "Amiga (68K)" + #endif +#else + #define CURRENT_OS_S "?" +#endif + +/* + dumper() dump n bytes of buffer + you can use here a virtual_start for the displayed counter + DUMPER_HEX + dump in hex (base: 16) (default) + DUMPER_DUAL + dump in dual (base: 2) + DUMPER_CODE + dump as C code + DUMPER_PRINT + printf() buffer after chars passed isprint() and isspace() + other chars will be printed as dots '.' + do NOT use DUMPER_PRINT in uCON64 code - dbjh + DUMPER_HEX_COUNT + show position as hex value (default) + DUMPER_DEC_COUNT + show position as decimal value +*/ +#define DUMPER_HEX (0) +#define DUMPER_HEX_COUNT (0) +#define DUMPER_DUAL (1) +#define DUMPER_CODE (1 << 1) +#define DUMPER_PRINT (1 << 2) +#define DUMPER_DEC (1 << 3) +#define DUMPER_DEC_COUNT (1 << 4) +#define DUMPER_DEFAULT (DUMPER_HEX_COUNT|DUMPER_HEX) +extern void dumper (FILE *output, const void *buffer, size_t bufferlen, + int virtual_start, unsigned int flags); + +/* + Misc stuff + + change_mem{2}() see header of implementation for usage + build_cm_patterns() helper function for change_mem2() to read search patterns + from a file + cleanup_cm_patterns() helper function for build_cm_patterns() to free all + memory allocated for a (list of) st_pattern_t structure(s) + ansi_init() initialize ANSI output + gauge() start_time + time when gauge() was first started or when + (you may use time(0) to set this) + the gauge started + pos + current position + size + the expected end postion (size) + given these values gauge will calculate many + informative things like time, status bar, cps, etc. + it can be used for procedures which take some time to + inform the user about the actual gauge + GAUGE_DEFAULT + default gauge with full information, without ansi colors + GAUGE_PERCENT + lite/small gauge only showing percentage with linefeed + without ansi colors (can be used with popen(), etc..) + GAUGE_ANSI + enable ansi colors + GAUGE_DELETE_AFTER + delete (clears the current line) gauge() after process + has finished + clear_line () clear the current line (79 spaces) + drop_privileges() switch to the real user and group id (leave "root mode") + register_func() atexit() replacement + returns -1 if it fails, 0 if it was successful + unregister_func() unregisters a previously registered function + returns -1 if it fails, 0 if it was successful + handle_registered_funcs() calls all the registered functions + wait2 wait (sleep) a specified number of milliseconds + getenv2() getenv() clone for enviroments w/o HOME, TMP or TEMP variables + strtol2() tries base == 16 after base == 10 failed +*/ +typedef struct st_cm_set +{ + char *data; + int size; +} st_cm_set_t; + +typedef struct st_cm_pattern +{ + char *search, wildcard, escape, *replace; + int search_size, replace_size, offset, n_sets; + st_cm_set_t *sets; +} st_cm_pattern_t; + +extern int change_mem (char *buf, int bufsize, char *searchstr, int strsize, + char wc, char esc, char *newstr, int newsize, int offset, ...); +extern int change_mem2 (char *buf, int bufsize, char *searchstr, int strsize, + char wc, char esc, char *newstr, int newsize, + int offset, st_cm_set_t *sets); +extern int build_cm_patterns (st_cm_pattern_t **patterns, const char *filename, int verbose); +extern void cleanup_cm_patterns (st_cm_pattern_t **patterns, int n_patterns); + +extern int ansi_init (void); +extern void clear_line (void); + +#define GAUGE_PERCENT (1) +#if 0 +#define GAUGE_BPS (1 << 1) +#define GAUGE_GAUGE (1 << 2) +#define GAUGE_ANSI (1 << 3) +#define GAUGE_DELETE_AFTER (1 << 4) +#define GAUGE_BYTES_POS (1 << 5) +#define GAUGE_LINEFEED_ON (1 << 6) +#define GAUGE_DEFAULT (GAUGE_PERCENT | GAUGE_BPS | GAUGE_GAUGE | GAUGE_ANSI | GAUGE_BYTES_POS) +#else +#define GAUGE_DEFAULT 0 +#endif +extern int gauge (FILE *output, time_t start_time, int pos, int total_size, unsigned int flags); + +#if defined __unix__ && !defined __MSDOS__ +extern int drop_privileges (void); +#endif +extern int register_func (void (*func) (void)); +extern int unregister_func (void (*func) (void)); +extern void handle_registered_funcs (void); + +extern void wait2 (int nmillis); +extern char *getenv2 (const char *variable); +extern long int strtol2 (const char *str, char **tail); + +/* + Portability (conio.h, etc...) + + init_conio() init console I/O + deinit_conio() stop console I/O + getch() + kbhit() + fix_character_set() fixes some Cygwin problems with filenames + truncate() + sync() + popen() + pclose() + vprintf2() + printf2() + fprintf2() +*/ +#if (defined __unix__ && !defined __MSDOS__) || defined __BEOS__ || \ + defined __APPLE__ // Mac OS X actually +extern void init_conio (void); +extern void deinit_conio (void); +#define getch getchar // getchar() acts like DOS getch() after init_conio() +extern int kbhit (void); // may only be used after init_conio()! + +#elif defined __MSDOS__ +#include // getch() +#include // kbhit() + +#elif defined _WIN32 +#include // kbhit() & getch() + +#elif defined AMIGA +extern int kbhit (void); +//#define getch getchar +// Gonna use my (Jan-Erik) fake one. Might work better and more like the real +// getch(). +#endif + +#ifdef __CYGWIN__ +extern char *fix_character_set (char *str); +#endif + +#ifdef _WIN32 +// Note that _WIN32 is defined by cl.exe while the other constants (like WIN32) +// are defined in header files. MinGW's gcc.exe defines all constants. + +#include + +extern int truncate (const char *path, off_t size); +extern int sync (void); +// For MinGW popen() and pclose() are unavailable for DLL's. For DLL's _popen() +// and _pclose() should be used. Visual C++ only has the latter two. +#ifndef pclose // archive.h's definition gets higher "precedence" +#define pclose _pclose +#endif +#ifndef popen // idem +#define popen _popen +#endif + +#ifdef USE_ANSI_COLOR +#include + +extern int vprintf2 (const char *format, va_list argptr); +extern int printf2 (const char *format, ...); +extern int fprintf2 (FILE *file, const char *format, ...); +#define vprintf vprintf2 +#define printf printf2 +#define fprintf fprintf2 +#endif // USE_ANSI_COLOR + +#ifndef __MINGW32__ +#include +#include +#include // According to MSDN must + // come after . Yep, that's M$. +#define S_IWUSR _S_IWRITE +#define S_IRUSR _S_IREAD +#define S_ISDIR(mode) ((mode) & _S_IFDIR ? 1 : 0) +#define S_ISREG(mode) ((mode) & _S_IFREG ? 1 : 0) + +#define F_OK 00 +#define W_OK 02 +#define R_OK 04 +#define X_OK R_OK // this is correct for dirs, but not for exes + +#define STDIN_FILENO (fileno (stdin)) +#define STDOUT_FILENO (fileno (stdout)) +#define STDERR_FILENO (fileno (stderr)) + +#else +#ifdef DLL +#define access _access +#define chmod _chmod +#define fileno _fileno +#define getcwd _getcwd +#define isatty _isatty +#define rmdir _rmdir +#define stat _stat +#define strdup _strdup +#define stricmp _stricmp +#define strnicmp _strnicmp +#endif // DLL + +#endif // !__MINGW32__ + +#elif defined AMIGA // _WIN32 +// custom _popen() and _pclose(), because the standard ones (named popen() and +// pclose()) are buggy +#ifndef pclose // archive.h's definition gets higher "precedence" +#define pclose _pclose +#endif +#ifndef popen // idem +#define popen _popen +#endif +extern FILE *_popen (const char *path, const char *mode); +extern int _pclose (FILE *stream); +#endif // AMIGA + +#ifdef __cplusplus +} +#endif + +#endif // MISC_H diff --git a/ucon64/2.0/src/misc/parallel.c b/ucon64/2.0/src/misc/parallel.c new file mode 100644 index 0000000..485b409 --- /dev/null +++ b/ucon64/2.0/src/misc/parallel.c @@ -0,0 +1,985 @@ +/* +parallel.c - miscellaneous parallel port functions + +Copyright (c) 1999 - 2004 NoisyB +Copyright (c) 2001 - 2004 dbjh +Copyright (c) 2001 Caz (original BeOS code) +Copyright (c) 2002 - 2004 Jan-Erik Karlsson (Amiga code) + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef USE_PARALLEL + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include // ioperm() (libc5) +#endif + +#ifdef USE_PPDEV // ppdev is a Linux parallel +#include // port device driver +#include +#include +#include +#include +#elif defined __linux__ && defined __GLIBC__ // USE_PPDEV +#ifdef HAVE_SYS_IO_H // necessary for some Linux/PPC configs +#include // ioperm() (glibc), in{b, w}(), out{b, w}() +#else +#error No sys/io.h; configure with --disable-parallel +#endif +#elif defined __OpenBSD__ // __linux__ && __GLIBC__ +#include +#include +#elif defined __BEOS__ || defined __FreeBSD__ // __OpenBSD__ +#include +#elif defined AMIGA // __BEOS__ || __FreeBSD__ +#error Parallel port I/O code is broken; Please contact Jan-Erik Karlsson +#include +#include +#include +#include +#include +#include +#include +#elif defined _WIN32 // AMIGA +#include +#include // inp{w}() & outp{w}() +#include "dlopen.h" +#elif defined __CYGWIN__ // _WIN32 +#include // definition of WINAPI +#undef _WIN32 +#include +#include +#include "dlopen.h" +#endif +#include "misc.h" +#include "file.h" +#include "itypes.h" +#include "parallel.h" +#include "getopt2.h" +#include "ucon64.h" + + +#if defined USE_PPDEV || defined __BEOS__ || defined __FreeBSD__ || defined AMIGA +static void close_io_port (void); +#endif +#if defined __i386__ || defined __x86_64__ // GCC && x86 +inline static unsigned char i386_input_byte (unsigned short); +inline static unsigned short i386_input_word (unsigned short); +inline static void i386_output_byte (unsigned short, unsigned char); +inline static void i386_output_word (unsigned short, unsigned short); +#endif + + +#if defined USE_PPDEV || defined __BEOS__ || defined __FreeBSD__ +static int parport_io_fd; +#ifdef USE_PPDEV +static enum { FORWARD = 0, REVERSE } parport_io_direction; +static int parport_io_mode; +#endif +#endif + +#ifdef __BEOS__ +typedef struct st_ioport +{ + unsigned int port; + unsigned char data8; + unsigned short data16; +} st_ioport_t; +#endif + +#ifdef AMIGA +static struct IOStdReq *parport_io_req; +static struct MsgPort *parport; +#endif + + +#if defined _WIN32 || defined __CYGWIN__ + +#define NODRIVER_MSG "ERROR: No (working) I/O port driver. Please see the FAQ, question 4\n" + +static void *io_driver; + +// inpout32.dll only has I/O functions for byte-sized I/O +static unsigned char (__stdcall *Inp32) (unsigned short) = NULL; +static void (__stdcall *Outp32) (unsigned short, unsigned char) = NULL; + +static unsigned char inpout32_input_byte (unsigned short port) { return Inp32 (port); } +static void inpout32_output_byte (unsigned short port, unsigned char byte) { Outp32 (port, byte); } + +// io.dll has more functions then the ones we refer to here, but we don't need them +static char (WINAPI *PortIn) (short int) = NULL; +static short int (WINAPI *PortWordIn) (short int) = NULL; +static void (WINAPI *PortOut) (short int, char) = NULL; +static void (WINAPI *PortWordOut) (short int, short int) = NULL; +static short int (WINAPI *IsDriverInstalled) () = NULL; + +static unsigned char io_input_byte (unsigned short port) { return PortIn (port); } +static unsigned short io_input_word (unsigned short port) { return PortWordIn (port); } +static void io_output_byte (unsigned short port, unsigned char byte) { PortOut (port, byte); } +static void io_output_word (unsigned short port, unsigned short word) { PortWordOut (port, word); } + +// dlportio.dll has more functions then the ones we refer to here, but we don't need them +static unsigned char (__stdcall *DlPortReadPortUchar) (unsigned long) = NULL; +static unsigned short (__stdcall *DlPortReadPortUshort) (unsigned long) = NULL; +static void (__stdcall *DlPortWritePortUchar) (unsigned long, unsigned char) = NULL; +static void (__stdcall *DlPortWritePortUshort) (unsigned long, unsigned short) = NULL; + +static unsigned char dlportio_input_byte (unsigned short port) { return DlPortReadPortUchar (port); } +static unsigned short dlportio_input_word (unsigned short port) { return DlPortReadPortUshort (port); } +static void dlportio_output_byte (unsigned short port, unsigned char byte) { DlPortWritePortUchar (port, byte); } +static void dlportio_output_word (unsigned short port, unsigned short word) { DlPortWritePortUshort (port, word); } + +#if defined __CYGWIN__ || defined __MINGW32__ +// default to functions which are always available (but which generate an +// exception under Windows NT/2000/XP without an I/O driver) +static unsigned char (*input_byte) (unsigned short) = i386_input_byte; +static unsigned short (*input_word) (unsigned short) = i386_input_word; +static void (*output_byte) (unsigned short, unsigned char) = i386_output_byte; +static void (*output_word) (unsigned short, unsigned short) = i386_output_word; + +#elif defined _WIN32 +// The following four functions are needed because inp{w} and outp{w} seem to be macros +static unsigned char inp_func (unsigned short port) { return (unsigned char) inp (port); } +static unsigned short inpw_func (unsigned short port) { return inpw (port); } +static void outp_func (unsigned short port, unsigned char byte) { outp (port, byte); } +static void outpw_func (unsigned short port, unsigned short word) { outpw (port, word); } + +// default to functions which are always available (but which generate an +// exception under Windows NT/2000/XP without an I/O driver) +static unsigned char (*input_byte) (unsigned short) = inp_func; +static unsigned short (*input_word) (unsigned short) = inpw_func; +static void (*output_byte) (unsigned short, unsigned char) = outp_func; +static void (*output_word) (unsigned short, unsigned short) = outpw_func; + +#endif +#endif // _WIN32 || __CYGWIN__ + + +#if defined __i386__ || defined __x86_64__ // GCC && x86 +unsigned char +i386_input_byte (unsigned short port) +{ + unsigned char byte; + __asm__ __volatile__ + ("inb %1, %0" + : "=a" (byte) + : "d" (port) + ); + return byte; +} + + +unsigned short +i386_input_word (unsigned short port) +{ + unsigned short word; + __asm__ __volatile__ + ("inw %1, %0" + : "=a" (word) + : "d" (port) + ); + return word; +} + + +void +i386_output_byte (unsigned short port, unsigned char byte) +{ + __asm__ __volatile__ + ("outb %1, %0" + : + : "d" (port), "a" (byte) + ); +} + + +void +i386_output_word (unsigned short port, unsigned short word) +{ + __asm__ __volatile__ + ("outw %1, %0" + : + : "d" (port), "a" (word) + ); +} +#endif // __i386__ || __x86_64__ + + +unsigned char +inportb (unsigned short port) +{ +#ifdef USE_PPDEV + int ppreg = port - ucon64.parport; + unsigned char byte; + + switch (ppreg) + { + case 0: // data + if (parport_io_direction == FORWARD) // dir is forward? + { + parport_io_direction = REVERSE; // change it to reverse + ioctl (parport_io_fd, PPDATADIR, &parport_io_direction); + } + ioctl (parport_io_fd, PPRDATA, &byte); + break; + case 1: // status + ioctl (parport_io_fd, PPRSTATUS, &byte); + break; + case 2: // control + ioctl (parport_io_fd, PPRCONTROL, &byte); + break; + case 3: // EPP/ECP address + if (!(parport_io_mode & IEEE1284_ADDR)) // IEEE1284_DATA is 0! + { + parport_io_mode |= IEEE1284_ADDR; + ioctl (parport_io_fd, PPSETMODE, &parport_io_mode); + } + read (parport_io_fd, &byte, 1); + break; + case 4: // EPP/ECP data + if (parport_io_mode & IEEE1284_ADDR) + { + parport_io_mode &= ~IEEE1284_ADDR; // IEEE1284_DATA is 0 + ioctl (parport_io_fd, PPSETMODE, &parport_io_mode); + } + read (parport_io_fd, &byte, 1); + break; + case 0x402: // ECP register + printf ("WARNING: Ignored read from ECP register, returning 0\n"); + byte = 0; + break; + default: + fprintf (stderr, + "ERROR: inportb() tried to read from an unsupported port (0x%x)\n", + port); + exit (1); + } + return byte; +#elif defined __BEOS__ + st_ioport_t temp; + + temp.port = port; + ioctl (parport_io_fd, 'r', &temp, 0); + + return temp.data8; +#elif defined AMIGA + (void) port; // warning remover + ULONG wait_mask; + + parport_io_req->io_Length = 1; + parport_io_req->io_Command = CMD_READ; + +/* + SendIO ((struct IORequest *) parport_io_req); + + wait_mask = SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_F | 1L << parport->mp_SigBit; + if (Wait (wait_mask) & (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_F)) + AbortIO ((struct IORequest *) parport_io_req); + WaitIO ((struct IORequest *) parport_io_req); +*/ + + /* + The difference between using SendIO() and DoIO(), is that DoIO() handles + messages etc. by itself but it will not return until the IO is done. + + Probably have to do more error handling here :-) + + Can one CTRL-C a DoIO() request? (Or for that matter a SendIO().) + */ + + if (DoIO ((struct IORequest *) parport_io_req)) + { + fprintf (stderr, "ERROR: Could not communicate with parallel port (%s, %d)\n", + ucon64.parport_dev, ucon64.parport); + exit (1); + } + + return (unsigned char) parport_io_req->io_Data; +#elif defined _WIN32 || defined __CYGWIN__ + return input_byte (port); +#elif defined __i386__ || defined __x86_64__ + return i386_input_byte (port); +#elif defined HAVE_SYS_IO_H + return inb (port); +#endif +} + + +unsigned short +inportw (unsigned short port) +{ +#ifdef USE_PPDEV + int ppreg = port - ucon64.parport; + unsigned char buf[2]; + + switch (ppreg) + { + case 3: // EPP/ECP address + if (!(parport_io_mode & IEEE1284_ADDR)) // IEEE1284_DATA is 0! + { + parport_io_mode |= IEEE1284_ADDR; + ioctl (parport_io_fd, PPSETMODE, &parport_io_mode); + } + read (parport_io_fd, buf, 2); + break; + case 4: // EPP/ECP data + if (parport_io_mode & IEEE1284_ADDR) + { + parport_io_mode &= ~IEEE1284_ADDR; // IEEE1284_DATA is 0 + ioctl (parport_io_fd, PPSETMODE, &parport_io_mode); + } + read (parport_io_fd, buf, 2); + break; + // the data, status, control and ECP registers should only be accessed in "8-bit mode" + default: + fprintf (stderr, + "ERROR: inportw() tried to read from an unsupported port (0x%x)\n", + port); + exit (1); + } + return buf[0] | buf[1] << 8; // words are read in little endian format +#elif defined __BEOS__ + st_ioport_t temp; + + temp.port = port; + ioctl (parport_io_fd, 'r16', &temp, 0); + + return temp.data16; +#elif defined AMIGA + (void) port; // warning remover + ULONG wait_mask; + + parport_io_req->io_Length = 2; + parport_io_req->io_Command = CMD_READ; + + if (DoIO ((struct IORequest *) parport_io_req)) + { + fprintf (stderr, "ERROR: Could not communicate with parallel port (%s, %d)\n", + ucon64.parport_dev, ucon64.parport); + exit (1); + } + + return (unsigned short) parport_io_req->io_Data; +#elif defined _WIN32 || defined __CYGWIN__ + return input_word (port); +#elif defined __i386__ || defined __x86_64__ + return i386_input_word (port); +#elif defined HAVE_SYS_IO_H + return inw (port); +#endif +} + + +void +outportb (unsigned short port, unsigned char byte) +{ +#ifdef USE_PPDEV + int ppreg = port - ucon64.parport; + + switch (ppreg) + { + case 0: // data + if (parport_io_direction == REVERSE) // dir is reverse? + { + parport_io_direction = FORWARD; // change it to forward + ioctl (parport_io_fd, PPDATADIR, &parport_io_direction); + } + ioctl (parport_io_fd, PPWDATA, &byte); + break; + case 2: // control + ioctl (parport_io_fd, PPWCONTROL, &byte); + break; + case 3: // EPP/ECP address + if (!(parport_io_mode & IEEE1284_ADDR)) // IEEE1284_DATA is 0! + { + parport_io_mode |= IEEE1284_ADDR; + ioctl (parport_io_fd, PPSETMODE, &parport_io_mode); + } + write (parport_io_fd, &byte, 1); + break; + case 4: // EPP/ECP data + if (parport_io_mode & IEEE1284_ADDR) + { + parport_io_mode &= ~IEEE1284_ADDR; // IEEE1284_DATA is 0 + ioctl (parport_io_fd, PPSETMODE, &parport_io_mode); + } + write (parport_io_fd, &byte, 1); + break; + case 0x402: // ECP register + printf ("WARNING: Ignored write to ECP register\n"); + break; + default: + fprintf (stderr, + "ERROR: outportb() tried to write to an unsupported port (0x%x)\n", + port); + exit (1); + } +#elif defined __BEOS__ + st_ioport_t temp; + + temp.port = port; + temp.data8 = byte; + ioctl (parport_io_fd, 'w', &temp, 0); +#elif defined AMIGA + (void) port; // warning remover + ULONG wait_mask; + + parport_io_req->io_Length = 1; + parport_io_req->io_Data = byte; + parport_io_req->io_Command = CMD_WRITE; + + if (DoIO ((struct IORequest *) parport_io_req)) + { + fprintf (stderr, "ERROR: Could not communicate with parallel port (%s, %d)\n", + ucon64.parport_dev, ucon64.parport); + exit (1); + } +#elif defined _WIN32 || defined __CYGWIN__ + output_byte (port, byte); +#elif defined __i386__ || defined __x86_64__ + i386_output_byte (port, byte); +#elif defined HAVE_SYS_IO_H + outb (byte, port); +#endif +} + + +void +outportw (unsigned short port, unsigned short word) +{ +#ifdef USE_PPDEV + int ppreg = port - ucon64.parport; + unsigned char buf[2]; + + // words are written in little endian format + buf[0] = word; + buf[1] = word >> 8; + switch (ppreg) + { + case 3: // EPP/ECP address + if (!(parport_io_mode & IEEE1284_ADDR)) // IEEE1284_DATA is 0! + { + parport_io_mode |= IEEE1284_ADDR; + ioctl (parport_io_fd, PPSETMODE, &parport_io_mode); + } + write (parport_io_fd, buf, 2); + break; + case 4: // EPP/ECP data + if (parport_io_mode & IEEE1284_ADDR) + { + parport_io_mode &= ~IEEE1284_ADDR; // IEEE1284_DATA is 0 + ioctl (parport_io_fd, PPSETMODE, &parport_io_mode); + } + write (parport_io_fd, buf, 2); + break; + // the data, control and ECP registers should only be accessed in "8-bit mode" + default: + fprintf (stderr, + "ERROR: outportw() tried to write to an unsupported port (0x%x)\n", + port); + exit (1); + } +#elif defined __BEOS__ + st_ioport_t temp; + + temp.port = port; + temp.data16 = word; + ioctl (parport_io_fd, 'w16', &temp, 0); +#elif defined AMIGA + (void) port; // warning remover + ULONG wait_mask; + + parport_io_req->io_Length = 2; + parport_io_req->io_Data = word; + parport_io_req->io_Command = CMD_WRITE; + + if (DoIO ((struct IORequest *) parport_io_req)) + { + fprintf (stderr, "ERROR: Could not communicate with parallel port (%s, %d)\n", + ucon64.parport_dev, ucon64.parport); + exit (1); + } +#elif defined _WIN32 || defined __CYGWIN__ + output_word (port, word); +#elif defined __i386__ || defined __x86_64__ + i386_output_word (port, word); +#elif defined HAVE_SYS_IO_H + outw (word, port); +#endif +} + + +#if (defined __i386__ || defined __x86_64__ || defined _WIN32) && !defined USE_PPDEV +#define DETECT_MAX_CNT 1000 +static int +parport_probe (unsigned int port) +{ + int i = 0; + + outportb ((unsigned short) port, 0xaa); + for (i = 0; i < DETECT_MAX_CNT; i++) + if (inportb ((unsigned short) port) == 0xaa) + break; + + if (i < DETECT_MAX_CNT) + { + outportb ((unsigned short) port, 0x55); + for (i = 0; i < DETECT_MAX_CNT; i++) + if (inportb ((unsigned short) port) == 0x55) + break; + } + + if (i >= DETECT_MAX_CNT) + return 0; + + return 1; +} +#endif + + +#ifdef _WIN32 +static LONG +new_exception_filter (LPEXCEPTION_POINTERS exception_pointers) +{ + if (exception_pointers->ExceptionRecord->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION) + { + fputs (NODRIVER_MSG, stderr); + exit (1); + } + return EXCEPTION_CONTINUE_SEARCH; +} +#elif defined __CYGWIN__ +static int +new_exception_handler (PEXCEPTION_RECORD exception_record, void *establisher_frame, + PCONTEXT context_record, void *dispatcher_context) +{ + (void) establisher_frame; + (void) context_record; + (void) dispatcher_context; + if (exception_record->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION) + { + fputs (NODRIVER_MSG, stderr); + exit (1); + } + return EXCEPTION_CONTINUE_SEARCH; +} +#endif + + +int +parport_open (int port) +{ +#ifdef USE_PPDEV + struct timeval t; + int capabilities = 0, ucon64_parport, x; + + if (port == PARPORT_UNKNOWN) + port = 0; + + parport_io_fd = open (ucon64.parport_dev, O_RDWR | O_NONBLOCK); + if (parport_io_fd == -1) + { + fprintf (stderr, "ERROR: Could not open parallel port device (%s)\n" + " Check if you have the required privileges\n", + ucon64.parport_dev); + exit (1); + } + + ioctl (parport_io_fd, PPEXCL); // disable sharing + ioctl (parport_io_fd, PPCLAIM); + t.tv_sec = 0; + t.tv_usec = 500 * 1000; // set time-out to 500 milliseconds + ioctl (parport_io_fd, PPSETTIME, &t); +/* + ioctl (parport_io_fd, PPGETTIME, &t); + printf ("Current time-out value: %ld microseconds\n", t.tv_usec); +*/ + + ioctl (parport_io_fd, PPGETMODES, &capabilities); +// printf ("Capabilities: %x\n", capabilities); + + if (ucon64.parport_mode == UCON64_EPP || ucon64.parport_mode == UCON64_ECP) + if ((capabilities & (PARPORT_MODE_EPP | PARPORT_MODE_ECP)) == 0) + printf ("WARNING: EPP or ECP mode was requested, but not available\n"); + + // set mode for read() and write() + if (capabilities & PARPORT_MODE_ECP) + parport_io_mode = IEEE1284_MODE_ECP; + else if (capabilities & PARPORT_MODE_EPP) + parport_io_mode = IEEE1284_MODE_EPP; + else + parport_io_mode = IEEE1284_MODE_BYTE; + parport_io_mode |= IEEE1284_DATA; // default to EPP/ECP data reg + ioctl (parport_io_fd, PPSETMODE, &parport_io_mode); // (IEEE1284_DATA is 0...) + + x = PP_FASTREAD | PP_FASTWRITE; // enable 16-bit transfers + ioctl (parport_io_fd, PPSETFLAGS, &x); + + parport_io_direction = FORWARD; // set forward direction as default + ioctl (parport_io_fd, PPDATADIR, &parport_io_direction); +#elif defined __BEOS__ + parport_io_fd = open ("/dev/misc/ioport", O_RDWR | O_NONBLOCK); + if (parport_io_fd == -1) + { + parport_io_fd = open ("/dev/misc/parnew", O_RDWR | O_NONBLOCK); + if (parport_io_fd == -1) + { + fprintf (stderr, "ERROR: Could not open I/O port device (no driver)\n" + " You can download the latest ioport driver from\n" + " http://www.infernal.currantbun.com or http://ucon64.sourceforge.net\n"); + exit (1); + } + else + { // print warning, but continue + printf ("WARNING: Support for the driver parnew is deprecated. Future versions of uCON64\n" + " might not support this driver. You can download the latest ioport\n" + " driver from http://www.infernal.currantbun.com or\n" + " http://ucon64.sourceforge.net\n\n"); + } + } +#elif defined AMIGA + int x; + + parport = CreatePort (NULL, 0); + if (parport == NULL) + { + fprintf (stderr, "ERROR: Could not create the MsgPort\n"); + exit (1); + } + parport_io_req = CreateExtIO (parport, sizeof (struct IOExtPar)); + if (parport_io_req == NULL) + { + fprintf (stderr, "ERROR: Could not create the I/O request structure\n"); + DeletePort (parport); + parport_io_req = NULL; + exit (1); + } + + // Is it possible to probe for the correct port? + if (port == PARPORT_UNKNOWN) + port = 0; + + x = OpenDevice (ucon64.parport_dev, port, (struct IORequest *) parport_io_req, + (ULONG) 0); + if (x != 0) + { + fprintf (stderr, "ERROR: Could not open parallel port (%s, %x)\n", + ucon64.parport_dev, port); + DeleteExtIO ((struct IOExtPar *) parport_io_req); + DeletePort (parport); + exit (1); + } + + if (register_func (close_io_port) == -1) + { + // AbortIO ((struct IORequest *) parport_io_req); // should not be necessary with DoIO() + CloseDevice ((struct IORequest *) parport_io_req); + DeleteExtIO (parport_io_req); + DeletePort (parport); + parport_io_req = NULL; + fprintf (stderr, "ERROR: Could not register function with register_func()\n"); + exit (1); + } +#elif defined __FreeBSD__ + parport_io_fd = open ("/dev/io", O_RDWR); + if (parport_io_fd == -1) + { + fprintf (stderr, "ERROR: Could not open I/O port device (/dev/io)\n" + " (This program needs root privileges for the requested action)\n"); + exit (1); + } +#endif + +#if defined USE_PPDEV || defined __BEOS__ || defined __FreeBSD__ + if (register_func (close_io_port) == -1) + { + close (parport_io_fd); + fprintf (stderr, "ERROR: Could not register function with register_func()\n"); + exit(1); + } +#endif + +#if defined __linux__ && (defined __i386__ || defined __x86_64__) && !defined USE_PPDEV + /* + Some code needs us to switch to the real uid and gid. However, other code + needs access to I/O ports other than the standard printer port registers. + We just do an iopl(3) and all code should be happy. Using iopl(3) enables + users to run all code without being root (of course with the uCON64 + executable setuid root). + Another reason to use iopl() and not ioperm() is that the former enables + access to all I/O ports, while the latter enables access to ports up to + 0x3ff. For the standard parallel port hardware addresses this is not a + problem. It *is* a problem for add-on parallel port cards which can be + mapped to I/O addresses above 0x3ff. + */ + if (iopl (3) == -1) + { + fprintf (stderr, "ERROR: Could not set the I/O privilege level to 3\n" + " (This program needs root privileges for the requested action)\n"); + exit (1); // Don't return, if iopl() fails port access + } // causes core dump +#endif // __linux__ && (__i386__ || __x86_64__) && !USE_PPDEV + +#ifdef __OpenBSD__ // || defined __NetBSD__, add after feature request ;-) + // We use i386_iopl() under OpenBSD for the same reasons we use iopl() under + // Linux (i386_set_ioperm() has the same limitation as ioperm()). + if (i386_iopl (3) == -1) + { + fprintf (stderr, "ERROR: Could not set the I/O privilege level to 3\n" + " (This program needs root privileges for the requested action)\n"); + exit (1); + } +#endif + +#if (defined __i386__ || defined __x86_64__ || defined _WIN32) && !defined USE_PPDEV + +#if defined _WIN32 || defined __CYGWIN__ + /* + We support the I/O port drivers inpout32.dll, io.dll and dlportio.dll, + because using them is way easier than using UserPort or GiveIO. The drivers + are also more reliable and seem to enable access to all I/O ports + (dlportio.dll enables access to ports > 0x100). The downsides of + inpout32.dll are that it's almost two times slower than UserPort and that + it only has functions for byte-sized I/O. + */ + char fname[FILENAME_MAX]; + int driver_found = 0; + + sprintf (fname, "%s" FILE_SEPARATOR_S "%s", ucon64.configdir, "dlportio.dll"); +#if 0 // We must not do this for Cygwin or access() won't "find" the file + change_mem (fname, strlen (fname), "/", 1, 0, 0, "\\", 1, 0); +#endif + if (access (fname, F_OK) == 0) + { + io_driver = open_module (fname); + + driver_found = 1; + printf ("Using %s\n", fname); + + DlPortReadPortUchar = +#ifdef __cplusplus // this is really nice: gcc wants something else than g++... + (unsigned char (__stdcall *) (unsigned long)) +#endif + get_symbol (io_driver, "DlPortReadPortUchar"); + DlPortReadPortUshort = +#ifdef __cplusplus + (unsigned short (__stdcall *) (unsigned long)) +#endif + get_symbol (io_driver, "DlPortReadPortUshort"); + DlPortWritePortUchar = +#ifdef __cplusplus + (void (__stdcall *) (unsigned long, unsigned char)) +#endif + get_symbol (io_driver, "DlPortWritePortUchar"); + DlPortWritePortUshort = +#ifdef __cplusplus + (void (__stdcall *) (unsigned long, unsigned short)) +#endif + get_symbol (io_driver, "DlPortWritePortUshort"); + input_byte = dlportio_input_byte; + input_word = dlportio_input_word; + output_byte = dlportio_output_byte; + output_word = dlportio_output_word; + } + + if (!driver_found) + { + sprintf (fname, "%s" FILE_SEPARATOR_S "%s", ucon64.configdir, "io.dll"); + if (access (fname, F_OK) == 0) + { + io_driver = open_module (fname); + + IsDriverInstalled = +#ifdef __cplusplus + (short int (WINAPI *) ()) +#endif + get_symbol (io_driver, "IsDriverInstalled"); + if (IsDriverInstalled ()) + { + driver_found = 1; + printf ("Using %s\n", fname); + + PortIn = +#ifdef __cplusplus + (char (WINAPI *) (short int)) +#endif + get_symbol (io_driver, "PortIn"); + PortWordIn = +#ifdef __cplusplus + (short int (WINAPI *) (short int)) +#endif + get_symbol (io_driver, "PortWordIn"); + PortOut = +#ifdef __cplusplus + (void (WINAPI *) (short int, char)) +#endif + get_symbol (io_driver, "PortOut"); + PortWordOut = +#ifdef __cplusplus + (void (WINAPI *) (short int, short int)) +#endif + get_symbol (io_driver, "PortWordOut"); + input_byte = io_input_byte; + input_word = io_input_word; + output_byte = io_output_byte; + output_word = io_output_word; + } + } + } + + if (!driver_found) + { + sprintf (fname, "%s" FILE_SEPARATOR_S "%s", ucon64.configdir, "inpout32.dll"); + if (access (fname, F_OK) == 0) + { + driver_found = 1; + printf ("Using %s\n", fname); + io_driver = open_module (fname); + Inp32 = +#ifdef __cplusplus + (unsigned char (__stdcall *) (unsigned short)) +#endif + get_symbol (io_driver, "Inp32"); + Outp32 = +#ifdef __cplusplus + (void (__stdcall *) (unsigned short, unsigned char)) +#endif + get_symbol (io_driver, "Out32"); + // note that inport_word and output_word keep their default value... + input_byte = inpout32_input_byte; + output_byte = inpout32_output_byte; + } + } + + /* + __try and __except are not supported by MinGW and Cygwin. MinGW has __try1 + and __except1, but using them requires more code than we currently have. + Cygwin does something stupid which breaks SetUnhandledExceptionFilter()... + */ + { +#ifdef _WIN32 // MinGW & Visual C++ + LPTOP_LEVEL_EXCEPTION_FILTER org_exception_filter = + SetUnhandledExceptionFilter ((LPTOP_LEVEL_EXCEPTION_FILTER) new_exception_filter); + input_byte (0x378); // 0x378 is okay (don't use this function's parameter) + // if we get here accessing I/O port 0x378 did not cause an exception + SetUnhandledExceptionFilter (org_exception_filter); +#else // Cygwin + exception_list list; + exception_handler *org_handler; + cygwin_internal (CW_INIT_EXCEPTIONS, &list); + org_handler = list.handler; + list.handler = new_exception_handler; + input_byte (0x378); + list.handler = org_handler; +#endif + } +#endif // _WIN32 || __CYGWIN__ + + if (port == PARPORT_UNKNOWN) // no port specified or forced? + { + unsigned int parport_addresses[] = { 0x3bc, 0x378, 0x278 }; + int x, found = 0; + + for (x = 0; x < 3; x++) + if ((found = parport_probe (parport_addresses[x])) == 1) + { + port = parport_addresses[x]; + break; + } + + if (found != 1) + { + fprintf (stderr, "ERROR: Could not find a parallel port on your system\n" + " Try to specify it by hand\n\n"); + exit (1); + } + } +#endif // (__i386__ || __x86_64__ || _WIN32) && !USE_PPDEV + +#ifdef USE_PPDEV + // the following two calls need a valid value for ucon64.parport + ucon64_parport = ucon64.parport; + ucon64.parport = port; +#endif + outportb ((unsigned short) (port + PARPORT_CONTROL), + (unsigned char) (inportb ((unsigned short) (port + PARPORT_CONTROL)) & 0x0f)); + // bit 4 = 0 -> IRQ disable for ACK, bit 5-7 unused +#ifdef USE_PPDEV + ucon64.parport = ucon64_parport; +#endif + + return port; +} + + +#if defined USE_PPDEV || defined __BEOS__ || defined __FreeBSD__ || defined AMIGA +void +close_io_port (void) +{ +#ifdef AMIGA + CloseDevice ((struct IORequest *) parport_io_req); + DeleteExtIO ((struct IOExtPar *) parport_io_req); + DeletePort (parport); + parport_io_req = NULL; +#elif defined USE_PPDEV + parport_io_mode = IEEE1284_MODE_COMPAT; + // We really don't want to perform IEEE 1284 negotiation, but if we don't do + // it ppdev will do it for us... + ioctl (parport_io_fd, PPNEGOT, &parport_io_mode); + ioctl (parport_io_fd, PPRELEASE); +#else // __BEOS__ || __FreeBSD__ + close (parport_io_fd); +#endif +} +#endif + + +int +parport_close (int parport) +{ + (void) parport; +#if defined USE_PPDEV || defined __BEOS__ || defined __FreeBSD__ || defined AMIGA + if (unregister_func (close_io_port) == 0) // call func only if it can be removed! + close_io_port (); // (or else it will be called twice) +#elif defined _WIN32 || defined __CYGWIN__ + input_byte = NULL; + input_word = NULL; + output_byte = NULL; + output_word = NULL; +#endif + return 0; +} + + +void +parport_print_info (void) +{ +#ifdef USE_PPDEV + printf ("Using parallel port device: %s\n", ucon64.parport_dev); +#elif defined AMIGA + printf ("Using parallel port device: %s, port %d\n", ucon64.parport_dev, ucon64.parport); +#else + printf ("Using I/O port base address: 0x%x\n", ucon64.parport); +#endif +} +#endif // USE_PARALLEL diff --git a/ucon64/2.0/src/misc/parallel.h b/ucon64/2.0/src/misc/parallel.h new file mode 100644 index 0000000..4a6c36c --- /dev/null +++ b/ucon64/2.0/src/misc/parallel.h @@ -0,0 +1,54 @@ +/* +parallel.h - miscellaneous parallel port functions + +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2001 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef MISC_PARALLEL_H +#define MISC_PARALLEL_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef USE_PARALLEL + +#define PARPORT_DATA 0 // output +#define PARPORT_STATUS 1 // input +#define PARPORT_CONTROL 2 +#define PARPORT_EADDRESS 3 // EPP/ECP address +#define PARPORT_EDATA 4 // EPP/ECP output/input + +#define PARPORT_INPUT_MASK 0x78 + +#define PARPORT_IBUSY 0x80 +#define PARPORT_STROBE 1 + +#define PARPORT_UNKNOWN (-1) + +// DJGPP (DOS) has these, but it's better that all code uses the same functions. +extern unsigned char inportb (unsigned short port); +extern unsigned short inportw (unsigned short port); +extern void outportb (unsigned short port, unsigned char byte); +extern void outportw (unsigned short port, unsigned short word); + +extern int parport_open (int parport); +extern int parport_close (int parport); +extern void parport_print_info (void); +#endif // USE_PARALLEL +#endif // MISC_PARALLEL_H diff --git a/ucon64/2.0/src/misc/property.c b/ucon64/2.0/src/misc/property.c new file mode 100644 index 0000000..49047cb --- /dev/null +++ b/ucon64/2.0/src/misc/property.c @@ -0,0 +1,254 @@ +/* +property.c - configfile handling + +Copyright (c) 2004 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include // for struct stat +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include "file.h" // realpath2() +#include "property.h" +#include "misc.h" // getenv2() + + +// Shouldn't we include string.h? - dbjh +#ifndef _WIN32 +#define stricmp strcasecmp +#endif + +#ifdef MAXBUFSIZE +#undef MAXBUFSIZE +#endif // MAXBUFSIZE +#define MAXBUFSIZE 32768 + +#define PROPERTY_SEPARATOR '=' +#define PROPERTY_SEPARATOR_S "=" +#define PROPERTY_COMMENT '#' +#define PROPERTY_COMMENT_S "#" + + +char * +get_property (const char *filename, const char *propname, char *buffer, + const char *def) +{ + char line[MAXBUFSIZE], *p = NULL; + FILE *fh; + int prop_found = 0, i, whitespace_len; + + if ((fh = fopen (filename, "r")) != 0) // opening the file in text mode + { // avoids trouble under DOS + while (fgets (line, sizeof line, fh) != NULL) + { + whitespace_len = strspn (line, "\t "); + p = line + whitespace_len; // ignore leading whitespace + if (*p == '#' || *p == '\n' || *p == '\r') + continue; // text after # is comment + if ((p = strpbrk (line, "#\r\n"))) // strip *any* returns + *p = 0; + + p = strchr (line, PROPERTY_SEPARATOR); + // if no divider was found the propname must be a bool config entry + // (present or not present) + if (p) + *p = 0; // note that this "cuts" _line_ + // strip trailing whitespace from property name part of line + for (i = strlen (line) - 1; + i >= 0 && (line[i] == '\t' || line[i] == ' '); + i--) + ; + line[i + 1] = 0; + + if (!stricmp (line + whitespace_len, propname)) + { + if (p) + { + p++; + // strip leading whitespace from value + strcpy (buffer, p + strspn (p, "\t ")); + // strip trailing whitespace from value + for (i = strlen (buffer) - 1; + i >= 0 && (buffer[i] == '\t' || buffer[i] == ' '); + i--) + ; + buffer[i + 1] = 0; + } + prop_found = 1; + break; // an environment variable + } // might override this + } + fclose (fh); + } + + p = getenv2 (propname); + if (*p == 0) // getenv2() never returns NULL + { + if (!prop_found) + { + if (def) + strcpy (buffer, def); + else + buffer = NULL; // buffer won't be changed + } // after this func (=ok) + } + else + strcpy (buffer, p); + return buffer; +} + + +int +get_property_int (const char *filename, const char *propname) +{ + char buf[MAXBUFSIZE]; // MAXBUFSIZE is enough for a *very* large number + // and people who might not get the idea that + // get_property_int() is ONLY about numbers + int value = 0; + + get_property (filename, propname, buf, NULL); + + if (*buf) + switch (tolower (*buf)) + { + case '0': // 0 + case 'n': // [Nn]o + return 0; + } + + value = strtol (buf, NULL, 10); + return value ? value : 1; // if buf was only text like 'Yes' +} // we'll return at least 1 + + +char * +get_property_fname (const char *filename, const char *propname, char *buffer, + const char *def) +// get a filename from file with name filename, expand it and fix characters +{ + char tmp[FILENAME_MAX]; + + get_property (filename, propname, tmp, def); +#ifdef __CYGWIN__ + fix_character_set (tmp); +#endif + return realpath2 (tmp, buffer); +} + + +int +set_property (const char *filename, const char *propname, const char *value, + const char *comment) +{ + int found = 0, result = 0, file_size = 0, i; + char line[MAXBUFSIZE], line2[MAXBUFSIZE], *str = NULL, *p = NULL; + FILE *fh; + struct stat fstate; + + if (stat (filename, &fstate) != 0) + file_size = fstate.st_size; + + if (!(str = (char *) malloc (file_size + MAXBUFSIZE))) + return -1; + *str = 0; + + if ((fh = fopen (filename, "r")) != 0) // opening the file in text mode + { // avoids trouble under DOS + while (fgets (line, sizeof line, fh) != NULL) + { + strcpy (line2, line); + if ((p = strpbrk (line2, PROPERTY_SEPARATOR_S "#\r\n"))) + *p = 0; // note that this "cuts" _line2_ + for (i = strlen (line2) - 1; + i >= 0 && (line2[i] == '\t' || line2[i] == ' '); + i--) + ; + line2[i + 1] = 0; + + if (!stricmp (line2 + strspn (line2, "\t "), propname)) + { + found = 1; + if (value == NULL) + continue; + + sprintf (line, "%s" PROPERTY_SEPARATOR_S "%s\n", propname, value); + } + strcat (str, line); + } + fclose (fh); + } + + if (!found && value) + { + if (comment) + { + strcat (str, PROPERTY_COMMENT_S "\n" PROPERTY_COMMENT_S " "); + + for (p = strchr (str, 0); *comment; comment++) + switch (*comment) + { + case '\r': + break; + case '\n': + strcat (str, "\n" PROPERTY_COMMENT_S " "); + break; + + default: + p = strchr (str, 0); + *p = *comment; + *(++p) = 0; + break; + } + + strcat (str, "\n" PROPERTY_COMMENT_S "\n"); + } + + sprintf (line, "%s" PROPERTY_SEPARATOR_S "%s\n", propname, value); + strcat (str, line); + } + + if ((fh = fopen (filename, "w")) == NULL) // open in text mode + return -1; + result = fwrite (str, 1, strlen (str), fh); + fclose (fh); + + return result; +} + + +int +set_property_array (const char *filename, const st_property_t *prop) +{ + int i = 0, result = 0; + + for (; prop[i].name; i++) + { + result = set_property (filename, prop[i].name, prop[i].value, prop[i].comment); + + if (result == -1) // failed + break; + } + + return result; +} diff --git a/ucon64/2.0/src/misc/property.h b/ucon64/2.0/src/misc/property.h new file mode 100644 index 0000000..3675eb9 --- /dev/null +++ b/ucon64/2.0/src/misc/property.h @@ -0,0 +1,67 @@ +/* +property.h - configfile handling + +Copyright (c) 2004 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef MISC_PROPERTY_H +#define MISC_PROPERTY_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct +{ + const char *name; // property name + const char *value; // property value + const char *comment; // property comment, # COMMENT +} st_property_t; // NAME=VALUE + + +/* + get_property() get value of propname from filename or return value + of env with name like propname or return def + get_property_int() like get_property() but returns an integer which is 0 + if the value of propname was 0, [Nn] or [Nn][Oo] + and an integer or at least 1 for every other case + get_property_fname() like get_property() but specifically for filenames, + i.e., it runs realpath2() on the filename and fixes + the characters if necessary (Cygwin) + set_property() set propname with value in filename + set_property_array() set an array of properties (st_property_t) at once + DELETE_PROPERTY() like set_property but when value of propname is NULL + the whole property will disappear from filename +*/ +extern char *get_property (const char *filename, const char *propname, char *value, + const char *def); +extern int get_property_int (const char *filename, const char *propname); +extern char *get_property_fname (const char *filename, const char *propname, + char *buffer, const char *def); +extern int set_property (const char *filename, const char *propname, const char *value, const char *comment); +extern int set_property_array (const char *filename, const st_property_t *prop); +#define DELETE_PROPERTY(a, b) (set_property(a, b, NULL, NULL)) + +#ifdef __cplusplus +} +#endif +#endif // MISC_PROPERTY_H diff --git a/ucon64/2.0/src/misc/string.c b/ucon64/2.0/src/misc/string.c new file mode 100644 index 0000000..022c951 --- /dev/null +++ b/ucon64/2.0/src/misc/string.c @@ -0,0 +1,240 @@ +/* +string.c - some string functions + +Copyright (c) 1999 - 2004 NoisyB +Copyright (c) 2001 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include "string.h" + + +#ifdef _MSC_VER +// Visual C++ doesn't allow inline in C source code +#define inline __inline +#endif + + +static inline int +is_func (char *s, int len, int (*func) (int)) +{ + char *p = s; + + /* + Casting to unsigned char * is necessary to avoid differences between the + different compilers' run-time environments. At least for isprint(). Without + the cast the isprint() of (older versions of) DJGPP, MinGW, Cygwin and + Visual C++ returns nonzero values for ASCII characters > 126. + */ + for (; len >= 0; p++, len--) + if (!func (*(unsigned char *) p)) + return 0; + + return 1; +} + + +static inline char * +to_func (char *s, int len, int (*func) (int)) +{ + char *p = s; + + for (; len > 0; p++, len--) + *p = func (*p); + + return s; +} + + +char * +strupr (char *s) +{ + return to_func (s, strlen (s), toupper); +} + + +char * +strlwr (char *s) +{ + return to_func (s, strlen (s), tolower); +} + + +char * +strcasestr2 (const char *str, const char *search) +{ + if (!(*search)) + return (char *) str; +#if 0 + else + { + int len = strlen (search); + + for (; *str; str++) + if (!strnicmp (str, search, len)) + return (char *) str; + } + + return NULL; +#else + return (char *) memmem2 (str, strlen (str), search, strlen (search), MEMMEM2_CASE); +#endif +} + + +char * +strtrimr (char *str) +{ + int i = strlen (str) - 1; + + while (isspace ((int) str[i]) && (i >= 0)) + str[i--] = 0; + + return str; +} + + +char * +strtriml (char *str) +{ + int i = 0, j; + + j = strlen (str) - 1; + + while (isspace ((int) str[i]) && (i <= j)) + i++; + + if (0 < i) + strcpy (str, &str[i]); + + return str; +} + + +#ifdef DEBUG +static int +strarg_debug (int argc, char **argv) +{ + int pos; + fprintf (stderr, "argc: %d\n", argc); + for (pos = 0; pos < argc; pos++) + fprintf (stderr, "argv[%d]: %s\n", pos, argv[pos]); + + fflush (stderr); +} +#endif + + +int +strarg (char **argv, char *str, const char *separator_s, int max_args) +{ + int argc = 0; + + if (str) + if (*str) + for (; (argv[argc] = (char *) strtok (!argc ? str : NULL, separator_s)) && + (argc < (max_args - 1)); argc++) + ; + +#ifdef DEBUG + strarg_debug (argc, argv); +#endif + + return argc; +} + + +int +memcmp2 (const void *buffer, const void *search, size_t searchlen, unsigned int flags) +{ + size_t i = 0; + const unsigned char *b = (const unsigned char *) buffer, + *s = (const unsigned char *) search; + + if (!flags) + return memcmp (buffer, search, searchlen); + + if (flags & MEMMEM2_REL) + { + searchlen--; + if (searchlen < 1) + return -1; + } + + for (i = 0; i < searchlen; i++) + { + if (flags & MEMMEM2_WCARD (0)) + if (*(s + i) == (flags & 0xff)) + continue; + + if (flags & MEMMEM2_REL) + { + if ((*(b + i) - *(b + i + 1)) != (*(s + i) - *(s + i + 1))) + break; + } + else + { + if (flags & MEMMEM2_CASE && isalpha (*(s + i))) + { + if (tolower (*(b + i)) != tolower (*(s + i))) + break; + } + else + if (*(b + i) != *(s + i)) + break; + } + } + + return i == searchlen ? 0 : -1; +} + + +const void * +memmem2 (const void *buffer, size_t bufferlen, + const void *search, size_t searchlen, unsigned int flags) +{ + size_t i; + + if (bufferlen >= searchlen) + for (i = 0; i <= bufferlen - searchlen; i++) + if (!memcmp2 ((const unsigned char *) buffer + i, search, searchlen, flags)) + return (const unsigned char *) buffer + i; + + return NULL; +} + + +#if 0 +int +memwcmp (const void *buffer, const void *search, unsigned int searchlen, int wildcard) +{ + unsigned int n; + + for (n = 0; n < searchlen; n++) + if (((unsigned char *) search)[n] != wildcard && + ((unsigned char *) buffer)[n] != ((unsigned char *) search)[n]) + return -1; + + return 0; +} +#endif diff --git a/ucon64/2.0/src/misc/string.h b/ucon64/2.0/src/misc/string.h new file mode 100644 index 0000000..9b07e1e --- /dev/null +++ b/ucon64/2.0/src/misc/string.h @@ -0,0 +1,92 @@ +/* +string.h - some string functions + +Copyright (c) 1999 - 2004 NoisyB +Copyright (c) 2001 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef MISC_STRING_H +#define MISC_STRING_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif +#include +/* + String manipulation + + strtriml() removes all leading blanks from a string + strtrimr() removes all trailing blanks from a string + Blanks are defined with isspace (blank, tab, newline, + return, formfeed, vertical tab = 0x09 - 0x0D + 0x20) + You can combine: strtriml (strtrimr ()) or + strtrimr (strtriml ()) + strarg() break a string into (max_args) tokens + replaces strtok[_r](), strsep(), etc... + strupr() strupr() clone + strlwr() strlwr() clone + memcmp2() memcmp() replacement with flags for wildcard and + relative/shifted similarities support + MEMCMP2_WCARD(WC) + WC is the wildcard char + MEMCMP2_REL + look for relative/shifted similarities + MEMCMP2_CASE + ignore case of isalpha() bytes + memmem2() memmem() replacement with flags for wildcard and + relative/shifted similarities support + MEMMEM2_WCARD(WC) + WC is the wildcard char + MEMMEM2_REL + look for relative/shifted similarities + MEMMEM2_CASE + ignore case of isalpha() bytes + stristr() same as strcasestr() + stricmp() same as strcasecmp() + strnicmp() same as strncasecmp() + strcasestr() strcasestr() clone for non-GNU platforms +*/ +extern char *strtriml (char *str); +extern char *strtrimr (char *str); +extern int strarg (char **argv, char *str, const char *separator_s, int max_args); +extern char *strlwr (char *str); +extern char *strupr (char *str); +#define MEMCMP2_WCARD(WC) ((1 << 9) | ((WC) & 0xff)) +#define MEMCMP2_REL (1 << 10) +#define MEMCMP2_CASE (1 << 11) +extern int memcmp2 (const void *buffer, + const void *search, size_t searchlen, unsigned int flags); +#define MEMMEM2_WCARD MEMCMP2_WCARD +#define MEMMEM2_REL MEMCMP2_REL +#define MEMMEM2_CASE MEMCMP2_CASE +extern const void *memmem2 (const void *buffer, size_t bufferlen, + const void *search, size_t searchlen, unsigned int flags); +extern char *strcasestr2 (const char *str, const char *search); +#define stristr strcasestr2 +#ifndef _WIN32 +#define stricmp strcasecmp +#define strnicmp strncasecmp +#endif + +#ifdef __cplusplus +} +#endif +#endif // MISC_STRING_H diff --git a/ucon64/2.0/src/misc/unzip.c b/ucon64/2.0/src/misc/unzip.c new file mode 100644 index 0000000..1b84182 --- /dev/null +++ b/ucon64/2.0/src/misc/unzip.c @@ -0,0 +1,1253 @@ +/* unzip.c -- IO on .zip files using zlib + Version 0.15 beta, Mar 19th, 1998, + + Read unzip.h for more info +*/ + + +#include +#include +#include +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + + +#if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) && \ + !defined(CASESENSITIVITYDEFAULT_NO) +#define CASESENSITIVITYDEFAULT_NO +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +const char unz_copyright[] = + " unzip 0.15 Copyright 1998 Gilles Vollant "; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;/* relative offset of local header 4 bytes */ +} unz_file_info_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + + uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + uLong offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + uLong pos_local_extrafield; /* position in the local extra field in read*/ + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + uLong rest_read_compressed; /* number of byte to be decompressed */ + uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + FILE* file; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ +} file_in_zip_read_info_s; + + +/* unz_s contain internal information about the zipfile +*/ +typedef struct +{ + FILE* file; /* io structore of the zipfile */ + unz_global_info gi; /* public global information */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + uLong num_file; /* number of the current file in the zipfile*/ + uLong pos_in_central_dir; /* pos of the current file in the central dir*/ + uLong current_file_ok; /* flag about the usability of the current file*/ + uLong central_pos; /* position of the beginning of the central dir*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info cur_file_info; /* public info about the current file in zip*/ + unz_file_info_internal cur_file_info_internal; /* private info about it*/ + file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ +} unz_s; + + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unzlocal_getByte(FILE *fin,int *pi) +{ + unsigned char c; + int err = fread(&c, 1, 1, fin); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ferror(fin)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unzlocal_getShort (FILE* fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unzlocal_getLong (FILE* fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (const char* fileName1,const char* fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (const char* fileName1,const char* fileName2, + int iCaseSensitivity) +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#define BUFREADCOMMENT (0x400) + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local uLong unzlocal_SearchCentralDir(FILE *fin) +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + if (fseek(fin,0,SEEK_END) != 0) + return 0; + + + uSizeFile = ftell( fin ); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (fseek(fin,uReadPos,SEEK_SET)!=0) + break; + + if (fread(buf,(uInt)uReadSize,1,fin)!=1) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib109.zip" or on an Unix computer + "zlib/zlib109.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +extern unzFile ZEXPORT unzOpen (const char *path) +{ + unz_s us; + unz_s *s; + uLong central_pos,uL; + FILE * fin ; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + fin=fopen(path,"rb"); + if (fin==NULL) + return NULL; + + central_pos = unzlocal_SearchCentralDir(fin); + if (central_pos==0) + err=UNZ_ERRNO; + + if (fseek(fin,central_pos,SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unzlocal_getLong(fin,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir */ + if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* zipfile comment length */ + if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + fclose(s->file); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo (unzFile file,unz_global_info *pglobal_info) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unzlocal_DosDateToTmuDate (uLong ulDosDate,tm_unz* ptm) +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unzlocal_GetCurrentFileInfoInternal (unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (fseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (fread(szFileName,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extrafile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (fread(extraField,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentfile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (fread(szComment,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) +{ + return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (unzFile file) +{ + int err=UNZ_OK; + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (unzFile file) +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (unzFile file, const char *szFileName, + int iCaseSensitivity) +{ + unz_s* s; + int err; + + + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + return err; +} + + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unzlocal_CheckCurrentFileCoherencyHeader (unz_s* s, + uInt* piSizeVar, + uLong *poffset_local_extrafield, + uInt *psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (fseek(s->file,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile (unzFile file) +{ + int err=UNZ_OK; + int Store; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*) + ALLOC(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if ((s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + Store = s->cur_file_info.compression_method==0; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->file=s->file; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if (!Store) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + + s->pfile_in_zip_read = pfile_in_zip_read_info; + return UNZ_OK; +} + + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ + int err=UNZ_OK; + uInt iRead = 0; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if (len>pfile_in_zip_read_info->rest_read_uncompressed) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (fseek(pfile_in_zip_read_info->file, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + if (fread(pfile_in_zip_read_info->read_buffer,uReadThis,1, + pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if (pfile_in_zip_read_info->compression_method==0) + { + uInt uDoCopy,i ; + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (unzFile file,voidp buf,unsigned len) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (fseek(pfile_in_zip_read_info->file, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (fread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (unzFile file, char *szComment, uLong uSizeBuf) +{ + unz_s* s; + uLong uReadThis ; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (fseek(s->file,s->central_pos+22,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (fread(szComment,(uInt)uReadThis,1,s->file)!=1) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} diff --git a/ucon64/2.0/src/misc/unzip.h b/ucon64/2.0/src/misc/unzip.h new file mode 100644 index 0000000..fe3e9fc --- /dev/null +++ b/ucon64/2.0/src/misc/unzip.h @@ -0,0 +1,275 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 0.15 beta, Mar 19th, 1998, + + Copyright (C) 1998 Gilles Vollant + + This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + Encryption and multi volume ZipFile (span) are not supported. + Old compressions used by old PKZip 1.x are not supported + + THIS IS AN ALPHA VERSION. AT THIS STAGE OF DEVELOPPEMENT, SOMES API OR STRUCTURE + CAN CHANGE IN FUTURE VERSION !! + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ +/* for more info about .ZIP format, see + ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip */ + +#ifndef _unz_H +#define _unz_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\zlib\\zlib111.zip" or on an Unix computer + "zlib/zlib111.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _unz_H */ diff --git a/ucon64/2.0/src/misc/usb.c b/ucon64/2.0/src/misc/usb.c new file mode 100644 index 0000000..33a8cf9 --- /dev/null +++ b/ucon64/2.0/src/misc/usb.c @@ -0,0 +1,99 @@ +/* +usb.c - USB support + +Copyright (c) 2003 Ulrich Hecht +Copyright (c) 2004 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef USE_USB +#include +#include +#include "usb.h" +#include "misc.h" + + +usb_dev_handle * +usbport_open (struct usb_device *device) +{ + usb_dev_handle *handle = usb_open (device); + return handle; +} + + +int +usbport_close (usb_dev_handle *handle) +{ + usb_release_interface (handle, 0); + return usb_close (handle); +} + + +int +usbport_read (usb_dev_handle *handle, char *buffer, int buffer_size) +{ + int result; + + result = usb_bulk_read (handle, 0x83, buffer, buffer_size, 20000); + if (result == -1) + { + fprintf (stderr, "ERROR: Could not read requested number of bytes read from USB\n" + " %s\n", usb_strerror ()); + return -1; + } + return result; +} + + +int +usbport_write (usb_dev_handle *handle, char *buffer, int buffer_size) +{ + int result; + + result = usb_bulk_write (handle, 0x4, buffer, buffer_size, 20000); + if (result == -1) + { + fprintf (stderr, "ERROR: Could not write requested number of bytes read to USB\n" + " %s\n", usb_strerror ()); + return -1; + } + return result; +} + + +struct usb_device * +usbport_probe (int vendor_id, int product_id) +{ + struct usb_bus *bus; + struct usb_device *dev; + + usb_init (); + usb_find_busses (); + usb_find_devices (); + + for (bus = usb_busses; bus; bus = bus->next) // usb_busses is present in libusb + for (dev = bus->devices; dev; dev = dev->next) + if ((dev->descriptor.idVendor == vendor_id) && + (dev->descriptor.idProduct == product_id)) + return dev; + + return NULL; +} + +#endif // USE_USB diff --git a/ucon64/2.0/src/misc/usb.h b/ucon64/2.0/src/misc/usb.h new file mode 100644 index 0000000..1322701 --- /dev/null +++ b/ucon64/2.0/src/misc/usb.h @@ -0,0 +1,37 @@ +/* +usb.h - USB support + +Copyright (c) 2003 Ulrich Hecht +Copyright (c) 2004 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef MISC_USB_H +#define MISC_USB_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef USE_USB +#include + +extern struct usb_device *usbport_probe (int vendor_id, int product_id); +extern usb_dev_handle *usbport_open (struct usb_device *device); +extern int usbport_close (usb_dev_handle *handle); +extern int usbport_read (usb_dev_handle *handle, char *buffer, int buffer_size); +extern int usbport_write (usb_dev_handle *handle, char *buffer, int buffer_size); + +#endif // USE_USB +#endif // MISC_USB_H diff --git a/ucon64/2.0/src/patch/aps.c b/ucon64/2.0/src/patch/aps.c new file mode 100644 index 0000000..f58bfe2 --- /dev/null +++ b/ucon64/2.0/src/patch/aps.c @@ -0,0 +1,528 @@ +/* +aps.c - Advanced Patch System support for uCON64 + +Copyright (c) 1998 Silo/BlackBag +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2002 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "misc/bswap.h" +#include "misc/file.h" +#include "misc/misc.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "aps.h" + + +#define N64APS_DESCRIPTION_LEN 50 +#define N64APS_BUFFERSIZE 255 +#define N64APS_MAGICLENGTH 5 + +const st_getopt2_t aps_usage[] = + { + { + "a", 0, 0, UCON64_A, + NULL, "apply APS PATCH to ROM (APS<=v1.2)", + &ucon64_wf[WF_OBJ_ALL_STOP] + }, + { + "mka", 1, 0, UCON64_MKA, + "ORG_ROM", "create APS patch; ROM should be the modified ROM", + &ucon64_wf[WF_OBJ_ALL_STOP] + }, + { + "na", 1, 0, UCON64_NA, + "DESC", "change APS single line DESCRIPTION", + NULL + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + +char n64aps_magic[] = "APS10"; +unsigned char n64aps_patchtype = 1, n64aps_encodingmethod = 0; +FILE *n64aps_apsfile, *n64aps_orgfile, *n64aps_modfile; +int n64aps_changefound; + + +static void +readstdheader (void) +{ + char magic[N64APS_MAGICLENGTH], description[N64APS_DESCRIPTION_LEN + 1]; + + fread (magic, 1, N64APS_MAGICLENGTH, n64aps_apsfile); + if (strncmp (magic, n64aps_magic, N64APS_MAGICLENGTH) != 0) + { + fprintf (stderr, "ERROR: Not a valid APS file\n"); + fclose (n64aps_modfile); + fclose (n64aps_apsfile); + exit (1); + } + n64aps_patchtype = fgetc (n64aps_apsfile); + if (n64aps_patchtype != 1) // N64 patch + { + fprintf (stderr, "ERROR: Could not process patch file\n"); + fclose (n64aps_modfile); + fclose (n64aps_apsfile); + exit (1); + } + n64aps_encodingmethod = fgetc (n64aps_apsfile); + if (n64aps_encodingmethod != 0) // simple encoding + { + fprintf (stderr, "ERROR: Unknown or new encoding method\n"); + fclose (n64aps_modfile); + fclose (n64aps_apsfile); + exit (1); + } + + memset (description, ' ', N64APS_DESCRIPTION_LEN); + fread (description, 1, N64APS_DESCRIPTION_LEN, n64aps_apsfile); + description[N64APS_DESCRIPTION_LEN] = 0; + printf ("Description: %s\n", description); +} + + +static void +readN64header (void) +{ + unsigned int n64aps_magictest; + unsigned char buffer[8], APSbuffer[8], cartid[2], temp, teritory, APSteritory; + + fseek (n64aps_modfile, 0, SEEK_SET); + fread (&n64aps_magictest, 4, 1, n64aps_modfile); +#ifdef WORDS_BIGENDIAN + n64aps_magictest = bswap_32 (n64aps_magictest); +#endif + buffer[0] = fgetc (n64aps_apsfile); // APS format + if (((n64aps_magictest == 0x12408037) && (buffer[0] == 1)) || + ((n64aps_magictest != 0x12408037 && (buffer[0] == 0)))) + // 0 for Doctor format, 1 for everything else + { + fprintf (stderr, "ERROR: Image is in the wrong format\n"); + fclose (n64aps_modfile); + fclose (n64aps_apsfile); + exit (1); + } + + fseek (n64aps_modfile, 60, SEEK_SET); // cart id + fread (cartid, 1, 2, n64aps_modfile); + fread (buffer, 1, 2, n64aps_apsfile); + if (n64aps_magictest == 0x12408037) + { + temp = cartid[0]; + cartid[0] = cartid[1]; + cartid[1] = temp; + } + if ((buffer[0] != cartid[0]) || (buffer[1] != cartid[1])) + { + fprintf (stderr, "ERROR: This patch does not belong to this image\n"); + fclose (n64aps_modfile); + fclose (n64aps_apsfile); + exit (1); + } + + if (n64aps_magictest == 0x12408037) + fseek (n64aps_modfile, 63, SEEK_SET); // teritory + else + fseek (n64aps_modfile, 62, SEEK_SET); + teritory = fgetc (n64aps_modfile); + APSteritory = fgetc (n64aps_apsfile); + if (teritory != APSteritory) + { + printf ("WARNING: Wrong country\n"); +#if 0 + if (!force) + { + fclose (n64aps_modfile); + fclose (n64aps_apsfile); + exit (1); + } +#endif + } + + fseek (n64aps_modfile, 16, SEEK_SET); // CRC header position + fread (buffer, 1, 8, n64aps_modfile); + fread (APSbuffer, 1, 8, n64aps_apsfile); + if (n64aps_magictest == 0x12408037) + ucon64_bswap16_n (buffer, 8); + if (memcmp (APSbuffer, buffer, 8)) + { + printf ("WARNING: Incorrect image\n"); +#if 0 + if (!force) + { + fclose (n64aps_modfile); + fclose (n64aps_apsfile); + exit (1); + } +#endif + } + + fseek (n64aps_apsfile, 5, SEEK_CUR); + fseek (n64aps_modfile, 0, SEEK_SET); +} + + +static void +readsizeheader (int modsize, const char *modname) +{ + int orgsize, i; + + fread (&orgsize, 4, 1, n64aps_apsfile); +#ifdef WORDS_BIGENDIAN + orgsize = bswap_32 (orgsize); +#endif + if (modsize != orgsize) // resize file + { + if (orgsize < modsize) + { + fclose (n64aps_modfile); + if (truncate (modname, orgsize) != 0) + fprintf (stderr, "ERROR: Truncate failed\n"); + if ((n64aps_modfile = fopen (modname, "rb")) == NULL) + { + fprintf (stderr, "ERROR: Could not open %s after truncation\n", modname); + exit (1); + } + } + else + { + fseek (n64aps_modfile, 0, SEEK_END); + for (i = 0; i < (orgsize - modsize); i++) + fputc (0, n64aps_modfile); + } + } +// fseek (n64aps_modfile, 0, SEEK_SET); +} + + +static void +readpatch (void) +{ + int APSreadlen, offset; + unsigned char buffer[N64APS_BUFFERSIZE], size; + + while ((APSreadlen = fread (&offset, 1, 4, n64aps_apsfile))) + { +#ifdef WORDS_BIGENDIAN + offset = bswap_32 (offset); +#endif + if ((size = fgetc (n64aps_apsfile))) + { + fread (buffer, 1, size, n64aps_apsfile); + if ((fseek (n64aps_modfile, offset, SEEK_SET)) != 0) + { + fprintf (stderr, "ERROR: Seek failed\n"); + exit (1); + } + fwrite (buffer, 1, size, n64aps_modfile); + } + else // apply an RLE block + { + unsigned char data, len; + int i; + + data = fgetc (n64aps_apsfile), + len = fgetc (n64aps_apsfile); + + if ((fseek (n64aps_modfile, offset, SEEK_SET)) != 0) + { + fprintf (stderr, "ERROR: Seek failed\n"); + exit (1); + } + for (i = 0; i < len; i++) + fputc (data, n64aps_modfile); + } + } +} + + +// based on source code (version 1.2 981217) by Silo / BlackBag +int +aps_apply (const char *mod, const char *apsname) +{ + char modname[FILENAME_MAX]; + int size = fsizeof (mod); + + strcpy (modname, mod); + ucon64_file_handler (modname, NULL, 0); + fcopy (mod, 0, size, modname, "wb"); // no copy if one file + + if ((n64aps_modfile = fopen (modname, "r+b")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], modname); + exit (1); + } + if ((n64aps_apsfile = fopen (apsname, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], apsname); + exit (1); + } + + readstdheader (); + readN64header (); + readsizeheader (size, modname); + + readpatch (); + + fclose (n64aps_modfile); + fclose (n64aps_apsfile); + + printf (ucon64_msg[WROTE], modname); + return 0; +} + + +static int +n64caps_checkfile (FILE *file, const char *filename) +{ + unsigned int n64aps_magictest; + + fread (&n64aps_magictest, 4, 1, file); +#ifdef WORDS_BIGENDIAN + n64aps_magictest = bswap_32 (n64aps_magictest); +#endif + fseek (file, 0, SEEK_SET); + + if (n64aps_magictest != 0x12408037 && n64aps_magictest != 0x40123780) + { + fprintf (stderr, "ERROR: %s is an invalid N64 image\n", filename); + return FALSE; + } + else + return TRUE; +} + + +static void +writestdheader (void) +{ + char description[N64APS_DESCRIPTION_LEN]; + + fwrite (n64aps_magic, 1, N64APS_MAGICLENGTH, n64aps_apsfile); + fputc (n64aps_patchtype, n64aps_apsfile); + fputc (n64aps_encodingmethod, n64aps_apsfile); + + memset (description, ' ', N64APS_DESCRIPTION_LEN); + fwrite (description, 1, N64APS_DESCRIPTION_LEN, n64aps_apsfile); +} + + +static void +writeN64header (void) +{ + unsigned int n64aps_magictest; + unsigned char buffer[8], teritory, cartid[2], temp; + + fread (&n64aps_magictest, 4, 1, n64aps_orgfile); +#ifdef WORDS_BIGENDIAN + n64aps_magictest = bswap_32 (n64aps_magictest); +#endif + + if (n64aps_magictest == 0x12408037) // 0 for Doctor format, 1 for everything else + fputc (0, n64aps_apsfile); + else + fputc (1, n64aps_apsfile); + + fseek (n64aps_orgfile, 60, SEEK_SET); + fread (cartid, 1, 2, n64aps_orgfile); + if (n64aps_magictest == 0x12408037) + { + temp = cartid[0]; + cartid[0] = cartid[1]; + cartid[1] = temp; + } + fwrite (cartid, 1, 2, n64aps_apsfile); + + if (n64aps_magictest == 0x12408037) + fseek (n64aps_orgfile, 63, SEEK_SET); + else + fseek (n64aps_orgfile, 62, SEEK_SET); + teritory = fgetc (n64aps_orgfile); + fputc (teritory, n64aps_apsfile); + + fseek (n64aps_orgfile, 0x10, SEEK_SET); // CRC header position + fread (buffer, 1, 8, n64aps_orgfile); + if (n64aps_magictest == 0x12408037) + ucon64_bswap16_n (buffer, 8); + + fwrite (buffer, 1, 8, n64aps_apsfile); + memset (buffer, 0, 5); + fwrite (buffer, 1, 5, n64aps_apsfile); // pad + + fseek (n64aps_orgfile, 0, SEEK_SET); +} + + +static void +writesizeheader (int orgsize, int newsize) +{ + if (orgsize != newsize) + n64aps_changefound = TRUE; + +#ifdef WORDS_BIGENDIAN + newsize = bswap_32 (newsize); +#endif + fwrite (&newsize, 4, 1, n64aps_apsfile); +} + + +static void +writepatch (void) +// currently RLE is not supported +{ + int orgreadlen, newreadlen, filepos, changedstart = 0, changedoffset = 0, + i, changedlen = 0, changefound = 0; + unsigned char orgbuffer[N64APS_BUFFERSIZE], newbuffer[N64APS_BUFFERSIZE]; + + fseek (n64aps_orgfile, 0, SEEK_SET); + fseek (n64aps_modfile, 0, SEEK_SET); + filepos = 0; + while ((newreadlen = fread (newbuffer, 1, N64APS_BUFFERSIZE, n64aps_modfile))) + { + orgreadlen = fread (orgbuffer, 1, N64APS_BUFFERSIZE, n64aps_orgfile); + for (i = orgreadlen; i < newreadlen; i++) + orgbuffer[i] = 0; + + for (i = 0; i < newreadlen; i++) + { + if (newbuffer[i] != orgbuffer[i]) + { + if (!changefound) + { + changedstart = filepos + i; + changedoffset = i; + changedlen = 0; + changefound = TRUE; + n64aps_changefound = TRUE; + } + changedlen++; + } + else if (changefound) + { +#ifdef WORDS_BIGENDIAN + changedstart = bswap_32 (changedstart); +#endif + fwrite (&changedstart, 4, 1, n64aps_apsfile); + fputc (changedlen, n64aps_apsfile); + fwrite (newbuffer + changedoffset, 1, changedlen, n64aps_apsfile); + changefound = FALSE; + } + } + + if (changefound) + { +#ifdef WORDS_BIGENDIAN + changedstart = bswap_32 (changedstart); +#endif + fwrite (&changedstart, 4, 1, n64aps_apsfile); + fputc (changedlen, n64aps_apsfile); + fwrite (newbuffer + changedoffset, 1, changedlen, n64aps_apsfile); + changefound = FALSE; + } + + filepos += newreadlen; + } +} + + +// based on source code (version 1.2 981217) by Silo / BlackBag +int +aps_create (const char *orgname, const char *modname) +{ + char apsname[FILENAME_MAX]; + + if ((n64aps_orgfile = fopen (orgname, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], orgname); + exit (1); + } + if ((n64aps_modfile = fopen (modname, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], modname); + exit (1); + } + strcpy (apsname, modname); + set_suffix (apsname, ".aps"); + ucon64_file_handler (apsname, NULL, 0); + if ((n64aps_apsfile = fopen (apsname, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], apsname); + exit (1); + } + + if (!n64caps_checkfile (n64aps_orgfile, orgname) || + !n64caps_checkfile (n64aps_modfile, modname)) + exit (1); // n64caps_checkfile() already + // displayed an error message + n64aps_changefound = FALSE; + + writestdheader (); + writeN64header (); + writesizeheader (fsizeof (orgname), fsizeof (modname)); + + printf ("Searching differences..."); + fflush (stdout); + writepatch (); + printf (" done\n"); + + fclose (n64aps_modfile); + fclose (n64aps_orgfile); + fclose (n64aps_apsfile); + + if (!n64aps_changefound) + { + printf ("%s and %s are identical\n" + "Removing: %s\n", orgname, modname, apsname); + remove (apsname); + return -1; + } + else + printf (ucon64_msg[WROTE], apsname); + + return 0; +} + + +int +aps_set_desc (const char *aps, const char *description) +{ + char desc[50], apsname[FILENAME_MAX]; + + strcpy (apsname, aps); + memset (desc, ' ', 50); + strncpy (desc, description, strlen (description)); + ucon64_file_handler (apsname, NULL, 0); + fcopy (aps, 0, fsizeof (aps), apsname, "wb"); // no copy if one file + ucon64_fwrite (desc, 7, 50, apsname, "r+b"); + + printf (ucon64_msg[WROTE], apsname); + return 0; +} diff --git a/ucon64/2.0/src/patch/aps.h b/ucon64/2.0/src/patch/aps.h new file mode 100644 index 0000000..ec030bf --- /dev/null +++ b/ucon64/2.0/src/patch/aps.h @@ -0,0 +1,29 @@ +/* +aps.h - Advanced Patch System support for uCON64 + +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef APS_H +#define APS_H + +extern const st_getopt2_t aps_usage[]; + +extern int aps_apply (const char *modname, const char *apsname); +extern int aps_create (const char *orgname, const char *modname); +extern int aps_set_desc (const char *apsname, const char *description); +#endif diff --git a/ucon64/2.0/src/patch/bsl.c b/ucon64/2.0/src/patch/bsl.c new file mode 100644 index 0000000..e58835e --- /dev/null +++ b/ucon64/2.0/src/patch/bsl.c @@ -0,0 +1,116 @@ +/* +bsl.c - Baseline patcher support for uCON64 + +Copyright (c) ???? - ???? The White Knight +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2003 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include "misc/misc.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "bsl.h" + + +const st_getopt2_t bsl_usage[] = + { + { + "b", 0, 0, UCON64_B, + NULL, "apply Baseline/BSL PATCH to ROM", + &ucon64_wf[WF_OBJ_ALL_STOP] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +int +bsl_apply (const char *mod, const char *bslname) +{ + FILE *modfile, *bslfile; + unsigned char byte; + char buf[4096], modname[FILENAME_MAX]; + int data, nbytes, offset; + + strcpy (modname, mod); + ucon64_file_handler (modname, NULL, 0); + fcopy (mod, 0, fsizeof (mod), modname, "wb"); // no copy if one file + + if ((modfile = fopen (modname, "r+b")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], modname); + return -1; + } + if ((bslfile = fopen (bslname, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], bslname); + return -1; + } + + printf ("Applying BSL/Baseline patch...\n"); + + while (!feof (bslfile)) // we could use 1, but feof() makes it fail-safe + { + fscanf (bslfile, "%d\n", &offset); + fscanf (bslfile, "%d\n", &data); + if ((offset == -1) && (data == -1)) + break; + + fseek (modfile, offset, SEEK_SET); + fputc (data, modfile); + } + + fscanf (bslfile, "%d\n", &offset); + fscanf (bslfile, "%d\n", &nbytes); + fseek (modfile, offset, SEEK_SET); + if (nbytes > 0) + { + while (nbytes > 4096) + { + fread (buf, 4096, 1, bslfile); + fwrite (buf, 4096, 1, modfile); + nbytes -= 4096; + } + while (nbytes-- >= 0) // yes, one byte more than the + { // _value_ read from the BSL file + byte = fgetc (bslfile); + fputc (byte, modfile); + } + } + + printf ("Patching complete\n\n"); + printf (ucon64_msg[WROTE], modname); + printf ("\n" + "NOTE: Sometimes you have to add/strip a 512 bytes header when you patch a ROM\n" + " This means you must modify for example a SNES ROM with -swc or -stp or\n" + " the patch will not work\n"); + + fclose (bslfile); + fclose (modfile); + + return 0; +} diff --git a/ucon64/2.0/src/patch/bsl.h b/ucon64/2.0/src/patch/bsl.h new file mode 100644 index 0000000..108c683 --- /dev/null +++ b/ucon64/2.0/src/patch/bsl.h @@ -0,0 +1,26 @@ +/* +bsl.h - Baseline patcher support for uCON64 + +Copyright (c) ???? - ???? The White Knight +Copyright (c) 1999 - 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef BSL_H +#define BSL_H +extern const st_getopt2_t bsl_usage[]; +extern int bsl_apply (const char *modname, const char *bslname); +#endif diff --git a/ucon64/2.0/src/patch/gg.c b/ucon64/2.0/src/patch/gg.c new file mode 100644 index 0000000..da97994 --- /dev/null +++ b/ucon64/2.0/src/patch/gg.c @@ -0,0 +1,1133 @@ +/******************************************************************** + * Copyright (c) 2001 by WyrmCorp . + * All rights reserved. Distributed under the BSD Software License. + * + * 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. + * + * 3. Neither the name of the nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 + * REGENTS 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. + * + * + * + * uggconv - Universal Game Genie (tm) Code Convertor + * + * Game Genie (tm) is a trademark of Galoob and is used for + * purely informational purposes. No endorsement of this + * utility by Galoob is implied. + * + * This application is 100% ANSI C. Compile with + * gcc uggconv.c -o uggconv + * or the equivalent for your platform. + */ +/* +Portions copyright (c) 2001 - 2002 NoisyB +Portions copyright (c) 2002 dbjh +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include "misc/file.h" +#include "misc/misc.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "console/snes.h" +#include "console/genesis.h" +#include "console/nes.h" +#include "console/sms.h" +#include "console/gb.h" +#include "gg.h" + + +#define GAME_GENIE_MAX_STRLEN 12 + + +const st_getopt2_t gg_usage[] = + { + { + "gge", 1, 0, UCON64_GGE, + "CODE", "encode and display Game Genie code\n" + "example: " OPTION_LONG_S "gge" OPTARG_S "CODE " OPTION_LONG_S "sms or " + OPTION_LONG_S "gge" OPTARG_S "CODE " OPTION_LONG_S "gb\n" + "CODE" OPTARG_S "'AAAA:VV' or CODE" OPTARG_S "'AAAA:VV:CC'\n" + OPTION_LONG_S "gge" OPTARG_S "CODE " OPTION_LONG_S "gen\n" + "CODE" OPTARG_S "'AAAAAA:VVVV'\n" + OPTION_LONG_S "gge" OPTARG_S "CODE " OPTION_LONG_S "nes\n" + "CODE" OPTARG_S "'AAAA:VV' or CODE" OPTARG_S "'AAAA:VV:CC'\n" + OPTION_LONG_S "gge" OPTARG_S "CODE " OPTION_LONG_S "snes\n" + "CODE" OPTARG_S "'AAAAAA:VV'", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_NO_ROM] + }, + { + "ggd", 1, 0, UCON64_GGD, + "GG_CODE", "decode Game Genie code\n" + "example: " OPTION_LONG_S "ggd" OPTARG_S "GG_CODE " OPTION_LONG_S "sms or " + OPTION_LONG_S "ggd" OPTARG_S "GG_CODE " OPTION_LONG_S "gb\n" + "GG_CODE" OPTARG_S "'XXX-XXX' or GG_CODE" OPTARG_S "'XXX-XXX-XXX'\n" + OPTION_LONG_S "ggd" OPTARG_S "GG_CODE " OPTION_LONG_S "gen\n" + "GG_CODE" OPTARG_S "'XXXX-XXXX'\n" + OPTION_LONG_S "ggd" OPTARG_S "GG_CODE " OPTION_LONG_S "nes\n" + "GG_CODE" OPTARG_S "'XXXXXX' or GG_CODE" OPTARG_S "'XXXXXXXX'\n" + OPTION_LONG_S "ggd" OPTARG_S "GG_CODE " OPTION_LONG_S "snes\n" + "GG_CODE" OPTARG_S "'XXXX-XXXX'", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_NO_ROM] + }, + { + "gg", 1, 0, UCON64_GG, + "GG_CODE", "apply Game Genie code (permanently)\n" + "example: like above but a ROM is required\n" + "supported are:\n" + "Game Boy/(Super GB)/GB Pocket/Color GB/(GB Advance),\n" + "Sega Master System(II/III)/Game Gear (Handheld),\n" + "Genesis/Sega Mega Drive/Sega CD/32X/Nomad,\n" + "Nintendo Entertainment System/NES/Famicom/Game Axe (Redant),\n" + "Super Nintendo Entertainment System/SNES/Super Famicom", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + + +static st_rominfo_t *gg_rominfo; +static int CPUaddress; + + +/********************************************************************* + * + * GAME BOY / GAME GEAR ROUTINES + * + *********************************************************************/ + +static int gameGenieDecodeGameBoy (const char *in, char *out); +static int gameGenieEncodeGameBoy (const char *in, char *out); + +/********************************************************************* + * + * MEGADRIVE ROUTINES + * + *********************************************************************/ + +static int isGenesisChar (char c); +static int genesisValue (char c); +static int gameGenieDecodeMegadrive (const char *in, char *out); +static int gameGenieEncodeMegadrive (const char *in, char *out); + +/********************************************************************* + * + * NES ROUTINES + * + ********************************************************************/ + +#define encodeNES(v, n, m, s) data[n] |= (v >> s) & m +#define decodeNES(v, n, m, s) v |= (data[n] & m) << s + +static char mapNesChar (char hex); +static char unmapNesChar (char genie); +static int isNesChar (char c); +static int gameGenieDecodeNES (const char *in, char *out); +static int gameGenieEncodeNES (const char *in, char *out); + +/********************************************************************* + * + * SNES ROUTINES + * + ********************************************************************/ + +#define encodeSNES(x, y) transposed |= (((address & (0xc00000 >> (2*y))) << (2*y)) >> (2*x)) +#define decodeSNES(x, y) address |= (((transposed & (0xc00000 >> (2*x))) << (2*x)) >> (2*y)) + +static char mapSnesChar (char hex); +static char unmapSnesChar (char genie); +static int gameGenieDecodeSNES (const char *in, char *out); +static int gameGenieEncodeSNES (const char *in, char *out); + + +/********************************************************************* + * + * UTILITY ROUTINES + * + ********************************************************************/ + +char +hexDigit (int value) +{ + switch (toupper (value)) + { + case 0: + return '0'; + case 1: + return '1'; + case 2: + return '2'; + case 3: + return '3'; + case 4: + return '4'; + case 5: + return '5'; + case 6: + return '6'; + case 7: + return '7'; + case 8: + return '8'; + case 9: + return '9'; + case 10: + return 'A'; + case 11: + return 'B'; + case 12: + return 'C'; + case 13: + return 'D'; + case 14: + return 'E'; + case 15: + return 'F'; + default: + break; + } + return '?'; + +} + + +int +hexValue (char digit) +{ + switch (toupper (digit)) + { + case '0': + return 0; + case '1': + return 1; + case '2': + return 2; + case '3': + return 3; + case '4': + return 4; + case '5': + return 5; + case '6': + return 6; + case '7': + return 7; + case '8': + return 8; + case '9': + return 9; + case 'A': + return 10; + case 'B': + return 11; + case 'C': + return 12; + case 'D': + return 13; + case 'E': + return 14; + case 'F': + return 15; + default: + break; + } + return 0; +} + + +int +hexByteValue (char x, char y) +{ + return (hexValue (x) << 4) + hexValue (y); +} + + +/********************************************************************* + * + * GAME BOY / GAME GEAR ROUTINES + * + *********************************************************************/ + +int +gameGenieDecodeGameBoy (const char *in, char *out) +{ + int address = 0; + int value = 0; + int check = 0; + int haveCheck = 0; + int i; + + if (strlen (in) == 11 && in[3] == '-' && in[7] == '-') + haveCheck = 1; + else if (strlen (in) == 7 && in[3] == '-') + haveCheck = 0; + else + return -1; + for (i = 0; i < (int) strlen (in); ++i) + if (in[i] != '-' && !isxdigit ((int) in[i])) + return -1; + if (hexValue (in[6]) < 8) + return -1; + + value = hexByteValue (in[0], in[1]); + + address = + (hexValue (in[2]) << 8) | (hexValue (in[4]) << 4) | hexValue (in[5]) | + ((~hexValue (in[6]) & 0xf) << 12); + + if (haveCheck) + { + check = hexByteValue (in[8], in[10]); + check = ~check; + check = (((check & 0xfc) >> 2) | ((check & 0x03) << 6)) ^ 0x45; + sprintf (out, "%04X:%02X:%02X", address, value, check); + } + else + sprintf (out, "%04X:%02X", address, value); + + return 0; +} + + +int +gameGenieEncodeGameBoy (const char *in, char *out) +{ + int check = 0; + int haveCheck = 0; + int i; + + if (strlen (in) == 10 && in[4] == ':' && in[7] == ':') + haveCheck = 1; + else if (strlen (in) == 7 && in[4] == ':') + haveCheck = 0; + else + return -1; + for (i = 0; i < (int) strlen (in); ++i) + if (in[i] != ':' && !isxdigit ((int) in[i])) + return -1; + if (hexValue (in[0]) > 7) + return -1; + + out[0] = toupper (in[5]); + out[1] = toupper (in[6]); + out[2] = toupper (in[1]); + out[3] = '-'; + out[4] = toupper (in[2]); + out[5] = toupper (in[3]); + out[6] = hexDigit (~hexValue (in[0]) & 0xf); + out[7] = 0; + + if (haveCheck) + { + check = hexByteValue (in[8], in[10]); + check = ~check; + check = (((check & 0xfc) >> 2) | ((check & 0x03) << 6)) ^ 0x45; + + check = hexByteValue (in[8], in[9]); + check ^= 0x45; + check = ~(((check & 0xc0) >> 6) | ((check & 0x3f) << 2)); + i = (check & 0xf0) >> 4; + out[7] = '-'; + out[8] = hexDigit (i & 0xf); + out[9] = hexDigit ((i ^ 8) & 0xf); + out[10] = hexDigit (check & 0xf); + out[11] = 0; + } + + return 0; +} + + +/********************************************************************* + * + * MEGADRIVE ROUTINES + * + *********************************************************************/ + +static const char genesisChars[] = "ABCDEFGHJKLMNPRSTVWXYZ0123456789"; + + +int +isGenesisChar (char c) +{ + return strchr (genesisChars, toupper (c)) != 0; +} + + +int +genesisValue (char c) +{ + return strchr (genesisChars, toupper (c)) - genesisChars; +} + + +int +gameGenieDecodeMegadrive (const char *in, char *out) +{ + int address = 0; + int value = 0; + int data[8]; + int i; + + if (strlen (in) != 9 || in[4] != '-') + return -1; + for (i = 0; i < 9; ++i) + if (in[i] != '-' && !isGenesisChar (in[i])) + return -1; + + for (i = 0; i < 4; ++i) + data[i] = genesisValue (in[i]); + for (i = 5; i < 9; ++i) + data[i - 1] = genesisValue (in[i]); + + address = 0; + address |= (data[3] & 0x0f) << 20; + address |= (data[4] & 0x1e) << 15; + address |= (data[1] & 0x03) << 14; + address |= (data[2] & 0x1f) << 9; + address |= (data[3] & 0x10) << 4; + address |= (data[6] & 0x07) << 5; + address |= (data[7] & 0x1f); + + value = 0; + value |= (data[5] & 0x01) << 15; + value |= (data[6] & 0x18) << 10; + value |= (data[4] & 0x01) << 12; + value |= (data[5] & 0x1e) << 7; + value |= (data[0] & 0x1f) << 3; + value |= (data[1] & 0x16) >> 2; + + sprintf (out, "%06X:%04X", address, value); + + return 0; +} + + +int +gameGenieEncodeMegadrive (const char *in, char *out) +{ + int address = 0; + int value = 0; + int data[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int i; + + if (strlen (in) != 11 || in[6] != ':') + return -1; + for (i = 0; i < 11; ++i) + if (in[i] != ':' && !isxdigit ((int) in[i])) + return -1; + + sscanf (in, "%x:%x", &address, &value); + + data[3] |= (address >> 20) & 0x0f; + data[4] |= (address >> 15) & 0x1e; + data[1] |= (address >> 14) & 0x03; + data[2] |= (address >> 9) & 0x1f; + data[3] |= (address >> 4) & 0x10; + data[6] |= (address >> 5) & 0x07; + data[7] |= (address & 0x1f); + + data[5] |= (value >> 15) & 0x01; + data[6] |= (value >> 10) & 0x18; + data[4] |= (value >> 12) & 0x01; + data[5] |= (value >> 7) & 0x1e; + data[0] |= (value >> 3) & 0x1f; + data[1] |= (value << 2) & 0x16; + + for (i = 0; i < 4; ++i) + out[i] = genesisChars[data[i]]; + out[4] = '-'; + for (i = 5; i < 9; ++i) + out[i] = genesisChars[data[i - 1]]; + out[9] = 0; + + return 0; +} + + +/********************************************************************* + * + * NES ROUTINES + * + ********************************************************************/ + +char +mapNesChar (char hex) +{ + switch (toupper (hex)) + { + case '0': + return 'A'; + case '1': + return 'P'; + case '2': + return 'Z'; + case '3': + return 'L'; + case '4': + return 'G'; + case '5': + return 'I'; + case '6': + return 'T'; + case '7': + return 'Y'; + case '8': + return 'E'; + case '9': + return 'O'; + case 'A': + return 'X'; + case 'B': + return 'U'; + case 'C': + return 'K'; + case 'D': + return 'S'; + case 'E': + return 'V'; + case 'F': + return 'N'; + default: + break; + } + return '?'; +} + + +char +unmapNesChar (char genie) +{ + switch (toupper (genie)) + { + case 'A': + return '0'; + case 'P': + return '1'; + case 'Z': + return '2'; + case 'L': + return '3'; + case 'G': + return '4'; + case 'I': + return '5'; + case 'T': + return '6'; + case 'Y': + return '7'; + case 'E': + return '8'; + case 'O': + return '9'; + case 'X': + return 'A'; + case 'U': + return 'B'; + case 'K': + return 'C'; + case 'S': + return 'D'; + case 'V': + return 'E'; + case 'N': + return 'F'; + default: + break; + } + return '0'; +} + + +int +isNesChar (char c) +{ + return strchr ("APZLGITYEOXUKSVN-", toupper (c)) != 0; +} + + +int +gameGenieDecodeNES (const char *in, char *out) +{ + int address = 0; + int value = 0; + int check = 0; + int haveCheck = 0; + int data[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int i; + + if (strlen (in) == 8) + haveCheck = 1; + else if (strlen (in) == 6) + haveCheck = 0; + else + return -1; + for (i = 0; i < (int) strlen (in); ++i) + if (!isNesChar (in[i])) + return -1; + + for (i = 0; i < 6; ++i) + data[i] = hexValue (unmapNesChar (in[i])); + if (haveCheck) + { + data[6] = hexValue (unmapNesChar (in[6])); + data[7] = hexValue (unmapNesChar (in[7])); + } + + address = 0x8000; /* force high bit on */ + decodeNES (address, 1, 8, 4); + decodeNES (address, 2, 7, 4); + decodeNES (address, 3, 7, 12); + decodeNES (address, 3, 8, 0); + decodeNES (address, 4, 7, 0); + decodeNES (address, 4, 8, 8); + decodeNES (address, 5, 7, 8); + if (haveCheck) + { + decodeNES (value, 0, 7, 0); + decodeNES (value, 0, 8, 4); + decodeNES (value, 1, 7, 4); + decodeNES (value, 7, 8, 0); + decodeNES (check, 6, 7, 0); + decodeNES (check, 6, 8, 0); + decodeNES (check, 6, 8, 4); + decodeNES (check, 7, 7, 4); + } + else + { + decodeNES (value, 0, 7, 0); + decodeNES (value, 0, 8, 4); + decodeNES (value, 1, 7, 4); + decodeNES (value, 5, 8, 0); + } + + if (haveCheck) + sprintf (out, "%04X:%02X:%02X", address, value, check); + else + sprintf (out, "%04X:%02X", address, value); + + return 0; +} + + +int +gameGenieEncodeNES (const char *in, char *out) +{ + int address = 0x8000; + int value = 0; + int check = 0; + int haveCheck = 0; + int data[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int i; + + if (strlen (in) == 10 && in[4] == ':' && in[7] == ':') + haveCheck = 1; + else if (strlen (in) == 7 && in[4] == ':') + haveCheck = 0; + else + return -1; + for (i = 0; i < (int) strlen (in); ++i) + if (in[i] != ':' && !isxdigit ((int) in[i])) + return -1; + + if (haveCheck) + sscanf (in, "%x:%x:%x", &address, &value, &check); + else + sscanf (in, "%x:%x", &address, &value); + + /* Encode address with transposition cipher. + * Do not encode the high address bit (optional, Galoob isn't + * consistent but usually doesn't. + */ + encodeNES (address, 1, 8, 4); + encodeNES (address, 2, 7, 4); + encodeNES (address, 3, 7, 12); + encodeNES (address, 3, 8, 0); + encodeNES (address, 4, 7, 0); + encodeNES (address, 4, 8, 8); + encodeNES (address, 5, 7, 8); + if (haveCheck) + { + encodeNES (value, 0, 7, 0); + encodeNES (value, 0, 8, 4); + encodeNES (value, 1, 7, 4); + encodeNES (value, 7, 8, 0); + encodeNES (check, 6, 7, 0); + encodeNES (check, 6, 8, 0); + encodeNES (check, 6, 8, 4); + encodeNES (check, 7, 7, 4); + } + else + { + encodeNES (value, 0, 7, 0); + encodeNES (value, 0, 8, 4); + encodeNES (value, 1, 7, 4); + encodeNES (value, 5, 8, 0); + } + + if (haveCheck) + { + sprintf (out, "%c%c%c%c%c%c%c%c", + mapNesChar (hexDigit (data[0])), + mapNesChar (hexDigit (data[1])), + mapNesChar (hexDigit (data[2])), + mapNesChar (hexDigit (data[3])), + mapNesChar (hexDigit (data[4])), + mapNesChar (hexDigit (data[5])), + mapNesChar (hexDigit (data[6])), + mapNesChar (hexDigit (data[7]))); + } + else + { + sprintf (out, "%c%c%c%c%c%c", + mapNesChar (hexDigit (data[0])), + mapNesChar (hexDigit (data[1])), + mapNesChar (hexDigit (data[2])), + mapNesChar (hexDigit (data[3])), + mapNesChar (hexDigit (data[4])), + mapNesChar (hexDigit (data[5]))); + } + + return 0; +} + + +/********************************************************************* + * + * SNES ROUTINES + * + ********************************************************************/ + +char +mapSnesChar (char hex) +{ + switch (toupper (hex)) + { + case '0': + return 'D'; + case '1': + return 'F'; + case '2': + return '4'; + case '3': + return '7'; + case '4': + return '0'; + case '5': + return '9'; + case '6': + return '1'; + case '7': + return '5'; + case '8': + return '6'; + case '9': + return 'B'; + case 'A': + return 'C'; + case 'B': + return '8'; + case 'C': + return 'A'; + case 'D': + return '2'; + case 'E': + return '3'; + case 'F': + return 'E'; + default: + break; + } + return ' '; +} + + +char +unmapSnesChar (char genie) +{ + switch (toupper (genie)) + { + case 'D': + return '0'; + case 'F': + return '1'; + case '4': + return '2'; + case '7': + return '3'; + case '0': + return '4'; + case '9': + return '5'; + case '1': + return '6'; + case '5': + return '7'; + case '6': + return '8'; + case 'B': + return '9'; + case 'C': + return 'A'; + case '8': + return 'B'; + case 'A': + return 'C'; + case '2': + return 'D'; + case '3': + return 'E'; + case 'E': + return 'F'; + default: + break; + } + return ' '; +} + + +int +gameGenieDecodeSNES (const char *in, char *out) +{ + int value, hirom, address, transposed; + + if (!isxdigit ((int) in[0]) || !isxdigit ((int) in[1]) || + !isxdigit ((int) in[2]) || !isxdigit ((int) in[3]) || + in[4] != '-' || + !isxdigit ((int) in[5]) || !isxdigit ((int) in[6]) || + !isxdigit ((int) in[7]) || !isxdigit ((int) in[8]) || in[9] != 0) + return -1; + + value = hexByteValue (unmapSnesChar (in[0]), unmapSnesChar (in[1])); + + transposed = hexValue (unmapSnesChar (in[8])) + + (hexValue (unmapSnesChar (in[7])) << 4) + + (hexValue (unmapSnesChar (in[6])) << 8) + + (hexValue (unmapSnesChar (in[5])) << 12) + + (hexValue (unmapSnesChar (in[3])) << 16) + + (hexValue (unmapSnesChar (in[2])) << 20); + + address = 0; + decodeSNES (0, 4); + decodeSNES (1, 5); + decodeSNES (2, 8); + decodeSNES (3, 9); + decodeSNES (4, 7); + decodeSNES (5, 0); + decodeSNES (6, 1); + decodeSNES (7, 10); + decodeSNES (8, 11); + decodeSNES (9, 2); + decodeSNES (10, 3); + decodeSNES (11, 6); + + // if a ROM was specified snes.c will handle ucon64.snes_hirom + if (UCON64_ISSET (ucon64.snes_hirom)) // -hi or -nhi option was specified + hirom = ucon64.snes_hirom; + // if only a ROM was specified (not -hi or -nhi) the next if will fail for a + // handful of ROMs, namely Sufami Turbo ROMs and Extended ROMs + else if (gg_rominfo != 0) + hirom = gg_rominfo->header_start > SNES_HEADER_START ? 1 : 0; + else + hirom = 1; // I am less sure about the LoROM + // CPU -> ROM address conversion + CPUaddress = address; + if (hirom) + { + if (address >= 0xc00000 && address <= 0xffffff) + address -= 0xc00000; + else if (address >= 0x800000 && address <= 0xbfffff) + address -= 0x800000; + else if (address >= 0x400000 && address <= 0x7fffff) + address -= 0x400000; + } + else + { + if (address >= 0x808000) + address -= 0x808000; + else if (address >= 0x008000) + address -= 0x008000; + address = (address & 0x7fff) | ((address & 0xff0000) >> 1); + } + + sprintf (out, "%06X:%02X", address, value); + + return 0; +} + + +int +gameGenieEncodeSNES (const char *in, char *out) +{ + int value, address, transposed; + + if (!isxdigit ((int) in[0]) || !isxdigit ((int) in[1]) || + !isxdigit ((int) in[2]) || !isxdigit ((int) in[3]) || + !isxdigit ((int) in[4]) || !isxdigit ((int) in[5]) || + in[6] != ':' || !isxdigit ((int) in[7]) || !isxdigit ((int) in[8]) || in[9] != 0) + return -1; + + value = hexByteValue (mapSnesChar (in[7]), mapSnesChar (in[8])); + sscanf (in, "%x", &address); + + transposed = 0; + encodeSNES (0, 4); + encodeSNES (1, 5); + encodeSNES (2, 8); + encodeSNES (3, 9); + encodeSNES (4, 7); + encodeSNES (5, 0); + encodeSNES (6, 1); + encodeSNES (7, 10); + encodeSNES (8, 11); + encodeSNES (9, 2); + encodeSNES (10, 3); + encodeSNES (11, 6); + + sprintf (out, "%02X%c%c-%c%c%c%c", value, + mapSnesChar (hexDigit ((transposed & 0xf00000) >> 20)), + mapSnesChar (hexDigit ((transposed & 0x0f0000) >> 16)), + mapSnesChar (hexDigit ((transposed & 0x00f000) >> 12)), + mapSnesChar (hexDigit ((transposed & 0x000f00) >> 8)), + mapSnesChar (hexDigit ((transposed & 0x0000f0) >> 4)), + mapSnesChar (hexDigit ((transposed & 0x00000f)))); + + return 0; +} + + +#if 0 +static void +usage (void) +{ + puts ("uggconv v1.0 - Universal Game Genie (tm) Convertor"); + puts ("Copyright (c) 2001 by WyrmCorp "); + puts ("\nUsage:"); + puts ("Game Boy/Gear: uggconv -g [XXX-XXX] [XXX-XXX-XXX] [AAAA:VV] [AAAA:VV:CC] ..."); + puts ("Megadrive: uggconv -m [XXXX-XXXX] [AAAAAA:VVVV] ..."); + puts ("NES: uggconv -n [XXXXXX] [XXXXXXXX] [AAAA:VV] [AAAA:VV:CC] ..."); + puts ("SNES: uggconv -s [XXXX-XXXX] [AAAAAA:VV] ..."); + exit (1); +} +#endif + + +int +gg_main (int argc, const char **argv) +{ + char buffer[GAME_GENIE_MAX_STRLEN]; + int i; + int result = 0; + +#if 0 + if (argc < 3 || strlen (argv[1]) != 2 || argv[1][0] != '-') + usage (); + if (strchr ("gmns", argv[1][1]) == 0) + usage (); +#endif + + for (i = 2; i < argc; ++i) + { + if (strchr (argv[i], ':') == 0) + { + switch (argv[1][1]) + { + case 'g': + result = gameGenieDecodeGameBoy (argv[i], buffer); + break; + case 'm': + result = gameGenieDecodeMegadrive (argv[i], buffer); + break; + case 'n': + result = gameGenieDecodeNES (argv[i], buffer); + break; + case 's': + result = gameGenieDecodeSNES (argv[i], buffer); + break; + } + } + else + { + switch (argv[1][1]) + { + case 'g': + result = gameGenieEncodeGameBoy (argv[i], buffer); + break; + case 'm': + result = gameGenieEncodeMegadrive (argv[i], buffer); + break; + case 'n': + result = gameGenieEncodeNES (argv[i], buffer); + break; + case 's': + result = gameGenieEncodeSNES (argv[i], buffer); + break; + } + } + if (result != 0) + printf ("%-12s is badly formed\n", argv[i]); + else + { + if (CPUaddress != -1) // SNES decode specific + printf ("%-12s = %s (CPU address: %06X)\n", argv[i], buffer, CPUaddress); + else + printf ("%-12s = %s\n", argv[i], buffer); + } + } + return 0; +} + + +int gg_argc; +const char *gg_argv[128]; + + +int +gg_display (st_rominfo_t *rominfo, const char *code) +{ + gg_argv[0] = "uggconv"; + + switch (ucon64.console) + { + case UCON64_SNES: + gg_argv[1] = "-s"; + break; + + case UCON64_GEN: + gg_argv[1] = "-m"; + break; + + case UCON64_NES: + gg_argv[1] = "-n"; + break; + + case UCON64_GB: + case UCON64_SMS: + gg_argv[1] = "-g"; + break; + + default: + fprintf (stderr, + "ERROR: You must specify a ROM or force the console type\n" + " The force recognition option for SNES would be " OPTION_LONG_S "snes\n"); + return -1; + } + gg_argv[2] = code; + gg_argc = 3; + + if (ucon64.file_size > 0) // check if rominfo contains valid ROM info + gg_rominfo = rominfo; + else + gg_rominfo = 0; + CPUaddress = -1; + gg_main (gg_argc, gg_argv); + + return 0; +} + + +int +gg_apply (st_rominfo_t *rominfo, const char *code) +{ + int size = ucon64.file_size - rominfo->buheader_len, address, value, + result = -1; + char buf[MAXBUFSIZE], dest_name[FILENAME_MAX]; + + if (ucon64.file_size > 0) // check if rominfo contains valid ROM info + gg_rominfo = rominfo; + else + { + fprintf (stderr, "ERROR: You must specify a ROM to apply the code to\n"); + return -1; + } + + CPUaddress = -1; + switch (ucon64.console) + { + // interleaved images (GD3 (SNES) & SMD (Genesis)) should be allowed to + // be patched + case UCON64_GB: + case UCON64_SMS: + result = gameGenieDecodeGameBoy (code, buf); + break; + case UCON64_GEN: + result = gameGenieDecodeMegadrive (code, buf); + break; + case UCON64_NES: + result = gameGenieDecodeNES (code, buf); + break; + case UCON64_SNES: + result = gameGenieDecodeSNES (code, buf); + break; + default: + break; + } + + if (result != 0) + { + fprintf (stderr, "ERROR: Game Genie code %s is badly formed\n", code); + return -1; + } + + if (CPUaddress != -1) // SNES decode specific + printf ("%-12s = %s (CPU address: %06X)\n", code, buf, CPUaddress); + else + printf ("%-12s = %s\n", code, buf); + sscanf (buf, "%x:%x:*", &address, &value); + + if (address >= size) + { + fprintf (stderr, "ERROR: Address is too high for this ROM (%d)\n", address); + return -1; + } + + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); // no copy if one file + + fputc ('\n', stdout); + buf[0] = ucon64_fgetc (dest_name, address + rominfo->buheader_len); + dumper (stdout, buf, 1, address + rominfo->buheader_len, DUMPER_HEX); + + ucon64_fputc (dest_name, address + rominfo->buheader_len, value, "r+b"); + + buf[0] = value; + dumper (stdout, buf, 1, address + rominfo->buheader_len, DUMPER_HEX); + fputc ('\n', stdout); + + printf (ucon64_msg[WROTE], dest_name); + return 0; +} diff --git a/ucon64/2.0/src/patch/gg.h b/ucon64/2.0/src/patch/gg.h new file mode 100644 index 0000000..0cf8716 --- /dev/null +++ b/ucon64/2.0/src/patch/gg.h @@ -0,0 +1,27 @@ +/* +gg.h - GameGenie support for uCON64 + +Copyright (c) 2001 WyrmCorp . +Copyright (c) 2002 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef GG_H +#define GG_H +extern const st_getopt2_t gg_usage[]; +extern int gg_apply (st_rominfo_t *rominfo, const char *code); +extern int gg_display (st_rominfo_t *rominfo, const char *code); +#endif diff --git a/ucon64/2.0/src/patch/ips.c b/ucon64/2.0/src/patch/ips.c new file mode 100644 index 0000000..0ed936c --- /dev/null +++ b/ucon64/2.0/src/patch/ips.c @@ -0,0 +1,524 @@ +/* +ips.c - IPS support for uCON64 + +Copyright (c) ???? - ???? madman +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2002 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include "misc/misc.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "ips.h" + + +#define NO_RLE 256 +// for some strange reason 6 seems to be a general optimum value for RLE_START_THRESHOLD +#define RLE_START_THRESHOLD 6 // must be smaller than RLE_RESTART_THRESHOLD! +#define RLE_RESTART_THRESHOLD 13 +#define BRIDGE_LEN 5 +#define BUFSIZE 65535 +//#define DEBUG_IPS + +const st_getopt2_t ips_usage[] = + { + { + "i", 0, 0, UCON64_I, + NULL, "apply IPS PATCH to ROM (IPS<=v1.2)", + &ucon64_wf[WF_OBJ_ALL_STOP] + }, + { + "mki", 1, 0, UCON64_MKI, + "ORG_ROM", "create IPS patch; ROM should be the modified ROM", + &ucon64_wf[WF_OBJ_ALL_STOP] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + +static FILE *orgfile, *modfile, *ipsfile, *destfile; +static int ndiffs = 0, totaldiffs = 0, address = -1, rle_value = NO_RLE, filepos = 0; +static const char *destfname = NULL; + + +static unsigned char +read_byte (FILE *file) +{ + int byte; // this must be an int! + + byte = fgetc (file); + if (byte == EOF) + { + fprintf (stderr, "ERROR: Unexpected end of file\n"); + exit (1); + } + return byte; +} + + +static void +remove_destfile (void) +{ + if (destfname) + { + printf ("Removing: %s\n", destfname); + fclose (destfile); // necessary under DOS/Win9x for DJGPP port + remove (destfname); + destfname = NULL; + } +} + + +// based on IPS v1.0 for UNIX by madman +int +ips_apply (const char *mod, const char *ipsname) +{ + unsigned char byte, byte2, byte3; + char modname[FILENAME_MAX], magic[6]; + unsigned int offset, length, i; + + strcpy (modname, mod); + ucon64_file_handler (modname, NULL, 0); + fcopy (mod, 0, fsizeof (mod), modname, "wb"); // no copy if one file + + if ((modfile = fopen (modname, "r+b")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], modname); + exit (1); + } + if ((ipsfile = fopen (ipsname, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], ipsname); + exit (1); + } + + destfname = modname; + destfile = modfile; + register_func (remove_destfile); + + fgets (magic, 6, ipsfile); + if (strcmp (magic, "PATCH") != 0) + { // do at least one check for validity + fprintf (stderr, "ERROR: %s is not a valid IPS file\n", ipsname); + exit (1); + } + + printf ("Applying IPS patch...\n"); + while (!feof (ipsfile)) + { + byte = read_byte (ipsfile); + byte2 = read_byte (ipsfile); + byte3 = read_byte (ipsfile); + offset = (byte << 16) + (byte2 << 8) + byte3; + if (offset == 0x454f46) // numerical representation of ASCII "EOF" + break; + fseek (modfile, offset, SEEK_SET); + + byte = read_byte (ipsfile); + byte2 = read_byte (ipsfile); + length = (byte << 8) + byte2; + if (length == 0) + { // code for RLE compressed block + byte = read_byte (ipsfile); + byte2 = read_byte (ipsfile); + length = (byte << 8) + byte2; + byte = read_byte (ipsfile); +#ifdef DEBUG_IPS + printf ("[%02x] <= %02x (* %d)\n", offset, byte, length); +#endif + for (i = 0; i < length; i++) + fputc (byte, modfile); + } + else + { // non compressed + for (i = 0; i < length; i++) + { + byte = read_byte (ipsfile); +#ifdef DEBUG_IPS + printf ("[%02x] <= %02x\n", offset + i, byte); +#endif + fputc (byte, modfile); + } + } + } + fclose (modfile); // commit changes before calling truncate2() + + byte = fgetc (ipsfile); // don't use read_byte() here; + if (!feof (ipsfile)) // this part is optional + { // IPS2 stuff + byte2 = read_byte (ipsfile); + byte3 = read_byte (ipsfile); + length = (byte << 16) + (byte2 << 8) + byte3; + truncate2 (modname, length); + printf ("File truncated to %.4f MBit\n", length / (float) MBIT); + } + + printf ("Patching complete\n\n"); + printf (ucon64_msg[WROTE], modname); + printf ("\n" + "NOTE: Sometimes you have to add/strip a 512 bytes header when you patch a ROM\n" + " This means you must modify for example a SNES ROM with -swc or -stp or\n" + " the patch will not work\n"); + + unregister_func (remove_destfile); // unregister _after_ possible padding + fclose (ipsfile); + + return 0; +} + + +static void +write_address (int new_address) +{ + address = new_address; + if (address < 16777216) + /* + 16777216 = 2^24. The original code checked for 16711680 (2^24 - 64K), but + that is an artificial limit. + */ + { + fputc (address >> 16, ipsfile); + fputc (address >> 8, ipsfile); + fputc (address, ipsfile); + } + else + { + fprintf (stderr, "ERROR: IPS doesn't support addresses greater than 16777215\n" + " Consider using another patch format\n"); + exit (1); // will call remove_destfile() (indirectly) + } +} + + +static void +write_block (int ndiffs, unsigned char *buffer, int rle_value) +{ + if (rle_value == NO_RLE) + { + fputc (ndiffs >> 8, ipsfile); + fputc (ndiffs, ipsfile); + fwrite (buffer, 1, ndiffs, ipsfile); + } + else + { + fputc (0, ipsfile); + fputc (0, ipsfile); + fputc (ndiffs >> 8, ipsfile); + fputc (ndiffs, ipsfile); + fputc (rle_value, ipsfile); +#ifdef DEBUG_IPS + printf ("RLE "); +#endif + } +#ifdef DEBUG_IPS + printf ("length: %d\n", ndiffs); +#endif +} + + +static void +flush_diffs (unsigned char *buffer) +{ + if (ndiffs) + { + totaldiffs += ndiffs; + write_block (ndiffs, buffer, rle_value); + ndiffs = 0; + rle_value = NO_RLE; + address = -1; + } +} + + +static int +rle_end (int value, unsigned char *buffer) +{ + if (rle_value != NO_RLE && rle_value != value) + { + flush_diffs (buffer); + filepos--; + fseek (orgfile, filepos, SEEK_SET); + fseek (modfile, filepos, SEEK_SET); +#ifdef DEBUG_IPS + printf ("->normal\n"); +#endif + return 1; + } + return 0; +} + + +static int +check_for_rle (unsigned char byte, unsigned char *buf) +{ + int use_rle, i, retval = 0; + + if (rle_value == NO_RLE) + { + /* + Start with a new block (and stop with the current one) only if there + are at least RLE_RESTART_THRESHOLD + 1 equal bytes in a row. Restarting + for RLE has some overhead: possibly 5 bytes for the interrupted current + block plus 7 bytes for the RLE block. So, values smaller than 11 for + RLE_RESTART_THRESHOLD only make the IPS file larger than if no RLE + compression would be used. + */ + if (ndiffs > RLE_RESTART_THRESHOLD) + { + use_rle = 1; + for (i = ndiffs - RLE_RESTART_THRESHOLD; i < ndiffs; i++) + if (buf[i] != byte) + { + use_rle = 0; + break; + } + if (use_rle) + // we are not using RLE, but we should => start a new block + { + ndiffs -= RLE_RESTART_THRESHOLD; + filepos -= RLE_RESTART_THRESHOLD; + fseek (orgfile, filepos, SEEK_SET); + fseek (modfile, filepos, SEEK_SET); + + flush_diffs (buf); + write_address (filepos); + retval = 1; +#ifdef DEBUG_IPS + printf ("restart (%x)\n", address); +#endif + } + } + /* + Use RLE only if the last RLE_START_THRESHOLD + 1 (or more) bytes were + the same. Values smaller than 7 for RLE_START_THRESHOLD will make the + IPS file larger than if no RLE would be used (for this block). + normal block: + i address high byte + i + 1 address medium byte + i + 2 address low byte + i + 3 length high byte + i + 4 length low byte + i + 5 new byte + ... + RLE block: + i address high byte + i + 1 address medium byte + i + 2 address low byte + i + 3 0 + i + 4 0 + i + 5 length high byte + i + 6 length low byte + i + 7 new byte + The value 7 for RLE_START_THRESHOLD (instead of 2) is to compensate for + normal blocks that immediately follow the RLE block. + */ + else if (ndiffs > RLE_START_THRESHOLD) + { + use_rle = 1; + for (i = 0; i < ndiffs; i++) + if (buf[i] != byte) + { + use_rle = 0; + break; + } + if (use_rle) + { + rle_value = byte; +#ifdef DEBUG_IPS + printf ("->RLE\n"); +#endif + } + } + } + return retval; +} + + +int +ips_create (const char *orgname, const char *modname) +{ + int i, orgfilesize, modfilesize; + char ipsname[FILENAME_MAX]; + unsigned char byte, byte2, buf[BUFSIZE]; + + if ((orgfile = fopen (orgname, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], orgname); + exit (1); + } + if ((modfile = fopen (modname, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], modname); + exit (1); + } + strcpy (ipsname, modname); + set_suffix (ipsname, ".ips"); + ucon64_file_handler (ipsname, NULL, 0); + if ((ipsfile = fopen (ipsname, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], ipsname); + exit (1); + } + + destfname = ipsname; + destfile = ipsfile; + register_func (remove_destfile); + + orgfilesize = fsizeof (orgname); + modfilesize = fsizeof (modname); + + fprintf (ipsfile, "PATCH"); + +next_byte: + while (1) + { + byte = fgetc (orgfile); + byte2 = fgetc (modfile); + filepos++; + if (feof (modfile)) + break; + + if (modfilesize > 0x454f46 && filepos == 0x454f46) + /* + We must avoid writing 0x454f46 (4542278) as offset, because it has a + special meaning. Offset 0x454f46 is interpreted as EOF marker. It is + a numerical representation of the ASCII string "EOF". + We solve the problem by writing 2 patch bytes for offsets 0x454f45 + and 0x454f46 if at least one of those bytes differs for orgfile and + modfile. + */ + { + int byte3 = fgetc (orgfile); + unsigned char byte4 = read_byte (modfile); + filepos++; + + if (byte3 == EOF || byte != byte2 || byte3 != byte4) + { + if (address < 0 || address + ndiffs != 0x454f46 - 1 || + ndiffs > BUFSIZE - 2) + { + flush_diffs (buf); // commit any pending data + write_address (0x454f46 - 1); + } + // write 2 patch bytes (for offsets 0x454f45 and 0x454f46) + buf[ndiffs++] = byte2; + buf[ndiffs++] = byte4; + +#ifdef DEBUG_IPS + printf ("[%02x] => %02x\n" + "[%02x] => %02x\n", + filepos - 2, buf[ndiffs - 2], filepos - 1, buf[ndiffs - 1]); +#endif + } + continue; + } + + if (byte != byte2 || feof (orgfile)) + { + if (rle_end (byte2, buf)) + continue; + + if (address < 0) + { + flush_diffs (buf); // commit previous block + write_address (filepos - 1); + } + + buf[ndiffs++] = byte2; +#ifdef DEBUG_IPS + printf ("[%02x] => %02x%s\n", filepos - 1, byte2, + rle_value != NO_RLE ? " *" : ""); +#endif + check_for_rle (byte2, buf); + } + else if (address >= 0) // byte == byte2 && !feof (orgfile) + { + int n, n2, n_compare; + unsigned char bridge[BRIDGE_LEN + 1]; + + bridge[0] = byte; + buf[ndiffs] = byte2; + n = fread (bridge + 1, 1, BRIDGE_LEN, orgfile); + n2 = fread (&buf[ndiffs + 1], 1, MIN (BRIDGE_LEN, BUFSIZE - ndiffs), modfile); + n_compare = 1 + MIN (n, n2); + + for (i = 0; i < n_compare; i++) + if (buf[ndiffs + i] != bridge[i]) + { + for (n = 0; n < i; n++) + { + if (rle_end (buf[ndiffs], buf)) + goto next_byte; // yep, ugly +#ifdef DEBUG_IPS + printf ("[%02x] => %02x b%s\n", filepos - 1, + buf[ndiffs], rle_value != NO_RLE ? " *" : ""); +#endif + ndiffs++; + if (check_for_rle (buf[ndiffs - 1], buf)) + goto next_byte; // even uglier + filepos++; + } + filepos--; +// byte = bridge[n]; +// byte2 = buf[ndiffs]; + break; + } + + fseek (orgfile, filepos, SEEK_SET); + fseek (modfile, filepos, SEEK_SET); + + if (i == n_compare) // next few bytes are equal (between the files) + address = -1; + } + if (ndiffs == BUFSIZE) + flush_diffs (buf); + } + + flush_diffs (buf); + fprintf (ipsfile, "EOF"); + if (modfilesize < orgfilesize) + write_address (modfilesize); + + unregister_func (remove_destfile); + fclose (orgfile); + fclose (modfile); + fclose (ipsfile); + + if (totaldiffs == 0) + { + printf ("%s and %s are identical\n" + "Removing: %s\n", orgname, modname, ipsname); + remove (ipsname); + return -1; + } + + printf (ucon64_msg[WROTE], ipsname); + return 0; +} diff --git a/ucon64/2.0/src/patch/ips.h b/ucon64/2.0/src/patch/ips.h new file mode 100644 index 0000000..187b217 --- /dev/null +++ b/ucon64/2.0/src/patch/ips.h @@ -0,0 +1,29 @@ +/* +ips.h - IPS support for uCON64 + +Copyright (c) ???? - ???? madman +Copyright (c) 1999 - 2001 NoisyB +Copyright (c) 2002 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef IPS_H +#define IPS_H +extern const st_getopt2_t ips_usage[]; + +extern int ips_apply (const char *destname, const char *ipsname); +extern int ips_create (const char *orgname, const char *modname); +#endif diff --git a/ucon64/2.0/src/patch/pal4u.c b/ucon64/2.0/src/patch/pal4u.c new file mode 100644 index 0000000..87a82d4 --- /dev/null +++ b/ucon64/2.0/src/patch/pal4u.c @@ -0,0 +1,43 @@ +/* +pal4u.c - pal4u patcher support for uCON64 + +Copyright (c) 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "pal4u.h" + + +const st_getopt2_t pal4u_usage[] = + { +#if 0 + "TODO: " OPTION_LONG_S "p4u apply PAL4U/P4U patch; " OPTION_LONG_S "rom" OPTARG_S "RAW_IMAGE" + "TODO: " OPTION_LONG_S "mkp4u create PAL4U/P4U patch; " OPTION_LONG_S "rom" OPTARG_S "RAW_IMAGE", +#endif + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; diff --git a/ucon64/2.0/src/patch/pal4u.h b/ucon64/2.0/src/patch/pal4u.h new file mode 100644 index 0000000..c4191bc --- /dev/null +++ b/ucon64/2.0/src/patch/pal4u.h @@ -0,0 +1,24 @@ +/* +pal4u.h - XPS patcher support for uCON64 + +Copyright (c) 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef PAL4U_H +#define PAL4U_H +extern const st_getopt2_t pal4u_usage[]; +#endif diff --git a/ucon64/2.0/src/patch/patch.h b/ucon64/2.0/src/patch/patch.h new file mode 100644 index 0000000..9b912bb --- /dev/null +++ b/ucon64/2.0/src/patch/patch.h @@ -0,0 +1,32 @@ +/* +patch.h - single header file for all patch functions + +Copyright (c) 2003 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef PATCH_H +#define PATCH_H + +#include "aps.h" +#include "bsl.h" +#include "gg.h" +#include "ips.h" +#include "pal4u.h" +#include "ppf.h" +#include "xps.h" + +#endif diff --git a/ucon64/2.0/src/patch/ppf.c b/ucon64/2.0/src/patch/ppf.c new file mode 100644 index 0000000..3f049f0 --- /dev/null +++ b/ucon64/2.0/src/patch/ppf.c @@ -0,0 +1,502 @@ +/* +ppf.c - Playstation Patch File support for uCON64 + +Copyright (c) ???? - ???? Icarus/Paradox +Copyright (c) 2001 NoisyB +Copyright (c) 2002 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "misc/bswap.h" +#include "misc/misc.h" +#include "misc/file.h" +#include "misc/string.h" // MEMMEM2_CASE +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_misc.h" +#include "ppf.h" + + +#define MAX_ID_SIZE 3072 +#define DIFF_FSIZE +/* + I (dbjh) couldn't tell from the specification below if it is required that + the original file and the modified file have the same size. By defining + DIFF_FSIZE, PPF as I understand it becomes quite a generic patch file + format. It can be used to patch any file up to 4 GB. +*/ + + +const st_getopt2_t ppf_usage[] = + { + { + "ppf", 0, 0, UCON64_PPF, + NULL, "apply PPF PATCH to IMAGE (PPF<=v2.0); ROM should be an IMAGE", + &ucon64_wf[WF_OBJ_ALL_STOP] + }, + { + "mkppf", 1, 0, UCON64_MKPPF, + "ORG_IMG", "create PPF patch; ROM should be the modified IMAGE", + &ucon64_wf[WF_OBJ_ALL_STOP] + }, + { + "nppf", 1, 0, UCON64_NPPF, + "DESC", "change PPF single line DESCRIPTION", + NULL + }, + { + "idppf", 1, 0, UCON64_IDPPF, + "FILE_ID.DIZ", "change FILE_ID.DIZ of PPF PATCH (PPF v2.0)", + NULL + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + +/* + +.-----------------------------------------------------------------. +| PLAYSTATION PATCH FILE VERSION 2.0 FILE-STRUCTURE FOR DEVELOPERS| +'-----------------------------------------------------------------' + +1. The PPF 2.0 Header: + +@START_PPF20HEADER +.----------+--------+---------------------------------------------. +| POSITION | SIZE | E X P L A N A T I O N | ++----------|--------|---------------------------------------------+ +| 00-04 | 05 | PPF-magic: "PPF20" | ++----------|--------|---------------------------------------------+ +| 05 | 01 | Encoding method: | +| | | - If $00 then it is a PPF 1.0 patch | +| | | - If $01 then it is a PPF 2.0 patch | ++----------|--------|---------------------------------------------+ +| 06-55 | 50 | Patch description | ++----------|--------|---------------------------------------------+ +| 56-59 | 04 | Size of the file (e.g. CDRWin binfile) this | +| | | patch was made of. Used for identification | ++----------|--------|---------------------------------------------+ +| 60-1083 | 1024 | This is a binary block of 1024 byte taken | +| | | from position $9320 of the file (e.g. CDRWin| +| | | binfile) this patch was made of. Used for | +| | | identification. | ++----------|--------|---------------------------------------------+ +| 1084-X | XX | The patch itself. See below for structure! | +'----------+--------+---------------------------------------------' +@END_PPF20HEADER - total headersize = 1084 bytes. + + +2. The PPF 2.0 patch itself (encoding method #1) + +@START_PPF20PATCH +FORMAT : xxxx,y,zzzz + + xxxx = 4 byte file offset. + + y = Number of bytes that will be changed. + + zzzz = New data to be written ('y' number of bytes). + +Example +~~~~~~~ + +Starting from file offset 0x0015F9D0 replace 3 bytes with 01,02,03 +D0 F9 15 00 03 01 02 03 + +Be careful! Watch the endian format! If you own an Amiga and want +to do a PPF2-patcher for Amiga don't forget to swap the endian-format +of the offset to avoid seek errors! + +@END_PPF20PATCH + + +3. The PPF 2.0 fileid area + +@START_FILEID + +The fileid area is used to store additional patch information of +the PPF 2.0 file. I implemented this following the AMIGA standard +of adding a fileid to e.g. .txt files. You can add a FILE_ID to a +PPF 2.0 patch by using the tool 'PPFdiz.exe' or "PPF-O-MATIC2" +included in this package. You don't have to add a FILE_ID to your +PPF 2.0 patch. It's only for your pleasure! :) + +For developers: a file_id area begins with @BEGIN_FILE_ID.DIZ and +ends with @END_FILE_ID.DIZ (Amiga BBS standard). +Between @BEGIN_FILE_ID.DIZ and @END_FILE_ID.DIZ you will find +the fileid and followed after @END_FILE_ID.DIZ you will find an +integer (4 bytes long) with the length of the FILE_ID.DIZ! + +A FILE_ID.DIZ file cannot be greater than 3072 bytes. + +If you do a PPF 2.0 applier be sure to check for an existing FILE_ID +AREA, because it is located after the patch data! + +@END_FILEID +*/ + + +// based on source code of ApplyPPF v2.0 for Linux/Unix by Icarus/Paradox +int +ppf_apply (const char *mod, const char *ppfname) +{ + FILE *modfile, *ppffile; + char desc[50 + 1], diz[MAX_ID_SIZE + 1], buffer[1024], ppfblock[1024], + modname[FILENAME_MAX]; + int x, method, dizlen = 0, modlen, ppfsize, bytes_to_skip = 0, n_changes; + unsigned int pos; + + strcpy (modname, mod); + ucon64_file_handler (modname, NULL, 0); + fcopy (mod, 0, fsizeof (mod), modname, "wb"); // no copy if one file + + if ((modfile = fopen (modname, "r+b")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], modname); + exit (1); + } + if ((ppffile = fopen (ppfname, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], ppfname); + exit (1); + } + + // Is it a PPF File? + fread (buffer, 3, 1, ppffile); + if (strncmp ("PPF", buffer, 3)) + { + fprintf (stderr, "ERROR: %s is not a valid PPF file\n", ppfname); + exit (1); + } + + // What encoding method? PPF 1.0 or PPF 2.0? + fseek (ppffile, 5, SEEK_SET); + method = fgetc (ppffile); + if (method != 0 && method != 1) + { + fprintf (stderr, "ERROR: Unknown encoding method! Check for updates\n"); + exit (1); + } + + ppfsize = fsizeof (ppfname); + + // Show PPF information + fseek (ppffile, 6, SEEK_SET); // Read description line + fread (desc, 50, 1, ppffile); + desc[50] = 0; // terminate string + printf ("\n" // print a newline between + "Filename : %s\n", ppfname); // backup message and PPF info + printf ("Encoding method : %d (PPF %d.0)\n", method, method + 1); + printf ("Description : %s\n", desc); + + if (method == 0) // PPF 1.0 + { + printf ("FILE_ID.DIZ : No\n\n"); + x = 56; // file pointer is at right position (56) + } + else // method == 1 // PPF 2.0 + { + fseek (ppffile, ppfsize - 8, SEEK_SET); + fread (buffer, 4, 1, ppffile); + + // Is there a file id? + if (strncmp (".DIZ", buffer, 4)) + printf ("FILE_ID.DIZ : No\n\n"); + else + { + printf ("FILE_ID.DIZ : Yes, showing...\n"); + fread (&dizlen, 4, 1, ppffile); +#ifdef WORDS_BIGENDIAN + dizlen = bswap_32 (dizlen); // FILE_ID.DIZ size is in little-endian format +#endif + fseek (ppffile, ppfsize - dizlen - (16 + 4), SEEK_SET); + bytes_to_skip = dizlen + 18 + 16 + 4; // +4 for FILE_ID.DIZ size integer + if (dizlen > MAX_ID_SIZE) + dizlen = MAX_ID_SIZE; // do this after setting bytes_to_skip! + fread (diz, dizlen, 1, ppffile); + diz[dizlen] = 0; // terminate string + puts (diz); + } + + // Do the file size check + fseek (ppffile, 56, SEEK_SET); + fread (&x, 4, 1, ppffile); +#ifdef WORDS_BIGENDIAN + x = bswap_32 (x); // file size is stored in little-endian format +#endif + modlen = fsizeof (modname); + if (x != modlen) + { + fprintf (stderr, "ERROR: The size of %s is not %d bytes\n", modname, x); + exit (1); + } + + // Do the binary block check + fseek (ppffile, 60, SEEK_SET); + fread (ppfblock, 1024, 1, ppffile); + fseek (modfile, 0x9320, SEEK_SET); + memset (buffer, 0, 1024); // one little hack that makes PPF + fread (buffer, 1024, 1, modfile); // suitable for files < 38688 bytes + if (memcmp (ppfblock, buffer, 1024)) + { + fprintf (stderr, "ERROR: This patch does not belong to this image\n"); + exit (1); + } + + fseek (ppffile, 1084, SEEK_SET); + x = 1084; + } + + // Patch the image + printf ("Patching...\n"); + for (; x < ppfsize - bytes_to_skip; x += 4 + 1 + n_changes) + { + fread (&pos, 4, 1, ppffile); // Get position for modfile +#ifdef WORDS_BIGENDIAN + pos = bswap_32 (pos); +#endif + n_changes = fgetc (ppffile); // How many bytes do we have to write? + fread (buffer, n_changes, 1, ppffile); // And this is what we have to write + fseek (modfile, pos, SEEK_SET); // Go to the right position in the modfile + fwrite (buffer, n_changes, 1, modfile); // Write n_changes bytes to that pos + } + + printf ("Done\n"); + fclose (ppffile); + fclose (modfile); + + printf (ucon64_msg[WROTE], modname); + return 0; +} + + +// based on sourcecode of MakePPF v2.0 Linux/Unix by Icarus/Paradox +int +ppf_create (const char *orgname, const char *modname) +{ + FILE *orgfile, *modfile, *ppffile; + char ppfname[FILENAME_MAX], buffer[MAX_ID_SIZE], obuf[512], mbuf[512]; +#if 0 + char *fidname = "FILE_ID.DIZ"; +#endif + int x, osize, msize, blocksize, n_changes, total_changes = 0; + unsigned int seekpos = 0, pos; + + osize = fsizeof (orgname); + msize = fsizeof (modname); +#ifndef DIFF_FSIZE + if (osize != msize) + { + fprintf (stderr, "ERROR: File sizes do not match\n"); + return -1; + } +#endif + + if ((orgfile = fopen (orgname, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], orgname); + exit (1); + } + if ((modfile = fopen (modname, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], modname); + exit (1); + } + strcpy (ppfname, modname); + set_suffix (ppfname, ".ppf"); + ucon64_file_handler (ppfname, NULL, 0); + if ((ppffile = fopen (ppfname, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], ppfname); + exit (1); + } + + // creating PPF 2.0 header + fwrite ("PPF20", 5, 1, ppffile); // magic + fputc (1, ppffile); // encoding method + memset (buffer, ' ', 50); + fwrite (buffer, 50, 1, ppffile); // description line +#ifdef WORDS_BIGENDIAN + x = bswap_32 (osize); + fwrite (&x, 4, 1, ppffile); +#else + fwrite (&osize, 4, 1, ppffile); // orgfile size +#endif + fseek (orgfile, 0x9320, SEEK_SET); + memset (buffer, 0, 1024); // one little hack that makes PPF + fread (buffer, 1024, 1, orgfile); // suitable for files < 38688 bytes + fwrite (buffer, 1024, 1, ppffile); // 1024 byte block + + printf ("Writing patch data, please wait...\n"); + // finding changes + fseek (orgfile, 0, SEEK_SET); + fseek (modfile, 0, SEEK_SET); + while ((blocksize = fread (obuf, 1, 255, orgfile))) + { + blocksize = fread (mbuf, 1, blocksize, modfile); +#ifdef DIFF_FSIZE + if (blocksize == 0) + break; +#endif + pos = seekpos; + x = 0; + while (x != blocksize) + { + if (obuf[x] != mbuf[x]) + { + pos = seekpos + x; + n_changes = 0; + do + { + buffer[n_changes] = mbuf[x]; + n_changes++; + x++; + } + while (x != blocksize && obuf[x] != mbuf[x]); + total_changes += n_changes; +#ifdef WORDS_BIGENDIAN + pos = bswap_32 (pos); +#endif + fwrite (&pos, 4, 1, ppffile); + fputc (n_changes, ppffile); + fwrite (buffer, n_changes, 1, ppffile); + } + else + x++; + } + seekpos += blocksize; + } + +#ifdef DIFF_FSIZE + if (msize > osize) + { + pos = seekpos; + while ((blocksize = fread (buffer, 1, 255, modfile))) + { + total_changes += blocksize; +#ifdef WORDS_BIGENDIAN + x = bswap_32 (pos); + fwrite (&x, 4, 1, ppffile); +#else + fwrite (&pos, 4, 1, ppffile); +#endif + fputc (blocksize, ppffile); + fwrite (buffer, blocksize, 1, ppffile); + pos += blocksize; + } + } + else if (msize < osize) + printf ("WARNING: %s is smaller than %s\n" + " PPF can't store information about that fact\n", + modname, orgname); +#endif + + fclose (orgfile); + fclose (modfile); + + if (total_changes == 0) + { + printf ("%s and %s are identical\n" + "Removing: %s\n", orgname, modname, ppfname); + fclose (ppffile); + remove (ppfname); + return -1; + } + +#if 0 + if (fidname) + { + int fsize = fsizeof (fidname); + if (fsize > MAX_ID_SIZE) + fsize = MAX_ID_SIZE; // File id only up to 3072 bytes! + printf ("Adding FILE_ID.DIZ (%s)...\n", fidname); + ucon64_fread (buffer, 0, fsize, fidname); + fwrite ("@BEGIN_FILE_ID.DIZ", 18, 1, ppffile); + fwrite (buffer, fsize, 1, ppffile); + fwrite ("@END_FILE_ID.DIZ", 16, 1, ppffile); +#ifdef WORDS_BIGENDIAN + fsize = bswap_32 (fsize); // Write file size in little-endian format +#endif + fwrite (&fsize, 4, 1, ppffile); + } +#endif + fclose (ppffile); + + printf (ucon64_msg[WROTE], ppfname); + return 0; +} + + +int +ppf_set_desc (const char *ppf, const char *description) +{ + char desc[50], ppfname[FILENAME_MAX]; + + strcpy (ppfname, ppf); + memset (desc, ' ', 50); + strncpy (desc, description, strlen (description)); + ucon64_file_handler (ppfname, NULL, 0); + fcopy (ppf, 0, fsizeof (ppf), ppfname, "wb"); // no copy if one file + ucon64_fwrite (desc, 6, 50, ppfname, "r+b"); + + printf (ucon64_msg[WROTE], ppfname); + return 0; +} + + +int +ppf_set_fid (const char *ppf, const char *fidname) +{ + int fidsize, ppfsize, pos; + char ppfname[FILENAME_MAX], + fidbuf[MAX_ID_SIZE + 34 + 1] = "@BEGIN_FILE_ID.DIZ"; // +1 for string terminator + + strcpy (ppfname, ppf); + ucon64_file_handler (ppfname, NULL, 0); + fcopy (ppf, 0, fsizeof (ppf), ppfname, "wb"); // no copy if one file + + printf ("Adding FILE_ID.DIZ (%s)...\n", fidname); + fidsize = ucon64_fread (fidbuf + 18, 0, MAX_ID_SIZE, fidname); + memcpy (fidbuf + 18 + fidsize, "@END_FILE_ID.DIZ", 16); + + ppfsize = fsizeof (ppfname); + pos = ucon64_find (ppfname, 0, ppfsize, "@BEGIN_FILE_ID.DIZ", 18, + MEMCMP2_CASE | UCON64_FIND_QUIET); + if (pos == -1) + pos = ppfsize; + truncate (ppfname, pos); + + ucon64_fwrite (fidbuf, pos, fidsize + 18 + 16, ppfname, "r+b"); + pos += fidsize + 18 + 16; +#ifdef WORDS_BIGENDIAN + fidsize = bswap_32 (fidsize); // Write file size in little-endian format +#endif + ucon64_fwrite (&fidsize, pos, 4, ppfname, "r+b"); + + printf (ucon64_msg[WROTE], ppfname); + return 0; +} diff --git a/ucon64/2.0/src/patch/ppf.h b/ucon64/2.0/src/patch/ppf.h new file mode 100644 index 0000000..1e8d813 --- /dev/null +++ b/ucon64/2.0/src/patch/ppf.h @@ -0,0 +1,29 @@ +/* +ppf.h - Playstation Patch File support for uCON64 + +Copyright (c) ???? - ???? Icarus/Paradox +Copyright (c) 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef PPF_H +#define PPF_H +extern const st_getopt2_t ppf_usage[]; +extern int ppf_apply (const char *modname, const char *ppfname); +extern int ppf_create (const char *orgname, const char *modname); +extern int ppf_set_desc (const char *ppfname, const char *description); +extern int ppf_set_fid (const char *ppfname, const char *fidname); +#endif diff --git a/ucon64/2.0/src/patch/xps.c b/ucon64/2.0/src/patch/xps.c new file mode 100644 index 0000000..632682b --- /dev/null +++ b/ucon64/2.0/src/patch/xps.c @@ -0,0 +1,43 @@ +/* +xps.c - XPS patcher support for uCON64 + +Copyright (c) 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "xps.h" + + +const st_getopt2_t xps_usage[] = + { +#if 0 + "TODO: " OPTIONS_LONG_S "xps apply XPS patch; " OPTION_LONG_S "rom" OPTARG_S "RAW_IMAGE" + "TODO: " OPTIONS_LONG_S "mkxps create XPS patch; " OPTION_LONG_S "rom" OPTARG_S "RAW_IMAGE " OPTION_LONG_S "file" OPTARG_S "CHANGED_IMAGE", +#endif + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; diff --git a/ucon64/2.0/src/patch/xps.h b/ucon64/2.0/src/patch/xps.h new file mode 100644 index 0000000..2337733 --- /dev/null +++ b/ucon64/2.0/src/patch/xps.h @@ -0,0 +1,24 @@ +/* +xps.h - XPS patcher support for uCON64 + +Copyright (c) 2001 NoisyB + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef XPS_H +#define XPS_H +extern const st_getopt2_t xps_usage[]; +#endif diff --git a/ucon64/2.0/src/snescopy.txt b/ucon64/2.0/src/snescopy.txt new file mode 100644 index 0000000..4393336 --- /dev/null +++ b/ucon64/2.0/src/snescopy.txt @@ -0,0 +1,66 @@ +# These codes have been put here for your convenience and as a reference so +# that you don't have to look at the source code. Read the FAQ for the format. +# +# Of the following two groups of three only one should be enabled at one time +# 02 01 01 70 02 01 01 70 d0 : 01: 02: ea ea : 0: 8f 9f : cf df +# Kirby's Dream Course, Lufia II - Rise of the Sinistrals +# 02 01 01 70 02 01 01 70 f0 : 01: 02: 80 : 0: 8f 9f : cf df +# 02 01 01 02 02 01 01 02 f0 : 01: 02: 80 : 0: 8f 9f : 30 31 32 33 : cf df : 30 31 32 33 +# +# 02 01 01 70 02 01 01 70 d0 : 01: 02: 80 : 0: 8f 9f : cf df +# Mega Man X +# 02 01 01 70 02 01 01 70 f0 : 01: 02: ea ea : 0: 8f 9f : cf df +# 02 01 01 02 02 01 01 02 f0 : 01: 02: ea ea : 0: 8f 9f : 30 31 32 33 : cf df : 30 31 32 33 +# +# Uniracers/Unirally +# 8f 01 01 77 e2 01 af 01 01 77 c9 01 f0:1:2:80:0 +# Donkey Kong Country (8f, 30, cf, 30) +# Note that this code must be searched for before the less specific uCON code. +# 02 02 02 02 02 02 60 02 d0 : 01: 02: ea ea : 0: 8f 9f : 57 59 : 60 68 : 30 31 32 33 : cf df : 57 59 : 30 31 32 33 +# 02 01 01 02 02 01 01 02 d0 : 01: 02: 80 : 0: 8f 9f : 30 31 32 33 : cf df : 30 31 32 33 +# Mario no Super Picross +# 02 01 01 b0 cf 01 01 b1 d0 : 01: 02: ea ea : 0: 8f af +# 02 01 01 02 af 01 01 02 c9 01 01 d0:01:02:80: 0: 8f 9f : 30 31 32 33 : 30 31 32 33 +# Super Metroid +# a9 00 00 a2 fe 1f df 00 00 70 d0:01:02:ea ea: 0 +# Breath of Fire II (bf, 30, df, 31) +# 02 01 01 02 02 01 01 02 f0 : 01: 02: 80 : 0: af bf : 30 31 32 33 : cf df : 30 31 32 33 +# +# Mega Man X +# 02 01 80 00 02 01 80 40 f0 : 01: 02: 80 : 0: af bf : cf df +# Demon's Crest (af, 80, cf) / Breath of Fire II (bf, c0, df) +# 02 01 ff 02 02 01 ff 40 f0 : 01: 02: 80 : 0: af bf : 80 c0 : cf df +# +# Killer Instinct +# 5c 7f d0 83 18 fb 78 c2 30 : 01: 02: ea ea ea ea ea ea ea ea ea : -8 +# Diddy's Kong Quest (the three below) +# 4b 4f 4e 47 00 f8 f7 : 01: 02: f8 : 0 +# 26 38 e9 48 12 c9 af 71 f0 : 01: 02: 80 : 0 +# a0 5c 2f 77 32 e9 c7 04 f0 : 01: 02: 80 : 0 +# +# BS The Legend of Zelda Remix +# 22 08 5c 10 b0 28 : 01: 02: ea ea ea ea ea ea : -5 +# BS The Legend of Zelda Remix (the next two enable music) +# da e2 30 c9 01 f0 18 c9 02 : 03: 04: 09 f0 18 c9 07 : -4 +# 29 ff 00 c9 07 00 90 16 : 01: 02: 00 : -3 +# +# Kirby's Dream Course (the two below) +# ca 10 f8 38 ef 1a 80 81 8d : 01: 02: 9c : 0 +# 81 ca 10 f8 cf 39 80 87 f0 : 01: 02: 80 : 0 +# +# Earthbound (internal codes, the three below) +# 84 26 ad 39 b5 d0 1a : 01: 02: ea ea : -1 +# 10 f8 38 ef ef ff c1 : 01: 02: ea a9 00 00 : -3 +# 10 f8 38 ef f2 fd c3 f0 : 01: 02: ea a9 00 00 80 : -4 +# Earthbound (alternative code, should be used alone; do not combine with -f!) +# 22 1c a1 c0 22 56 87 c0 : 01: 02: 51 : -6 +# +# Tetris Attack (the two below) +# c2 30 ad fc 1f c9 50 44 d0 : 01: 02: 4c d1 80 : -6 +# 8f 01 01 70 af 01 01 70 c9 01 01 d0 : 01: 02: 80 : 0 +# +# Dixie Kong's Double Trouble E. U version looks like it already has been "patched" +# a9 c3 80 dd ff ff f0 6c : 01: 02: f0 cc ff ff 80 7d : -5 +# +# Front Mission - Gun Hazard +# d0 f4 ab cf ae ff 00 d0 01: 03: 04: 00: 0 diff --git a/ucon64/2.0/src/snesntsc.txt b/ucon64/2.0/src/snesntsc.txt new file mode 100644 index 0000000..b829d08 --- /dev/null +++ b/ucon64/2.0/src/snesntsc.txt @@ -0,0 +1,34 @@ +# Currently none of these codes are active, because they are all built in +# uCON64. They have been put here for your convenience and as a reference +# so that you don't have to look at the source code. See the document +# "NTSC-PAL notes.txt" for more detailed information. Read the FAQ for the +# format. +# +# 3f 21 02 10 f0: 01: 02: 80: 0: 29 89 +# ad 3f 21 29 10 d0: 01: 02: ea ea: 0 +# ad 3f 21 89 10 d0: 01: 02: ea ea: 0 +# The next code could be the alternative for the previous one. Leave it +# disabled until we find a game that needs it. +# ad 3f 21 89 10 d0: 01: 02: 80: 0 +# 3f 21 02 10 00 f0: 01: 02: 80: 0: 29 89 +# 3f 21 02 10 00 d0: 01: 02: ea ea: 0: 29 89 +# 3f 21 89 10 c2 01 d0: 01: 02: ea ea: 0 +# 3f 21 89 10 c2 01 f0: 01: 02: 80: 0 +# 3f 21 02 10 c9 10 f0: 01: 02: 80: 0: 29 89 +# ad 3f 21 29 10 c9 00 f0: 01: 02: ea ea: 0 +# ad 3f 21 29 10 c9 00 d0: 01: 02: 80: 0 +# ad 3f 21 29 10 c9 10 d0: 01: 02: ea ea: 0 +# 3f 21 29 10 cf 01 01 80 f0: 01: 02: 80: 0 +# ad 3f 21 8d 01 01 29 10 8d: 01: 02: 00: -1 +# 3f 21 00 02 10 f0: 01: 02: 80: 0: 29 89 +# af 3f 21 00 02 10 d0: 01: 02: ea ea: 0: 29 89 +# af 3f 21 00 02 10 00 f0: 01: 02: 80: 0: 29 89 +# af 3f 21 00 29 01 c9 01 f0: 01: 02: 80: 0 +# af 3f 21 00 29 10 80 2d 00 1b: 01: 02: 00: -4 +# 3f 21 00 89 10 c2 01 f0: 01: 02: 80: 0 +# af 3f 21 00 01 01 29 10 00 d0: 01: 02: ea ea: 0 +# 3f 21 c2 01 29 10 00 f0: 01: 02: 80: 0 +# 3f 21 c2 01 29 10 00 d0: 01: 02: ea ea: 0 +# af 3f 21 ea 89 10 00 d0: 01: 02: a9 00 00: -7 +# a2 18 01 bd 27 20 89 10 00 d0 01: 03: 04: ea ea: -1 +# 29 10 00 a2 00 00 c9 10 00 d0: 01: 02: 80: 0 diff --git a/ucon64/2.0/src/snespal.txt b/ucon64/2.0/src/snespal.txt new file mode 100644 index 0000000..cfc311e --- /dev/null +++ b/ucon64/2.0/src/snespal.txt @@ -0,0 +1,14 @@ +# Currently none of these codes are active, because they are all built in +# uCON64. They have been put here for your convenience and as a reference +# so that you don't have to look at the source code. See the document +# "NTSC-PAL notes.txt" for more detailed information. Read the FAQ for the +# format. +# +# ad 3f 21 89 10 d0: 01: 02: 80: 0 +# ad 3f 21 29 10 00 d0: 01: 02: 80: 0 +# ad 3f 21 89 10 00 d0: 01: 02: a9 10 00: -6 +# ad 3f 21 29 10 cf bd ff 01 f0: 01: 02: 80: 0 +# af 3f 21 00 29 10 d0: 01: 02: 80: 0 +# af 3f 21 00 29 10 00 d0: 01: 02: ea ea: 0 +# af 3f 21 00 29 01 c9 01 f0: 01: 02: 80: 0 +# a2 18 01 bd 27 20 89 10 00 f0 01: 03: 04: ea ea: -1 diff --git a/ucon64/2.0/src/snesslow.txt b/ucon64/2.0/src/snesslow.txt new file mode 100644 index 0000000..ab9fdf4 --- /dev/null +++ b/ucon64/2.0/src/snesslow.txt @@ -0,0 +1,14 @@ +# Currently none of these codes are active, because they are all built in +# uCON64. They have been put here for your convenience and as a reference +# so that you don't have to look at the source code. Read the FAQ for the +# format. +# +# 04 0d 42: 03: 04: 9c: -2: 8c 8d 8e 8f +# 01 0d 42: 03: 04: 00: -2 +# a9 01 85 0d: 03: 04: 00: -2 +# a2 01 86 0d: 03: 04: 00: -2 +# a0 01 84 0d: 03: 04: 00: -2 +# +# 04 01 04 0d 42: 03: 04: 00: -3: a9 a2: 8d 8e +# a9 01 00 8d 0d 42: 03: 04: 00: -4 +# a9 01 8f 0d 42 00: 03: 04: 00: -4 diff --git a/ucon64/2.0/src/ucon64.c b/ucon64/2.0/src/ucon64.c new file mode 100644 index 0000000..a53a436 --- /dev/null +++ b/ucon64/2.0/src/ucon64.c @@ -0,0 +1,1437 @@ +/* +uCON64 - a tool to modify video game ROMs and to transfer ROMs to the +different backup units/emulators that exist. It is based on the old uCON but +with completely new source. It aims to support all cartridge consoles and +handhelds like N64, JAG, SNES, NG, GENESIS, GB, LYNX, PCE, SMS, GG, NES and +their backup units + +Copyright (c) 1999 - 2004 NoisyB +Copyright (c) 2001 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + First I want to thank SiGMA SEVEN! who was my mentor and taught me how to + write programs in C. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_DIRENT_H +#include +#endif +#include +#include +#ifdef _WIN32 +#include +#endif + +#ifdef DEBUG +#ifdef __GNUC__ +#warning DEBUG active +#else +#pragma message ("DEBUG active") +#endif +#endif +#ifdef USE_PARALLEL +#include "misc/parallel.h" +#endif +#include "misc/bswap.h" +#include "misc/misc.h" +#include "misc/property.h" +#include "misc/chksum.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "misc/string.h" +#include "ucon64.h" +#include "ucon64_misc.h" +#include "ucon64_opts.h" +#include "ucon64_dat.h" +#include "console/console.h" +#include "patch/patch.h" +#include "backup/backup.h" + + +static void ucon64_exit (void); +static int ucon64_execute_options (void); +static void ucon64_rom_nfo (const st_rominfo_t *rominfo); +static st_rominfo_t *ucon64_probe (st_rominfo_t *rominfo); +static int ucon64_rom_handling (void); +static int ucon64_process_rom (char *fname); + + +st_ucon64_t ucon64; // containes ptr to image, dat and rominfo + +static const char *ucon64_title = "uCON64 " UCON64_VERSION_S " " CURRENT_OS_S " 1999-2005"; + +#ifdef AMIGA +unsigned long __stacksize = 102400; // doesn't work on PPC? is StormC specific? +//unsigned long __stack = 102400; // for SAS/C, DICE, GCC etc.? +char vers[] = "$VER: uCON64 "UCON64_VERSION_S" "CURRENT_OS_S" ("__DATE__") ("__TIME__")"; +#endif + +typedef struct +{ + int val; // (st_getopt2_t->val) +// const + char *optarg; // option argument + int console; // the console (st_getopt2_t->object) + int flags; // workflow flags (st_getopt2_t->object) +} st_args_t; + +static st_args_t arg[UCON64_MAX_ARGS]; + +static st_getopt2_t options[UCON64_MAX_ARGS]; +static const st_getopt2_t lf[] = + { + {NULL, 0, 0, 0, NULL, "", NULL}, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + *option[] = + { + ucon64_options_usage, + ucon64_options_without_usage, + lf, + ucon64_padding_usage, + lf, + ucon64_dat_usage, + lf, + ucon64_patching_usage, + bsl_usage, + ips_usage, + aps_usage, + pal4u_usage, + ppf_usage, + xps_usage, + gg_usage, + lf, +#ifdef USE_DISCMAGE + libdm_usage, + lf, +#endif + dc_usage, + lf, + psx_usage, +#ifdef USE_PARALLEL + dex_usage, +#endif + lf, + gba_usage, +#if defined USE_PARALLEL || defined USE_USB + // f2a_usage has to come before fal_usage in case only USE_USB is defined + // (no support for parallel port) + f2a_usage, +#ifdef USE_PARALLEL + fal_usage, +#endif +#endif // USE_PARALLEL || USE_USB + lf, + n64_usage, +#ifdef USE_PARALLEL + doctor64_usage, + doctor64jr_usage, +#ifdef USE_LIBCD64 + cd64_usage, +#endif + dex_usage, +#endif // USE_PARALLEL + lf, + snes_usage, +#ifdef USE_PARALLEL + swc_usage, + gd_usage, + fig_usage, + sflash_usage, + // mgd_usage, +#endif + lf, + neogeo_usage, + lf, + genesis_usage, +#ifdef USE_PARALLEL + smd_usage, + mdpro_usage, + mcd_usage, + cmc_usage, + // mgd_usage, +#endif + lf, + gameboy_usage, +#ifdef USE_PARALLEL + gbx_usage, + mccl_usage, +#endif + lf, + lynx_usage, +#ifdef USE_PARALLEL + lynxit_usage, +#endif + lf, + pcengine_usage, +#ifdef USE_PARALLEL + msg_usage, + pcepro_usage, + // mgd_usage, +#endif + lf, + nes_usage, +#ifdef USE_PARALLEL + smc_usage, +#endif + lf, + sms_usage, +#ifdef USE_PARALLEL + smsggpro_usage, +#endif + lf, + swan_usage, + lf, + jaguar_usage, + lf, + ngp_usage, +#ifdef USE_PARALLEL + pl_usage, +#endif + lf, + atari_usage, + NULL + }; + + +static st_rominfo_t * +ucon64_rom_flush (st_rominfo_t * rominfo) +{ + if (rominfo) + memset (rominfo, 0, sizeof (st_rominfo_t)); + + ucon64.rominfo = NULL; + ucon64.crc32 = ucon64.fcrc32 = 0; // yes, this belongs here + rominfo->data_size = UCON64_UNKNOWN; + + return rominfo; +} + + +#ifdef DEBUG +void +ucon64_runtime_debug_output (st_getopt2_t *p) +{ + printf ("{\"%s\", %d, 0, %d, \"%s\", \"%s\", %d}, // console: %d workflow: %d\n", + p->name, + p->has_arg, + p->val, + p->arg_name, + p->help ? "usage" : p->help, // i (nb) mean it +// p->help, + 0, + p->object ? ((st_ucon64_obj_t *) p->object)->console : 0, + p->object ? ((st_ucon64_obj_t *) p->object)->flags : 0); +} + + +static void +ucon64_runtime_debug (void) +{ + int x = 0, y = 0, c = 0; + (void) x; + (void) y; + (void) c; + +#if 0 + // how many options (incl. dupes) do we have? + for (x = y = 0; options[x].name || options[x].help; x++) + if (options[x].name) + y++; + printf ("DEBUG: Total options (with dupes): %d\n", y); + printf ("DEBUG: UCON64_MAX_ARGS == %d, %s\n", UCON64_MAX_ARGS, + (y < UCON64_MAX_ARGS ? "good" : "\nERROR: too small; must be larger than options")); +#endif + +#if 1 + // list all options as a single st_getopt2_t array + for (x = 0; options[x].name || options[x].help; x++) + if (options[x].name) + ucon64_runtime_debug_output ((st_getopt2_t *) &options[x]); +#endif + +#if 0 + // how many consoles does uCON64 support? + for (x = y = 0; options[x].name || options[x].help; x++) + if (options[x].name && options[x].object) + if (options[x].val == ((st_ucon64_obj_t *) options[x].object)->console) + ucon64_runtime_debug_output ((st_getopt2_t *) &options[x]); +#endif + +#if 0 + // find options without an object (allowed) + for (x = 0; options[x].name || options[x].help; x++) + if (options[x].name && !options[x].object) + ucon64_runtime_debug_output ((st_getopt2_t *) &options[x]); +#endif + +#if 0 + // find options without a console (allowed) + for (x = 0; options[x].name || options[x].help; x++) + if (options[x].name && !((st_ucon64_obj_t *) options[x].object)->console) + ucon64_runtime_debug_output ((st_getopt2_t *) &options[x]); +#endif + +#if 0 + // find options without a workflow (allowed) + for (x = 0; options[x].name || options[x].help; x++) + if (options[x].name && !((st_ucon64_obj_t *) options[x].object)->flags) + ucon64_runtime_debug_output ((st_getopt2_t *) &options[x]); +#endif + +#if 0 + // find options without a val (NOT allowed) + for (x = 0; options[x].name || options[x].help; x++) + if (options[x].name && !options[x].val) + ucon64_runtime_debug_output ((st_getopt2_t *) &options[x]); +#endif + +#if 0 + // find options with has_arg but without arg_name AND/OR usage + // hidden options without arg_name AND usage are allowed + for (x = 0; options[x].name || options[x].help; x++) + if (options[x].name && + ((!options[x].has_arg && options[x].arg_name) || + (options[x].has_arg && !options[x].arg_name) || + !options[x].help)) + ucon64_runtime_debug_output ((st_getopt2_t *) &options[x]); +#endif + +#if 0 + // find dupe (NOT a problem) options that have different values for val, + // flag, and/or object (NOT allowed) + // getopt1() will always use the 1st option in the array + // (st_getopt2_t *)->arg_name and (st_getopt2_t *)->help can be as + // different as you like + for (x = 0; options[x].name || options[x].help; x++) + if (options[x].name) + for (y = 0; options[y].name || options[y].help; y++) + if (options[y].name && x != y) // IS option + if (!strcmp (options[y].name, options[x].name)) + if (options[y].has_arg != options[x].has_arg || // (NOT allowed) + options[y].flag != options[x].flag || // (NOT allowed) + options[y].val != options[x].val || // (NOT allowed) +// options[y].arg_name != options[x].arg_name || // (allowed) +// options[y].help != options[x].help || // (allowed) + ((st_ucon64_obj_t *) options[y].object)->console != ((st_ucon64_obj_t *) options[x].object)->console // (NOT allowed) + ((st_ucon64_obj_t *) options[x].object)->flags != ((st_ucon64_obj_t *) options[x].object)->flags) // (NOT allowed) + { + fputs ("ERROR: different dupe options found\n ", stdout); + ucon64_runtime_debug_output ((st_getopt2_t *) &options[x]); + fputs (" ", stdout); + ucon64_runtime_debug_output ((st_getopt2_t *) &options[y]); + fputs ("\n\n", stdout); + } +#endif + puts ("DEBUG: Sanity check finished"); + fflush (stdout); +} +#endif // DEBUG + + +void +ucon64_exit (void) +{ +#ifdef USE_DISCMAGE + if (ucon64.discmage_enabled) + if (ucon64.image) + dm_close ((dm_image_t *) ucon64.image); +#endif + + handle_registered_funcs (); + fflush (stdout); +} + + +int +main (int argc, char **argv) +{ + int x = 0, y = 0, rom_index = 0, c = 0; +#if (FILENAME_MAX < MAXBUFSIZE) + static char buf[MAXBUFSIZE]; +#else + static char buf[FILENAME_MAX]; +#endif + struct stat fstate; + struct option long_options[UCON64_MAX_ARGS]; + + printf ("%s\n" + "Uses code from various people. See 'developers.html' for more!\n" + "This may be freely redistributed under the terms of the GNU Public License\n\n", + ucon64_title); + + if (atexit (ucon64_exit) == -1) + { + fputs ("ERROR: Could not register function with atexit()\n", stderr); + exit (1); + } + + // flush st_ucon64_t + memset (&ucon64, 0, sizeof (st_ucon64_t)); + + ucon64.rom = + ucon64.file = + ucon64.mapr = + ucon64.comment = ""; + + ucon64.parport_needed = 0; + + ucon64.flags = WF_DEFAULT; + + ucon64.fname_arch[0] = 0; + + ucon64.argc = argc; + ucon64.argv = argv; // must be set prior to calling + // ucon64_load_discmage() (for DOS) + + // convert (st_getopt2_t **) to (st_getopt2_t *) + memset (&options, 0, sizeof (st_getopt2_t) * UCON64_MAX_ARGS); + for (c = x = 0; option[x]; x++) + for (y = 0; option[x][y].name || option[x][y].help; y++) + if (c < UCON64_MAX_ARGS) + { + memcpy (&options[c], &option[x][y], sizeof (st_getopt2_t)); + c++; + } + ucon64.options = options; + +#ifdef DEBUG + ucon64_runtime_debug (); // check (st_getopt2_t *) options consistency +#endif + +#ifdef __unix__ + // We need to modify the umask, because the configfile is made while we are + // still running in root mode. Maybe 0 is even better (in case root did + // `chmod +s'). + umask (002); +#endif + ucon64_configfile (); + +#ifdef USE_ANSI_COLOR + // ANSI colors? + ucon64.ansi_color = get_property_int (ucon64.configfile, "ansi_color"); + // the conditional call to ansi_init() has to be done *after* the check for + // the switch -ncol +#endif + + // parallel port? +#ifdef USE_PPDEV + get_property (ucon64.configfile, "parport_dev", ucon64.parport_dev, "/dev/parport0"); +#elif defined AMIGA + get_property (ucon64.configfile, "parport_dev", ucon64.parport_dev, "parallel.device"); +#endif + // use -1 (UCON64_UNKNOWN) to force probing if the config file doesn't contain + // a parport line + sscanf (get_property (ucon64.configfile, "parport", buf, "-1"), "%x", &ucon64.parport); + + // make backups? + ucon64.backup = get_property_int (ucon64.configfile, "backups"); + + // $HOME/.ucon64/ ? + get_property_fname (ucon64.configfile, "ucon64_configdir", ucon64.configdir, ""); + + // DAT file handling + ucon64.dat_enabled = 0; + get_property_fname (ucon64.configfile, "ucon64_datdir", ucon64.datdir, ""); + + // we use ucon64.datdir as path to the dats + if (!access (ucon64.datdir, + // !W_OK doesn't mean that files can't be written to dir for Win32 exe's +#if !defined __CYGWIN__ && !defined _WIN32 + W_OK | +#endif + R_OK | X_OK)) + if (!stat (ucon64.datdir, &fstate)) + if (S_ISDIR (fstate.st_mode)) + ucon64.dat_enabled = 1; + + if (!ucon64.dat_enabled) + if (!access (ucon64.configdir, +#if !defined __CYGWIN__ && !defined _WIN32 + W_OK | +#endif + R_OK | X_OK)) + if (!stat (ucon64.configdir, &fstate)) + if (S_ISDIR (fstate.st_mode)) + { +// fprintf (stderr, "Please move your DAT files from %s to %s\n\n", ucon64.configdir, ucon64.datdir); + strcpy (ucon64.datdir, ucon64.configdir); // use .ucon64/ instead of .ucon64/dat/ + ucon64.dat_enabled = 1; + } + + if (ucon64.dat_enabled) + ucon64_dat_indexer (); // update cache (index) files if necessary + +#ifdef USE_DISCMAGE + // load libdiscmage + ucon64.discmage_enabled = ucon64_load_discmage (); +#endif + + if (argc < 2) + { + ucon64_usage (argc, argv); + return 0; + } + + + // turn st_getopt2_t into struct option + getopt2_long_only (long_options, options, UCON64_MAX_ARGS); + + // getopt() is utilized to make uCON64 handle/parse cmdlines in a sane + // and expected way + x = optind = 0; + memset (&arg, 0, sizeof (st_args_t) * UCON64_MAX_ARGS); + while ((c = getopt_long_only (argc, argv, "", long_options, NULL)) != -1) + { + if (c == '?') // getopt() returns 0x3f ('?') when an unknown option was given + { + fprintf (stderr, + "Try '%s " OPTION_LONG_S "help' for more information.\n", + argv[0]); + exit (1); + } + + if (x < UCON64_MAX_ARGS) + { + const st_ucon64_obj_t *p = (st_ucon64_obj_t *) getopt2_get_index_by_val (options, c)->object; + + arg[x].console = UCON64_UNKNOWN; // default + + if (p) + { + arg[x].flags = p->flags; + if (p->console) + arg[x].console = p->console; + } + + arg[x].val = c; + arg[x++].optarg = (optarg ? optarg : NULL); + } + else + // this shouldn't happen + exit (1); + } + +#ifdef DEBUG + for (x = 0; arg[x].val; x++) + printf ("%d %s %d %d\n\n", + arg[x].val, + arg[x].optarg ? arg[x].optarg : "(null)", + arg[x].flags, + arg[x].console); +#endif + + rom_index = optind; // save index of first file + if (rom_index == argc) + ucon64_execute_options(); + else + for (; rom_index < argc; rom_index++) + { + int result = 0; + char buf2[FILENAME_MAX]; +#ifndef _WIN32 + struct dirent *ep; + DIR *dp; +#else + char search_pattern[FILENAME_MAX]; + WIN32_FIND_DATA find_data; + HANDLE dp; +#endif + + realpath2 (argv[rom_index], buf); + if (stat (buf, &fstate) != -1) + { + if (S_ISREG (fstate.st_mode)) + result = ucon64_process_rom (buf); + else if (S_ISDIR (fstate.st_mode)) // a dir? + { + char *p; +#if defined __MSDOS__ || defined _WIN32 || defined __CYGWIN__ + /* + Note that this code doesn't make much sense for Cygwin, + because at least the version I use (1.3.6, dbjh) doesn't + support current directories for drives. + */ + c = toupper (buf[0]); + if (buf[strlen (buf) - 1] == FILE_SEPARATOR || + (c >= 'A' && c <= 'Z' && buf[1] == ':' && buf[2] == 0)) +#else + if (buf[strlen (buf) - 1] == FILE_SEPARATOR) +#endif + p = ""; + else + p = FILE_SEPARATOR_S; + +#ifndef _WIN32 + if ((dp = opendir (buf))) + { + while ((ep = readdir (dp))) + { + sprintf (buf2, "%s%s%s", buf, p, ep->d_name); + if (stat (buf2, &fstate) != -1) + if (S_ISREG (fstate.st_mode)) + { + result = ucon64_process_rom (buf2); + if (result == 1) + break; + } + } + closedir (dp); + } +#else + sprintf (search_pattern, "%s%s*", buf, p); + if ((dp = FindFirstFile (search_pattern, &find_data)) != INVALID_HANDLE_VALUE) + { + do + { + sprintf (buf2, "%s%s%s", buf, p, find_data.cFileName); + if (stat (buf2, &fstate) != -1) + if (S_ISREG (fstate.st_mode)) + { + result = ucon64_process_rom (buf2); + if (result == 1) + break; + } + } + while (FindNextFile (dp, &find_data)); + FindClose (dp); + } +#endif + } + else + result = ucon64_process_rom (buf); + } + else + result = ucon64_process_rom (buf); + + if (result == 1) + break; + } + + return 0; +} + + +int +ucon64_process_rom (char *fname) +{ +#ifdef USE_ZLIB + int n_entries = unzip_get_number_entries (fname); + if (n_entries != -1) // it's a zip file + { + for (unzip_current_file_nr = 0; unzip_current_file_nr < n_entries; + unzip_current_file_nr++) + { + ucon64_fname_arch (fname); + /* + There seems to be no other way to detect directories in ZIP files + than by looking at the file name. Paths in ZIP files should contain + forward slashes. ucon64_fname_arch() changes forward slashes into + backslashes (FILE_SEPARATORs) when uCON64 is compiled with Visual + C++ or MinGW so that basename2() always produces a correct base + name. So, if the entry in the ZIP file is a directory + ucon64.fname_arch will be an empty string. + */ + if (ucon64.fname_arch[0] == 0) + continue; + + ucon64.rom = fname; + + ucon64_execute_options(); + + if (ucon64.flags & WF_STOP) + break; + } + unzip_current_file_nr = 0; + ucon64.fname_arch[0] = 0; + + if (ucon64.flags & WF_STOP) + return 1; + } + else +#endif + { + ucon64.rom = fname; + + ucon64_execute_options(); + if (ucon64.flags & WF_STOP) + return 1; + } + + return 0; +} + + +int +ucon64_execute_options (void) +/* + Execute all options for a single file. + Please, if you experience problems then try your luck with the flags + in ucon64_misc.c/ucon64_wf[] before changing things here or in + ucon64_rom_handling() +*/ +{ + int c = 0, result = 0, x = 0, opts = 0; + static int first_call = 1; // first call to this function + + ucon64.dat = NULL; +#ifdef USE_DISCMAGE + ucon64.image = NULL; +#endif + ucon64.rominfo = NULL; + + ucon64.battery = + ucon64.bs_dump = + ucon64.buheader_len = + ucon64.console = + ucon64.controller = + ucon64.controller2 = + ucon64.do_not_calc_crc = + ucon64.id = + ucon64.interleaved = + ucon64.mirror = + ucon64.part_size = + ucon64.region = + ucon64.snes_header_base = + ucon64.snes_hirom = + ucon64.split = + ucon64.tv_standard = + ucon64.use_dump_info = + ucon64.vram = UCON64_UNKNOWN; + + ucon64.file_size = + ucon64.crc32 = + ucon64.fcrc32 = + ucon64.io_mode = 0; + + // switches + for (x = 0; arg[x].val; x++) + { + if (arg[x].console != UCON64_UNKNOWN) + ucon64.console = arg[x].console; + if (arg[x].flags) + ucon64.flags = arg[x].flags; + if (arg[x].val) + ucon64.option = arg[x].val; + ucon64.optarg = arg[x].optarg; + +// if (ucon64.flags & WF_SWITCH) + ucon64_switches (&ucon64); + } +#ifdef USE_ANSI_COLOR + if (ucon64.ansi_color && first_call) + ucon64.ansi_color = ansi_init (); +#endif + +#ifdef USE_PARALLEL + /* + The copier options need root privileges for parport_open() + We can't use ucon64.flags & WF_PAR to detect whether a (parallel port) + copier option has been specified, because another switch might've been + specified after -port. + */ + if (ucon64.parport_needed == 1) + ucon64.parport = parport_open (ucon64.parport); +#endif // USE_PARALLEL +#if defined __unix__ && !defined __MSDOS__ + /* + We can drop privileges after we have set up parallel port access. We cannot + drop privileges if the user wants to communicate with the USB version of the + F2A. + SECURITY WARNING: We stay in root mode if the user specified an F2A option! + We could of course drop privileges which requires the user to run uCON64 as + root (not setuid root), but we want to be user friendly. Besides, doing + things as root is bad anyway (from a security viewpoint). + */ + if (first_call && ucon64.parport_needed != 2 +#ifdef USE_USB + && !ucon64.usbport +#endif + ) + drop_privileges (); +#endif // __unix__ && !__MSDOS__ + first_call = 0; + + for (x = 0; arg[x].val; x++) + if (!(arg[x].flags & WF_SWITCH)) + { + if (ucon64.console == UCON64_UNKNOWN) + ucon64.console = arg[x].console; + ucon64.flags = arg[x].flags; + ucon64.option = arg[x].val; + ucon64.optarg = arg[x].optarg; + + opts++; + + // WF_NO_SPLIT, WF_INIT, WF_PROBE, CRC32, DATabase and WF_NFO + result = ucon64_rom_handling (); + + if (result == -1) // no rom, but WF_NO_ROM + return -1; + + if (ucon64_options (&ucon64) == -1) + { + const st_getopt2_t *p = getopt2_get_index_by_val (options, c); + const char *opt = p ? p->name : NULL; + + fprintf (stderr, "ERROR: %s%s encountered a problem\n", + opt ? (!opt[1] ? OPTION_S : OPTION_LONG_S) : "", + opt ? opt : "uCON64"); + +// if (p) +// getopt2_usage (p); + + fputs (" Is the option you used available for the current console system?\n" + " Please report bugs to noisyb@gmx.net or ucon64-announce@lists.sf.net\n\n", + stderr); + + return -1; + } + +#if 0 + // WF_NFO_AFTER?! + if (!result && (ucon64.flags & WF_NFO_AFTER) && ucon64.quiet < 1) + ucon64_rom_handling (); +#endif + + /* + "stop" options: + - -multi (and -xfalmulti) takes more than one file as argument, but + should be executed only once. + - stop after sending one ROM to a copier ("multizip") + - stop after applying a patch so that the patch file won't be + interpreted as ROM + */ + if (ucon64.flags & WF_STOP) + break; + } + + if (!opts) // no options => just display ROM info + { + ucon64.flags = WF_DEFAULT; + // WF_NO_SPLIT WF_INIT, WF_PROBE, CRC32, DATabase and WF_NFO + if (ucon64_rom_handling () == -1) + return -1; // no rom, but WF_NO_ROM + } + + fflush (stdout); + + return 0; +} + + +int +ucon64_rom_handling (void) +{ + int no_rom = 0; + static st_rominfo_t rominfo; + struct stat fstate; + + ucon64_rom_flush (&rominfo); + + // a ROM (file)? + if (!ucon64.rom) + no_rom = 1; + else if (!ucon64.rom[0]) + no_rom = 1; + else if (access (ucon64.rom, F_OK | R_OK) == -1 && (!(ucon64.flags & WF_NO_ROM))) + { + fprintf (stderr, "ERROR: Could not open %s\n", ucon64.rom); + no_rom = 1; + } + else if (stat (ucon64.rom, &fstate) == -1) + no_rom = 1; + else if (S_ISREG (fstate.st_mode) != TRUE) + no_rom = 1; +#if 0 + // printing the no_rom error message for files of 0 bytes only confuses people + else if (!fstate.st_size) + no_rom = 1; +#endif + + if (no_rom) + { + if (!(ucon64.flags & WF_NO_ROM)) + { + fputs ("ERROR: This option requires a file argument (ROM/image/SRAM file/directory)\n", stderr); + return -1; + } + return 0; + } + + // The next statement is important and should be executed as soon as + // possible (and sensible) in this function + ucon64.file_size = fsizeof (ucon64.rom); + // We have to do this here, because we don't know the file size until now + if (ucon64.buheader_len > ucon64.file_size) + { + fprintf (stderr, + "ERROR: A backup unit header length was specified that is larger than the file\n" + " size (%d > %d)\n", ucon64.buheader_len, ucon64.file_size); + return -1; + } + + if (!(ucon64.flags & WF_INIT)) + return 0; + + // "walk through" _init() + if (ucon64.flags & WF_PROBE) + { + if (ucon64.rominfo) + { + // Restore any overrides from st_ucon64_t + // We have to do this *before* calling ucon64_probe(), *not* afterwards + if (UCON64_ISSET (ucon64.buheader_len)) + rominfo.buheader_len = ucon64.buheader_len; + + if (UCON64_ISSET (ucon64.interleaved)) + rominfo.interleaved = ucon64.interleaved; + +// ucon64.rominfo = (st_rominfo_t *) &rominfo; + } + ucon64.rominfo = ucon64_probe (&rominfo); // determines console type + +#ifdef USE_DISCMAGE + // check for disc image only if ucon64_probe() failed or --disc was used + if (ucon64.discmage_enabled) +// if (!ucon64.rominfo || ucon64.force_disc) + if (ucon64.force_disc) + ucon64.image = dm_reopen (ucon64.rom, 0, (dm_image_t *) ucon64.image); +#endif + } + // end of WF_PROBE + + // Does the option allow split ROMs? + if (ucon64.flags & WF_NO_SPLIT) + /* + Test for split files only if the console type knows about split files at + all. However we only know the console type after probing. + */ + if (ucon64.console == UCON64_NES || ucon64.console == UCON64_SNES || + ucon64.console == UCON64_GEN || ucon64.console == UCON64_NG) + if ((UCON64_ISSET (ucon64.split)) ? ucon64.split : ucon64_testsplit (ucon64.rom)) + { + fprintf (stderr, "ERROR: %s seems to be split. You have to join it first\n", + basename2 (ucon64.rom)); + return -1; + } + + + /* + CRC32 + + Calculating the CRC32 checksum for the ROM data of a UNIF file (NES) + shouldn't be done with ucon64_fcrc32(). nes_init() uses crc32(). + The CRC32 checksum is used to search in the DAT files, but at the time + of this writing (Februari the 7th 2003) all DAT files contain checksums + of files in only one format. This matters for SNES and Genesis ROMs in + interleaved format and Nintendo 64 ROMs in non-interleaved format. The + corresponding initialization functions calculate the CRC32 checksum of + the data in the format of which the checksum is stored in the DAT + files. For these "problematic" files, their "real" checksum is stored + in ucon64.fcrc32. + */ + if (ucon64.crc32 == 0) + if (!ucon64.force_disc) // NOT for disc images + if (!(ucon64.flags & WF_NO_CRC32) && ucon64.file_size <= MAXROMSIZE) + ucon64_chksum (NULL, NULL, &ucon64.crc32, ucon64.rom, ucon64.rominfo ? ucon64.rominfo->buheader_len : 0); + + + // DATabase + ucon64.dat = NULL; + if (ucon64.crc32 != 0 && ucon64.dat_enabled) + { + ucon64.dat = ucon64_dat_search (ucon64.crc32, NULL); + if (ucon64.dat) + { + // detected file size must match DAT file size + int size = ucon64.rominfo ? + UCON64_ISSET (ucon64.rominfo->data_size) ? + ucon64.rominfo->data_size : + ucon64.file_size - ucon64.rominfo->buheader_len : + ucon64.file_size; + if ((int) (((st_ucon64_dat_t *) ucon64.dat)->fsize) != size) + ucon64.dat = NULL; + } + + if (ucon64.dat) + switch (ucon64.console) + { + case UCON64_SNES: + case UCON64_GEN: + case UCON64_GB: + case UCON64_GBA: + case UCON64_N64: + // These ROMs have internal headers with name, country, maker, etc. + break; + + default: + // Use ucon64.dat instead of ucon64.dat_enabled in case the index + // file could not be created/opened -> no segmentation fault + if (ucon64.dat && ucon64.rominfo) + { + if (!ucon64.rominfo->name[0]) + strcpy (ucon64.rominfo->name, NULL_TO_EMPTY (((st_ucon64_dat_t *) ucon64.dat)->name)); + else if (ucon64.console == UCON64_NES) + { // override the three-character FDS or FAM name + int t = nes_get_file_type (); + if (t == FDS || t == FAM) + strcpy (ucon64.rominfo->name, NULL_TO_EMPTY (((st_ucon64_dat_t *) ucon64.dat)->name)); + } + + if (!ucon64.rominfo->country) + ucon64.rominfo->country = NULL_TO_EMPTY (((st_ucon64_dat_t *) ucon64.dat)->country); + } + break; + } + } + + // display info + if ((ucon64.flags & WF_NFO || ucon64.flags & WF_NFO_AFTER) && ucon64.quiet < 1) + ucon64_nfo (); + + return 0; +} + + +st_rominfo_t * +ucon64_probe (st_rominfo_t * rominfo) +{ + typedef struct + { + int console; + int (*init) (st_rominfo_t *); + uint32_t flags; + } st_probe_t; + +// auto recognition +#define AUTO (1) + + int x = 0; + st_probe_t probe[] = + { + /* + The order of the init functions is important. snes_init() must be + called before nes_init(), but after gameboy_init() and sms_init(). + sms_init() must be called before snes_init(), but after genesis_init(). + There may be more dependencies, so don't change the order unless you + can verify it won't break anything. + */ + {UCON64_GBA, gba_init, AUTO}, + {UCON64_N64, n64_init, AUTO}, + {UCON64_GEN, genesis_init, AUTO}, + {UCON64_LYNX, lynx_init, AUTO}, + {UCON64_GB, gameboy_init, AUTO}, + {UCON64_SMS, sms_init, AUTO}, + {UCON64_SNES, snes_init, AUTO}, + {UCON64_NES, nes_init, AUTO}, + {UCON64_NGP, ngp_init, AUTO}, + {UCON64_SWAN, swan_init, AUTO}, + {UCON64_JAG, jaguar_init, AUTO}, + {UCON64_PCE, pcengine_init, AUTO}, + {UCON64_NG, neogeo_init, 0}, + {UCON64_SWAN, swan_init, 0}, + {UCON64_DC, dc_init, 0}, + {UCON64_PSX, psx_init, 0}, +#if 0 + {UCON64_GC, NULL, 0}, + {UCON64_GP32, NULL, 0}, + {UCON64_COLECO, NULL, 0}, + {UCON64_INTELLI, NULL, 0}, + {UCON64_S16, NULL, 0}, + {UCON64_ATA, NULL, 0}, + {UCON64_VEC, NULL, 0}, + {UCON64_VBOY, NULL, 0}, +#endif + {UCON64_UNKNOWN, unknown_init, 0}, + {0, NULL, 0} + }; + + if (ucon64.console != UCON64_UNKNOWN) // force recognition option was used + { + for (x = 0; probe[x].console != 0; x++) + if (probe[x].console == ucon64.console) + { + ucon64_rom_flush (rominfo); + + probe[x].init (rominfo); + + return rominfo; + } + } + else if (ucon64.file_size <= MAXROMSIZE) // give auto_recognition a try + { + for (x = 0; probe[x].console != 0; x++) + if (probe[x].flags & AUTO) + { + ucon64_rom_flush (rominfo); + + if (!probe[x].init (rominfo)) + { + ucon64.console = probe[x].console; + return rominfo; + } + } + } + + return NULL; +} + + +int +ucon64_nfo (void) +{ + puts (ucon64.rom); + if (ucon64.fname_arch[0]) + printf (" (%s)\n", ucon64.fname_arch); + fputc ('\n', stdout); +#ifdef USE_DISCMAGE + if (ucon64.console == UCON64_UNKNOWN && !ucon64.image) +#else + if (ucon64.console == UCON64_UNKNOWN) +#endif + fprintf (stderr, "%s\n", ucon64_msg[CONSOLE_ERROR]); + + if (ucon64.rominfo && ucon64.console != UCON64_UNKNOWN && !ucon64.force_disc) + ucon64_rom_nfo (ucon64.rominfo); + +#ifdef USE_DISCMAGE + if (ucon64.discmage_enabled) + if (ucon64.image) + { + dm_nfo ((dm_image_t *) ucon64.image, ucon64.quiet < 0 ? 1 : 0, +#ifdef USE_ANSI_COLOR + ucon64.ansi_color ? 1 : +#endif + 0); + fputc ('\n', stdout); + + return 0; // no crc calc. for disc images and therefore no dat entry either + } +#endif + // Use ucon64.fcrc32 for SNES, Genesis & SMS interleaved/N64 non-interleaved + if (ucon64.fcrc32 && ucon64.crc32) + printf ("Search checksum (CRC32): 0x%08x\n" + "Data checksum (CRC32): 0x%08x\n", ucon64.crc32, ucon64.fcrc32); + else if (ucon64.fcrc32 || ucon64.crc32) + printf ("Checksum (CRC32): 0x%08x\n", ucon64.fcrc32 ? ucon64.fcrc32 : ucon64.crc32); + + // The check for the size of the file is made, so that uCON64 won't display a + // (nonsense) DAT info line when dumping a ROM (file doesn't exist, so + // ucon64.file_size is 0). + if (ucon64.file_size > 0 && ucon64.dat_enabled) + if (ucon64.dat) + ucon64_dat_nfo ((st_ucon64_dat_t *) ucon64.dat, 1); + + fputc ('\n', stdout); + + return 0; +} + + +static inline char * +to_func (char *s, int len, int (*func) (int)) +{ + char *p = s; + + for (; len > 0; p++, len--) + *p = func (*p); + + return s; +} + + +static inline int +toprint (int c) +{ + if (isprint (c)) + return c; + + // characters that also work with printf() +#ifdef USE_ANSI_COLOR + if (c == '\x1b') + return ucon64.ansi_color ? c : '.'; +#endif + + return strchr ("\t\n\r", c) ? c : '.'; +} + + +void +ucon64_rom_nfo (const st_rominfo_t *rominfo) +{ + unsigned int padded = ucon64_testpad (ucon64.rom), + intro = ((ucon64.file_size - rominfo->buheader_len) > MBIT) ? + ((ucon64.file_size - rominfo->buheader_len) % MBIT) : 0; + int x, split = (UCON64_ISSET (ucon64.split)) ? ucon64.split : + ucon64_testsplit (ucon64.rom); + char buf[MAXBUFSIZE]; + + // backup unit header + if (rominfo->buheader && rominfo->buheader_len && rominfo->buheader_len != UNKNOWN_HEADER_LEN) + { + dumper (stdout, rominfo->buheader, rominfo->buheader_len, rominfo->buheader_start, DUMPER_HEX); + fputc ('\n', stdout); + } + else + if (rominfo->buheader_len && ucon64.quiet < 0) + { + ucon64_dump (stdout, ucon64.rom, rominfo->buheader_start, rominfo->buheader_len, DUMPER_HEX); + fputc ('\n', stdout); + } + + // backup unit type? + if (rominfo->copier_usage != NULL) + { + puts (rominfo->copier_usage); + fputc ('\n', stdout); + } + + // ROM header + if (rominfo->header && rominfo->header_len) + { + dumper (stdout, rominfo->header, rominfo->header_len, + rominfo->header_start + rominfo->buheader_len, DUMPER_HEX); + fputc ('\n', stdout); + } + + // console type + if (rominfo->console_usage != NULL) + puts (rominfo->console_usage); + + // name, maker, country and size + strcpy (buf, NULL_TO_EMPTY (rominfo->name)); + x = UCON64_ISSET (rominfo->data_size) ? + rominfo->data_size : + ucon64.file_size - rominfo->buheader_len; + printf ("%s\n%s\n%s\n%d Bytes (%.4f Mb)\n\n", + // some ROMs have a name with control chars in it -> replace control chars + to_func (buf, strlen (buf), toprint), + NULL_TO_EMPTY (rominfo->maker), + NULL_TO_EMPTY (rominfo->country), + x, + TOMBIT_F (x)); + + // padded? + if (!padded) + puts ("Padded: No"); + else + printf ("Padded: Maybe, %d Bytes (%.4f Mb)\n", padded, TOMBIT_F (padded)); + + // intro, trainer? + // nes.c determines itself whether or not there is a trainer + if (intro && ucon64.console != UCON64_NES) + printf ("Intro/Trainer: Maybe, %d Bytes\n", intro); + + // interleaved? + if (rominfo->interleaved != UCON64_UNKNOWN) + // printing this is handy for SNES, N64 & Genesis ROMs, but maybe + // nonsense for others + printf ("Interleaved/Swapped: %s\n", + rominfo->interleaved ? + (rominfo->interleaved > 1 ? "Yes (2)" : "Yes") : + "No"); + + // backup unit header? + if (rominfo->buheader_len) + printf ("Backup unit/emulator header: Yes, %d Bytes\n", + rominfo->buheader_len); + else +// for NoisyB: + puts ("Backup unit/emulator header: No"); // printing No is handy for SNES ROMs +// for NoisyB: + + // split? + if (split) + { + printf ("Split: Yes, %d part%s\n", split, (split != 1) ? "s" : ""); + // nes.c calculates the correct checksum for split ROMs (=Pasofami + // format), so there is no need to join the files + if (ucon64.console != UCON64_NES) + puts ("NOTE: To get the correct checksum the ROM parts must be joined"); + } + + // miscellaneous info + if (rominfo->misc[0]) + { + strcpy (buf, rominfo->misc); + printf ("%s\n", to_func (buf, strlen (buf), toprint)); + } + + // internal checksums? + if (rominfo->has_internal_crc) + { + char *fstr; + + // the internal checksum of GBA ROMS stores only the checksum of the + // internal header + if (ucon64.console != UCON64_GBA) + fstr = "Checksum: %%s, 0x%%0%dlx (calculated) %%c= 0x%%0%dlx (internal)\n"; + else + fstr = "Header checksum: %%s, 0x%%0%dlx (calculated) %%c= 0x%%0%dlx (internal)\n"; + + sprintf (buf, fstr, + rominfo->internal_crc_len * 2, rominfo->internal_crc_len * 2); +#ifdef USE_ANSI_COLOR + printf (buf, + ucon64.ansi_color ? + ((rominfo->current_internal_crc == rominfo->internal_crc) ? + "\x1b[01;32mOk\x1b[0m" : "\x1b[01;31mBad\x1b[0m") + : + ((rominfo->current_internal_crc == rominfo->internal_crc) ? "Ok" : "Bad"), + rominfo->current_internal_crc, + (rominfo->current_internal_crc == rominfo->internal_crc) ? '=' : '!', + rominfo->internal_crc); +#else + printf (buf, + (rominfo->current_internal_crc == rominfo->internal_crc) ? "Ok" : "Bad", + rominfo->current_internal_crc, + (rominfo->current_internal_crc == rominfo->internal_crc) ? '=' : '!', + rominfo->internal_crc); +#endif + + if (rominfo->internal_crc2[0]) + printf ("%s\n", rominfo->internal_crc2); + } + + fflush (stdout); +} + + +#ifdef USE_ZLIB +void +ucon64_fname_arch (const char *fname) +{ + char name[FILENAME_MAX]; + + unzFile file = unzOpen (fname); + unzip_goto_file (file, unzip_current_file_nr); + unzGetCurrentFileInfo (file, NULL, name, FILENAME_MAX, NULL, 0, NULL, 0); + unzClose (file); +#if defined _WIN32 || defined __MSDOS__ + { + int n, l = strlen (name); + for (n = 0; n < l; n++) + if (name[n] == '/') + name[n] = FILE_SEPARATOR; + } +#endif + strncpy (ucon64.fname_arch, basename2 (name), FILENAME_MAX)[FILENAME_MAX - 1] = 0; +} +#endif + + +void +ucon64_usage (int argc, char *argv[]) +{ + int x = 0, y = 0, c = 0, single = 0; + const char *name_exe = basename2 (argv[0]); +#ifdef USE_DISCMAGE + char *name_discmage; +#endif + (void) argc; // warning remover + +#ifdef USE_ZLIB + printf ("Usage: %s [OPTION]... [ROM|IMAGE|SRAM|FILE|DIR|ARCHIVE]...\n\n", name_exe); +#else + printf ("Usage: %s [OPTION]... [ROM|IMAGE|SRAM|FILE|DIR]...\n\n", name_exe); +#endif + + // single usage + for (x = 0; arg[x].val; x++) + if (arg[x].console) // IS console + for (y = 0; option[y]; y++) + for (c = 0; option[y][c].name || option[y][c].help; c++) + if (option[y][c].object) + if (((st_ucon64_obj_t *) option[y][c].object)->console == arg[x].console) + { + getopt2_usage (option[y]); + single = 1; + break; + } + + if (!single) + getopt2_usage (options); + + fputc ('\n', stdout); + + printf ("DATabase: %d known ROMs (DAT files: %s)\n\n", + ucon64_dat_total_entries (), ucon64.datdir); + +#ifdef USE_DISCMAGE + name_discmage = +#ifdef DLOPEN + ucon64.discmage_path; +#else +#if defined __MSDOS__ + "discmage.dxe"; +#elif defined __CYGWIN__ || defined _WIN32 + "discmage.dll"; +#elif defined __APPLE__ // Mac OS X actually + "libdiscmage.dylib"; +#elif defined __unix__ || defined __BEOS__ + "libdiscmage.so"; +#else + "unknown"; +#endif +#endif + + if (!ucon64.discmage_enabled) + { + printf (ucon64_msg[NO_LIB], name_discmage); + fputc ('\n', stdout); + } +#endif + +#ifdef USE_PARALLEL + puts ("NOTE: You only need to specify PORT if uCON64 doesn't detect the (right)\n" + " parallel port. If that is the case give a hardware address. For example:\n" + " ucon64 " OPTION_LONG_S "xswc \"rom.swc\" " OPTION_LONG_S "port=0x378\n" + " In order to connect a copier to a PC's parallel port you need a standard\n" + " bidirectional parallel cable\n"); +#endif + + printf ("TIP: %s " OPTION_LONG_S "help " OPTION_LONG_S "snes (would show only SNES related help)\n", name_exe); + +#if defined __MSDOS__ || defined _WIN32 + printf (" %s " OPTION_LONG_S "help|more (to see everything in more)\n", name_exe); +#else + printf (" %s " OPTION_LONG_S "help|less (to see everything in less)\n", name_exe); // less is more ;-) +#endif + + puts (" Give the force recognition switch a try if something went wrong\n" + "\n" + "Please report any problems/ideas/fixes to noisyb@gmx.net or\n" + "ucon64-announce@lists.sf.net or visit http://ucon64.sf.net\n"); +} diff --git a/ucon64/2.0/src/ucon64.h b/ucon64/2.0/src/ucon64.h new file mode 100644 index 0000000..5ab8cfa --- /dev/null +++ b/ucon64/2.0/src/ucon64.h @@ -0,0 +1,211 @@ +/* +uCON64 - a tool to modify video game ROMs and to transfer ROMs to the +different backup units/emulators that exist. It is based on the old uCON but +with completely new source. It aims to support all cartridge consoles and +handhelds like N64, JAG, SNES, NG, GENESIS, GB, LYNX, PCE, SMS, GG, NES and +their backup units + +Copyright (c) 1999 - 2004 NoisyB +Copyright (c) 2001 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef UCON64_H +#define UCON64_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "ucon64_defines.h" // MAXBUFSIZE, etc.. + + +#define UCON64_ISSET(x) (x != UCON64_UNKNOWN) + + +typedef enum { UCON64_SPP, UCON64_EPP, UCON64_ECP } parport_mode_t; + +/* + st_rominfo_t this struct contains very specific informations only + _init() can supply after the correct console + type was identified. + st_ucon64_t this struct containes st_rominfo_t and unspecific + informations and some workflow stuff + st_ucon64_obj_t ucon64 object for use in st_getopt2_t +*/ +typedef struct +{ + const char *console_usage; // console system of the ROM + const char *copier_usage; // backup unit of the ROM + + int interleaved; // ROM is interleaved (swapped) + int data_size; // ROM data size without "red tape" + + const void *buheader; // (possible) header of backup unit + int buheader_start; // start of backup unit header (mostly 0) + int buheader_len; // length of backup unit header 0 == no bu hdr + + const void *header; // (possible) internal ROM header + int header_start; // start of internal ROM header + int header_len; // length of internal ROM header 0 == no hdr + + char name[MAXBUFSIZE]; // internal ROM name + const char *maker; // maker name of the ROM + const char *country; // country name of the ROM + char misc[MAXBUFSIZE]; // some miscellaneous information + // about the ROM in one single string + int has_internal_crc; // ROM has internal checksum (SNES, Mega Drive, Game Boy) + unsigned int current_internal_crc; // calculated checksum + + unsigned int internal_crc; // internal checksum + int internal_crc_start; // start of internal checksum in ROM header + int internal_crc_len; // length (in bytes) of internal checksum in ROM header + + char internal_crc2[MAXBUFSIZE]; // 2nd or inverse internal checksum +} st_rominfo_t; + +typedef struct +{ + int argc; + char **argv; + + int option; // current option (UCON64_HEX, UCON64_FIND, ...) + const char *optarg; // ptr to current options optarg + + // TODO?: rename to fname + const char *rom; // ROM (cmdline) with path + + int fname_len; // force fname output format for --rrom, --rename, etc... + // fname_len can be UCON64_RR83 (8.3) or UCON64_FORCE63 (63.x) + char fname_arch[FILENAME_MAX]; // filename in archive (currently only for zip) + int file_size; // (uncompressed) ROM file size (NOT console specific) + unsigned int crc32; // crc32 value of ROM (used for DAT files) (NOT console specific) + unsigned int fcrc32; // if non-zero: crc32 of ROM as it is on disk (NOT console specific) + + /* + if console == UCON64_UNKNOWN or st_rominfo_t == NULL ucon64_rom_nfo() won't + be shown + */ + int console; // the detected console system + + const char *file; // FILE (cmdline) with path + + char configfile[FILENAME_MAX]; // path and name of the config file + char configdir[FILENAME_MAX]; // directory for config + char datdir[FILENAME_MAX]; // directory for DAT files + char output_path[FILENAME_MAX]; // -o argument (default: cwd) +#ifdef USE_DISCMAGE + char discmage_path[FILENAME_MAX]; // path to the discmage DLL +#endif +#if defined USE_PPDEV || defined AMIGA + char parport_dev[80]; // parallel port device (e.g. +#endif // /dev/parport0 or parallel.device) + int parport_needed; + int parport; // parallel port address + parport_mode_t parport_mode; // parallel port mode: ECP, EPP, SPP +#ifdef USE_USB + int usbport; // non-zero => use usbport, 1 = USB0, 2 = USB1 + char usbport_dev[80]; // usb port device (e.g. /dev/usb/hiddev0) +#endif + +#ifdef USE_ANSI_COLOR + int ansi_color; +#endif + int backup; // flag if backups files should be created + int frontend; // flag if uCON64 was started by a frontend +#ifdef USE_DISCMAGE + int discmage_enabled; // flag if discmage DLL is loaded +#endif + int dat_enabled; // flag if DAT file(s) are usable/enabled + int quiet; // quiet == -1 means verbose + 1 + + int force_disc; // --disc was used + uint32_t flags; // detect and init ROM info + + int do_not_calc_crc; // disable checksum calc. to speed up --ls,--lsv, etc. + + /* + These values override values in st_rominfo_t. Use UCON64_ISSET() + to check them. When adding new ones don't forget to update + ucon64_execute_options() too. + */ +#if 1 + int buheader_len; // length of backup unit header 0 == no bu hdr + int interleaved; // ROM is interleaved (swapped) +#else + st_rominfo_t *override; // overrides values in (st_rominfo_t *) rominfo +#endif + +#if 1 + int id; // generate unique name (currently + // only used by snes_gd3()) + // the following values are for SNES, NES, Genesis and Nintendo 64 + int battery; // NES UNIF/iNES/Pasofami + int bs_dump; // SNES "ROM" is a Broadcast Satellaview dump + const char *comment; // NES UNIF + int controller; // NES UNIF & SNES NSRT + int controller2; // SNES NSRT + const char *dump_info; // NES UNIF + int io_mode; // SNES SWC, Nintendo 64 CD64 & Cyan's Megadrive copier + const char *mapr; // NES UNIF board name or iNES mapper number + int mirror; // NES UNIF/iNES/Pasofami + int part_size; // SNES/Genesis split part size + int region; // Genesis (for -multi) + int snes_header_base; // SNES ROM is "Extended" (or Sufami Turbo) + int snes_hirom; // SNES ROM is HiROM + int split; // ROM is split + int tv_standard; // NES UNIF + int use_dump_info; // NES UNIF + int vram; // NES UNIF +#else + void *console_workflow; // ptr to a console specific workflow +#endif + +#ifdef USE_DISCMAGE + void *image; // info from libdiscmage (dm_image_t *) +#endif + void *dat; // info from DATabase (st_ucon64_dat_t *) + st_rominfo_t *rominfo; // info from _init() (st_rominfo_t *) + const st_getopt2_t *options; // all options with help (st_getopt2_t) +} st_ucon64_t; + +typedef struct +{ + int console; // UCON64_SNES, etc... + uint32_t flags; // WF_INIT, etc.. +// const char *optarg; // pointer to optarg + // initialised with NULL and used later +} st_ucon64_obj_t; + +/* + ucon64_init() init st_rominfo_t, st_ucon64_dat_t and dm_image_t + ucon64_nfo() display contents of st_rominfo_t, st_ucon64_dat_t and + dm_image_t + ucon64_flush_rom() flush only st_rominfo_t + ucon64_usage() print usage + ucon64_fname_arch() + + ucon64 global (st_ucon64_t *) +*/ +extern int ucon64_init (void); +extern int ucon64_nfo (void); +extern st_rominfo_t *ucon64_flush_rom (st_rominfo_t *); +extern void ucon64_usage (int argc, char *argv[]); +#ifdef USE_ZLIB +extern void ucon64_fname_arch (const char *fname); +#endif + +extern st_ucon64_t ucon64; +#endif // UCON64_H diff --git a/ucon64/2.0/src/ucon64_dat.c b/ucon64/2.0/src/ucon64_dat.c new file mode 100644 index 0000000..e51d274 --- /dev/null +++ b/ucon64/2.0/src/ucon64_dat.c @@ -0,0 +1,1248 @@ +/* +ucon64_dat.c - support for DAT files as known from RomCenter, GoodXXXX, etc. + +Copyright (c) 1999 - 2004 NoisyB +Copyright (c) 2002 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_DIRENT_H +#include +#endif +#ifdef _WIN32 +#include +#endif +#include "misc/misc.h" +#include "misc/getopt2.h" +#include "misc/property.h" +#include "misc/string.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "ucon64.h" +#include "ucon64_misc.h" +#include "ucon64_dat.h" +#include "console/console.h" + +#define MAX_FIELDS_IN_DAT 32 +#define DAT_FIELD_SEPARATOR (0xac) +#define DAT_FIELD_SEPARATOR_S "\xac" +#define MAX_GAMES_FOR_CONSOLE 50000 // TODO?: dynamic size + +typedef struct +{ + const char *id; // string to detect console from datfile name + int (*compare) (const void *a, const void *b); // the function which compares the id with the filename + // compare() == 0 means success + int8_t console; // UCON64_SNES, UCON64_NES, etc. + const st_getopt2_t *console_usage; +} st_console_t; + +typedef struct +{ + uint32_t crc32; + long filepos; +} st_idx_entry_t; + +typedef struct +{ + uint32_t crc32; + char *fname; +} st_mkdat_entry_t; + +#ifndef _WIN32 +static DIR *ddat = NULL; +#else +static HANDLE ddat = NULL; +#endif +static FILE *fdat = NULL; +static int ucon64_n_files = 0, filepos_line = 0, warning = 1; // show the warning only +static FILE *ucon64_datfile; // once when indexing +static char ucon64_dat_fname[FILENAME_MAX]; +static st_mkdat_entry_t *ucon64_mkdat_entries = NULL; + +const st_getopt2_t ucon64_dat_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "DATabase (support for DAT files)", + NULL + }, + { + "db", 0, 0, UCON64_DB, + NULL, "DATabase statistics", + &ucon64_wf[WF_OBJ_ALL_STOP_NO_ROM] + }, + { + "dbv", 0, 0, UCON64_DBV, + NULL, "like " OPTION_LONG_S "db but more verbose", + &ucon64_wf[WF_OBJ_ALL_STOP_NO_ROM] + }, + { + "dbs", 1, 0, UCON64_DBS, + "CRC32", "search ROM with CRC32 in DATabase", + &ucon64_wf[WF_OBJ_ALL_STOP_NO_ROM] + }, + { + "scan", 0, 0, UCON64_SCAN, + NULL, "generate ROM list for all ROMs using DATabase\n" + "like: GoodXXXX scan ...", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_NO_SPLIT] + }, + { + "lsd", 0, 0, UCON64_LSD, + NULL, "same as " OPTION_LONG_S "scan", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE] + }, + { + "mkdat", 1, 0, UCON64_MKDAT, + "DATFILE", "create DAT file; use -o to specify an output directory", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE] + }, + { + "rrom", 0, 0, UCON64_RROM, + NULL, "rename ROMs to their internal names", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_NO_SPLIT] + }, + { + "rename", 0, 0, UCON64_RENAME, + NULL, "rename ROMs to their DATabase names\n" + "use -o to specify an output directory", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_NO_SPLIT] + }, + { + "rr83", 0, 0, UCON64_RR83, + NULL, "force to rename to 8.3 filenames", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_NO_SPLIT] + }, + { + "force63", 0, 0, UCON64_FORCE63, + NULL, "force to rename all filenames into Joliet CD format\n" + "like: GoodXXXX rename inplace force63 ...\n" + "TIP: using " OPTION_LONG_S "nes would process only NES ROMs", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "rl", 0, 0, UCON64_RL, + NULL, "rename ROMs to lowercase", + NULL + }, + { + "ru", 0, 0, UCON64_RU, + NULL, "rename ROMs to uppercase", + NULL + }, +#if 0 + { + "good", 0, 0, UCON64_GOOD, + NULL, "used with " OPTION_LONG_S "rrom and " OPTION_LONG_S "rr83 ROMs will be renamed using\n" + "the DATabase", + NULL + }, +#endif + { + NULL, 0, 0, 0, + NULL, NULL, + NULL + } + }; + + +static void +closedir_ddat (void) +{ + if (ddat) +#ifndef _WIN32 + closedir (ddat); +#else + FindClose (ddat); +#endif + ddat = NULL; +} + + +static void +fclose_fdat (void) +{ + if (fdat) + fclose (fdat); + fdat = NULL; +} + + +static int +custom_stristr (const void *a, const void *b) +{ + return !stristr ((const char *) a, (const char *) b); +} + + +static int +custom_strnicmp (const void *a, const void *b) +{ + return strnicmp ((const char *) a, (const char *) b, + MIN (strlen ((const char *) a), strlen ((const char *) b))); +} + + +#if 0 +static int +custom_stricmp (const void *a, const void *b) +{ + return stricmp ((const char *) a, (const char *) b); +} +#endif + + +static char * +get_next_file (char *fname) +{ +#ifndef _WIN32 + struct dirent *ep; + + if (!ddat) + if (!(ddat = opendir (ucon64.datdir))) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], ucon64.datdir); + return NULL; + } + while ((ep = readdir (ddat)) != NULL) + if (!stricmp (get_suffix (ep->d_name), ".dat")) + { + sprintf (fname, "%s" FILE_SEPARATOR_S "%s", ucon64.datdir, ep->d_name); + return fname; + } +#else + char search_pattern[FILENAME_MAX]; + WIN32_FIND_DATA find_data; + + if (!ddat) + { + // Note that FindFirstFile() & FindNextFile() are case insensitive + sprintf (search_pattern, "%s" FILE_SEPARATOR_S "*.dat", ucon64.datdir); + if ((ddat = FindFirstFile (search_pattern, &find_data)) == INVALID_HANDLE_VALUE) + { + // Not being able to find a DAT file is not a real error + if (GetLastError () != ERROR_FILE_NOT_FOUND) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], ucon64.datdir); + return NULL; + } + } + else + { + sprintf (fname, "%s" FILE_SEPARATOR_S "%s", ucon64.datdir, find_data.cFileName); + return fname; + } + } + while (FindNextFile (ddat, &find_data)) + { + sprintf (fname, "%s" FILE_SEPARATOR_S "%s", ucon64.datdir, find_data.cFileName); + return fname; + } +#endif + closedir_ddat (); + return NULL; +} + + +static st_ucon64_dat_t * +get_dat_header (char *fname, st_ucon64_dat_t *dat) +{ + char buf[50 * 80]; // should be enough + + // Hell yes! I (NoisyB) use get_property() here... + strncpy (dat->author, get_property (fname, "author", buf, "Unknown"), sizeof (dat->author))[sizeof (dat->author) - 1] = 0; + strncpy (dat->version, get_property (fname, "version", buf, "?"), sizeof (dat->version))[sizeof (dat->version) - 1] = 0; + strncpy (dat->refname, get_property (fname, "refname", buf, ""), sizeof (dat->refname))[sizeof (dat->refname) - 1] = 0; + strcpy (dat->comment, get_property (fname, "comment", buf, "")); + strncpy (dat->date, get_property (fname, "date", buf, "?"), sizeof (dat->date))[sizeof (dat->date) - 1] = 0; + + return dat; +} + + +static int +fname_to_console (const char *fname, st_ucon64_dat_t *dat) +{ + int pos; + // We use the filename to find out for what console a DAT file is meant. + // The field "refname" seems too unreliable. + static const st_console_t console_type[] = + { + {"GoodSNES", custom_strnicmp, UCON64_SNES, snes_usage}, + {"SNES", custom_strnicmp, UCON64_SNES, snes_usage}, + {"GoodNES", custom_strnicmp, UCON64_NES, nes_usage}, + {"NES", custom_strnicmp, UCON64_NES, nes_usage}, + {"FDS", custom_stristr, UCON64_NES, nes_usage}, + {"GoodGBA", custom_strnicmp, UCON64_GBA, gba_usage}, + {"GBA", custom_strnicmp, UCON64_GBA, gba_usage}, + {"GoodGBX", custom_strnicmp, UCON64_GB, gameboy_usage}, + {"GBX", custom_strnicmp, UCON64_GB, gameboy_usage}, + {"GoodGEN", custom_strnicmp, UCON64_GEN, genesis_usage}, + {"GEN", custom_strnicmp, UCON64_GEN, genesis_usage}, + {"GoodGG", custom_strnicmp, UCON64_SMS, sms_usage}, + {"GG", custom_strnicmp, UCON64_SMS, sms_usage}, + {"GoodSMS", custom_strnicmp, UCON64_SMS, sms_usage}, + {"SMS", custom_strnicmp, UCON64_SMS, sms_usage}, + {"GoodJAG", custom_strnicmp, UCON64_JAG, jaguar_usage}, + {"JAG", custom_strnicmp, UCON64_JAG, jaguar_usage}, + {"GoodLynx", custom_strnicmp, UCON64_LYNX, lynx_usage}, + {"Lynx", custom_strnicmp, UCON64_LYNX, lynx_usage}, + {"GoodN64", custom_strnicmp, UCON64_N64, n64_usage}, + {"N64", custom_strnicmp, UCON64_N64, n64_usage}, + {"GoodPCE", custom_strnicmp, UCON64_PCE, pcengine_usage}, + {"PCE", custom_strnicmp, UCON64_PCE, pcengine_usage}, + {"Good2600", custom_strnicmp, UCON64_ATA, atari_usage}, + {"2600", custom_strnicmp, UCON64_ATA, atari_usage}, + {"Good5200", custom_strnicmp, UCON64_ATA, atari_usage}, + {"5200", custom_strnicmp, UCON64_ATA, atari_usage}, + {"Good7800", custom_strnicmp, UCON64_ATA, atari_usage}, + {"7800", custom_strnicmp, UCON64_ATA, atari_usage}, + {"GoodVECT", custom_strnicmp, UCON64_VEC, vectrex_usage}, + {"Vectrex", custom_stristr, UCON64_VEC, vectrex_usage}, + {"GoodWSX", custom_strnicmp, UCON64_SWAN, swan_usage}, + {"swan", custom_stristr, UCON64_SWAN, swan_usage}, + {"GoodCOL", custom_strnicmp, UCON64_COLECO, coleco_usage}, + {"Coleco", custom_stristr, UCON64_COLECO, coleco_usage}, + {"GoodINTV", custom_strnicmp, UCON64_INTELLI, intelli_usage}, + {"Intelli", custom_stristr, UCON64_INTELLI, intelli_usage}, + {"GoodNGPX", custom_strnicmp, UCON64_NGP, ngp_usage}, + {"NGP", custom_strnicmp, UCON64_NGP, ngp_usage}, + {"GoodVBOY", custom_strnicmp, UCON64_VBOY, vboy_usage}, + {"VBOY", custom_strnicmp, UCON64_VBOY, vboy_usage}, + + {"Neo-Geo", custom_strnicmp, UCON64_NG, neogeo_usage}, + {"MAME", custom_stristr, UCON64_MAME, mame_usage}, + {"Dreamcast", custom_stristr, UCON64_DC, dc_usage}, + {"Saturn", custom_stristr, UCON64_SAT, sat_usage}, + {"3do", custom_stristr, UCON64_3DO, real3do_usage}, + {"CDi", custom_stristr, UCON64_CDI, cdi_usage}, + {"XBox", custom_stristr, UCON64_XBOX, xbox_usage}, + {"CD32", custom_stristr, UCON64_CD32, cd32_usage}, +/* TODO: + {"psx", custom_stristr, UCON64_PSX, psx_usage}, + {"ps1", custom_stristr, UCON64_PSX, psx_usage}, + {"psone", custom_stristr, UCON64_PSX, psx_usage}, + {"ps2", custom_stristr, UCON64_PS2, ps2_usage}, + {"dc", custom_stristr, UCON64_DC, dc_usage}, + {"system", custom_stristr, UCON64_S16, s16_usage}, + {"pocket", custom_stristr, UCON64_NGP, ngp_usage}, + {"virtual", custom_stristr, UCON64_VBOY, vboy_usage}, + {"", custom_stristr, 0, channelf_usage}, + {"", custom_stristr, 0, gamecom_usage}, + {"", custom_stristr, 0, gc_usage}, + {"", custom_stristr, 0, gp32_usage}, + {"", custom_stristr, 0, odyssey2_usage}, + {"", custom_stristr, 0, odyssey_usage}, + {"", custom_stristr, 0, s16_usage}, + {"", custom_stristr, 0, sat_usage}, + {"", custom_stristr, 0, vc4000_usage}, +*/ + {0, 0, 0, 0} + }; + + for (pos = 0; console_type[pos].id; pos++) + { + if (!console_type[pos].compare (fname, console_type[pos].id)) + { + dat->console = console_type[pos].console; + dat->console_usage = (console_type[pos].console_usage[0].help); + break; + } + } + + if (console_type[pos].id == 0) + { + if (warning) + { + printf ("WARNING: \"%s\" is meant for a console unknown to uCON64\n\n", fname); + warning = 0; + } + dat->console = UCON64_UNKNOWN; + dat->console_usage = NULL; + } + + return dat->console; +} + + +static st_ucon64_dat_t * +line_to_dat (const char *fname, const char *dat_entry, st_ucon64_dat_t *dat) +// parse a dat entry into st_ucon64_dat_t +{ + static const char *dat_country[28][2] = + { + {"(FC)", "French Canada"}, + {"(FN)", "Finland"}, + {"(G)", "Germany"}, + {"(GR)", "Greece"}, + {"(H)", "Holland"}, // other (incorrect) name for The Netherlands + {"(HK)", "Hong Kong"}, + {"(I)", "Italy"}, + {"(J)", "Japan"}, + {"(JE)", "Japan & Europe"}, + {"(JU)", "Japan & U.S.A."}, + {"(JUE)", "Japan, U.S.A. & Europe"}, + {"(K)", "Korea"}, + {"(NL)", "The Netherlands"}, + {"(PD)", "Public Domain"}, + {"(S)", "Spain"}, + {"(SW)", "Sweden"}, + {"(U)", "U.S.A."}, + {"(UE)", "U.S.A. & Europe"}, + {"(UK)", "England"}, + {"(Unk)", "Unknown country"}, + /* + At least (A), (B), (C), (E) and (F) have to come after the other + countries, because some games have (A), (B) etc. in their name (the + non-country part). For example, the SNES games + "SD Gundam Generations (A) 1 Nen Sensouki (J) (ST)" or + "SD Gundam Generations (B) Guripus Senki (J) (ST)". + */ + {"(1)", "Japan & Korea"}, + {"(4)", "U.S.A. & Brazil NTSC"}, + {"(A)", "Australia"}, + {"(B)", "non U.S.A. (Genesis)"}, + {"(C)", "China"}, + {"(E)", "Europe"}, + {"(F)", "France"}, + {NULL, NULL} + }; + static const char *dat_flags[][2] = + { + // Often flags contain numbers, so don't search for the closing bracket + {"[a", "Alternate"}, + {"[p", "Pirate"}, + {"[b", "Bad dump"}, + {"[t", "Trained"}, + {"[f", "Fixed"}, + {"[T", "Translation"}, + {"[h", "Hack"}, + {"[x", "Bad checksum"}, + {"[o", "Overdump"}, + {"[!]", "Verified good dump"}, // [!] is ok + {NULL, NULL} + }; + char *dat_field[MAX_FIELDS_IN_DAT + 2] = { NULL }, buf[MAXBUFSIZE], *p = NULL; + uint32_t pos = 0; + int x = 0; + + if ((unsigned char) dat_entry[0] != DAT_FIELD_SEPARATOR) + return NULL; + + strcpy (buf, dat_entry); + + strarg (dat_field, buf, DAT_FIELD_SEPARATOR_S, MAX_FIELDS_IN_DAT); + + memset (dat, 0, sizeof (st_ucon64_dat_t)); + + strcpy (dat->datfile, basename2 (fname)); + + if (dat_field[3]) + strcpy (dat->name, dat_field[3]); + + if (dat_field[4]) + strcpy (dat->fname, dat_field[4]); + + if (dat_field[5]) + sscanf (dat_field[5], "%x", (unsigned int *) &dat->crc32); + + if (dat_field[6][0] == 'N' && dat_field[7][0] == 'O') + // e.g. GoodSNES bad crc & Nintendo FDS DAT + sscanf (dat_field[8], "%d", (int *) &dat->fsize); + else + sscanf (dat_field[6], "%d", (int *) &dat->fsize); + + *buf = 0; + for (x = 0, p = buf; dat_flags[x][0]; x++, p += strlen (p)) + if (strstr (dat->name, dat_flags[x][0])) + sprintf (p, "%s, ", dat_flags[x][1]); + if (buf[0]) + { + if ((p = strrchr (buf, ','))) + *p = 0; + sprintf (dat->misc, "Flags: %s", buf); + } + + p = dat->name; + dat->country = NULL; + for (pos = 0; dat_country[pos][0]; pos++) + if (stristr (p, dat_country[pos][0])) + { + dat->country = dat_country[pos][1]; + break; + } + + fname_to_console (dat->datfile, dat); + dat->copier_usage = unknown_usage[0].help; + + return dat; +} + + +uint32_t +line_to_crc (const char *dat_entry) +// get crc32 of current line +{ + char *dat_field[MAX_FIELDS_IN_DAT + 2] = { NULL }, buf[MAXBUFSIZE]; + unsigned int crc32 = 0; // has to be unsigned int to + // avoid a stupid gcc warning + if ((unsigned char) dat_entry[0] != DAT_FIELD_SEPARATOR) + return 0; + + strcpy (buf, dat_entry); + + strarg (dat_field, buf, DAT_FIELD_SEPARATOR_S, MAX_FIELDS_IN_DAT); + + if (dat_field[5]) + sscanf (dat_field[5], "%x", &crc32); + + return (uint32_t) crc32; +} + + +static st_ucon64_dat_t * +get_dat_entry (char *fname, st_ucon64_dat_t *dat, uint32_t crc32, long start) +{ + char buf[MAXBUFSIZE]; + + if (!fdat) + if (!(fdat = fopen (fname, "rb"))) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], fname); +#if defined _WIN32 || defined __CYGWIN__ || defined __MSDOS__ + if (!stricmp (basename2 (fname), "ntuser.dat")) + fputs (" Please see the FAQ, question 47 & 36\n", stderr); + // "ERROR: " +#endif + return NULL; + } + + if (start >= 0) + fseek (fdat, start, SEEK_SET); + + filepos_line = ftell (fdat); + while (fgets (buf, MAXBUFSIZE, fdat) != NULL) + { + if ((unsigned char) buf[0] == DAT_FIELD_SEPARATOR) + if (!crc32 || line_to_crc (buf) == crc32) + if (line_to_dat (fname, buf, dat)) + return dat; + filepos_line = ftell (fdat); + } + + fclose_fdat (); + return NULL; +} + + +int +ucon64_dat_view (int console, int verbose) +{ + char fname_dat[FILENAME_MAX], fname_index[FILENAME_MAX]; + const char *fname; + unsigned char *p; + static st_ucon64_dat_t dat; + int n, fsize, n_entries, n_entries_sum = 0, n_datfiles = 0; + st_idx_entry_t *idx_entry; + + while (get_next_file (fname_dat)) + { + fname = basename2 (fname_dat); + if (console != UCON64_UNKNOWN) + if (fname_to_console (fname, &dat) != console) + continue; + + get_dat_header (fname_dat, &dat); + strcpy (fname_index, fname_dat); + set_suffix (fname_index, ".idx"); + + fsize = fsizeof (fname_index); + n_entries = fsize / sizeof (st_idx_entry_t); + n_entries_sum += n_entries; + n_datfiles++; + + printf ("DAT info:\n" + " %s\n" +// " Console: %s\n" + " Version: %s (%s, %s)\n" + " Author: %s\n" + " Comment: %s\n" + " Entries: %d\n\n", + fname, +// dat.console_usage[0], + dat.version, + dat.date, + dat.refname, + dat.author, + dat.comment, + n_entries); + + if (!(p = (unsigned char *) malloc (fsize))) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], fsize); + continue; + } + + if (ucon64_fread (p, 0, fsize, fname_index) != fsize) + { + fprintf (stderr, ucon64_msg[READ_ERROR], fname_index); + free (p); + continue; + } + + if (verbose) + { + // display all DAT entries + for (n = 0; n < n_entries; n++) + { + idx_entry = &((st_idx_entry_t *) p)[n]; + printf ("Checksum (CRC32): 0x%08x\n", (unsigned int) idx_entry->crc32); + if (get_dat_entry (fname_dat, &dat, idx_entry->crc32, idx_entry->filepos)) + ucon64_dat_nfo (&dat, 0); + fputc ('\n', stdout); + } + fclose_fdat (); + } + free (p); + } + + printf ("DAT files: %d; entries: %d; total entries: %d\n", + n_datfiles, n_entries_sum, ucon64_dat_total_entries ()); + + return 0; +} + + +unsigned int +ucon64_dat_total_entries (void) +{ + uint32_t entries = 0; + int fsize; + char fname[FILENAME_MAX]; + + if (!ucon64.dat_enabled) + return 0; + + while (get_next_file (fname)) + { + set_suffix (fname, ".idx"); + fsize = fsizeof (fname); + entries += (fsize < 0 ? 0 : fsize / sizeof (st_idx_entry_t)); + } + + return entries; +} + + +static int +idx_compare (const void *key, const void *found) +{ + /* + The return statement looks overly complicated, but is really necessary. + This construct: + return ((st_idx_entry_t *) key)->crc32 - ((st_idx_entry_t *) found)->crc32; + does *not* work correctly for all cases. + */ + return (int) (((int64_t) ((st_idx_entry_t *) key)->crc32 - + (int64_t) ((st_idx_entry_t *) found)->crc32) / 2); +} + + +st_ucon64_dat_t * +ucon64_dat_search (uint32_t crc32, st_ucon64_dat_t *datinfo) +{ + char fname_dat[FILENAME_MAX], fname_index[FILENAME_MAX]; + const char *fname; + unsigned char *p = NULL; + int32_t fsize = 0; + st_idx_entry_t *idx_entry, key; + static st_ucon64_dat_t dat; + st_ucon64_dat_t *dat_p = NULL; + + memset (&dat, 0, sizeof (st_ucon64_dat_t)); + + if (!crc32) + return NULL; + + while (get_next_file (fname_dat)) + { + fname = basename2 (fname_dat); + + if (ucon64.console != UCON64_UNKNOWN) + if (fname_to_console (fname, &dat) != ucon64.console) + continue; + + strcpy (fname_index, fname_dat); + set_suffix (fname_index, ".idx"); + if (access (fname_index, F_OK) != 0) // for a "bad" DAT file + continue; + fsize = fsizeof (fname_index); + + if (!(p = (unsigned char *) malloc (fsize))) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], fsize); + closedir_ddat (); + return NULL; + } + + // load the index for the current dat file + if (ucon64_fread (p, 0, fsize, fname_index) != fsize) + { + fprintf (stderr, ucon64_msg[READ_ERROR], fname_index); + closedir_ddat (); + free (p); + return NULL; + } + + // search index for crc + key.crc32 = crc32; + idx_entry = (st_idx_entry_t *) bsearch (&key, p, fsize / sizeof (st_idx_entry_t), + sizeof (st_idx_entry_t), idx_compare); + if (idx_entry) // crc32 found + { + if (!datinfo) + dat_p = (st_ucon64_dat_t *) &dat; // TODO?: malloc() + else + dat_p = (st_ucon64_dat_t *) &datinfo; + + // open dat file and read entry + if (get_dat_entry (fname_dat, dat_p, crc32, idx_entry->filepos)) + if (crc32 == dat_p->crc32) + { + strcpy (dat_p->datfile, basename2 (fname_dat)); + get_dat_header (fname_dat, dat_p); + closedir_ddat (); + fclose_fdat (); + free (p); + return dat_p; + } + fclose_fdat (); + } + free (p); + } + + return NULL; +} + + +int +ucon64_dat_indexer (void) +// create or update index of DAT file +{ + char fname_dat[FILENAME_MAX], fname_index[FILENAME_MAX], errorfname[FILENAME_MAX]; + struct stat fstate_dat, fstate_index; + st_ucon64_dat_t dat; + FILE *errorfile; + time_t start_time = 0; + int update = 0, n_duplicates, n, size = 0, pos; + st_idx_entry_t *idx_entries, *idx_entry; + + warning = 1; // enable warning again for DATs with unrecognized console systems + + if (!(idx_entries = (st_idx_entry_t *) + malloc (MAX_GAMES_FOR_CONSOLE * sizeof (st_idx_entry_t)))) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], + MAX_GAMES_FOR_CONSOLE * sizeof (st_idx_entry_t)); + exit (1); + } + + while (get_next_file (fname_dat)) + { + strcpy (fname_index, fname_dat); + set_suffix (fname_index, ".idx"); + + if (!stat (fname_dat, &fstate_dat) && !stat (fname_index, &fstate_index)) + { + if (fstate_dat.st_mtime < fstate_index.st_mtime) + continue; // index file seems to be present and up-to-date + update = 1; + } + + start_time = time (0); + size = fsizeof (fname_dat); + + printf ("%s: %s\n", (update ? "Update" : "Create"), basename2 (fname_index)); + pos = 0; + n_duplicates = 0; + errorfile = NULL; + while (get_dat_entry (fname_dat, &dat, 0, -1)) + { + if (pos == MAX_GAMES_FOR_CONSOLE) + { + fprintf (stderr, + "\n" + "INTERNAL ERROR: MAX_GAMES_FOR_CONSOLE is too small (%d)\n", + MAX_GAMES_FOR_CONSOLE); + break; + } + + /* + Doing a linear search removes the need of using the slow qsort() + function inside the loop. Doing a binary search doesn't improve the + speed much, but is much more efficient of course. Using qsort() + inside the loop slows it down with a factor greater than 10. + */ + idx_entry = NULL; + for (n = 0; n < pos; n++) + if (idx_entries[n].crc32 == dat.crc32) + idx_entry = &idx_entries[n]; + if (idx_entry) + { + // This really makes one loose trust in the DAT files... + char current_name[2 * 80]; + long current_filepos = ftell (fdat); + + if (!errorfile) + { + strcpy (errorfname, fname_index); + set_suffix (errorfname, ".err"); + if (!(errorfile = fopen (errorfname, "w"))) // text file for WinDOS + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], errorfname); + continue; + } + } + + strcpy (current_name, dat.name); + get_dat_entry (fname_dat, &dat, 0, idx_entry->filepos); + fprintf (errorfile, + "\n" + "WARNING: DAT file contains a duplicate CRC32 (0x%x)!\n" + " First game with this CRC32: \"%s\"\n" + " Ignoring game: \"%s\"\n", + (unsigned int) dat.crc32, dat.name, current_name); + + n_duplicates++; + fseek (fdat, current_filepos, SEEK_SET); + continue; + } + + idx_entries[pos].crc32 = dat.crc32; + idx_entries[pos].filepos = filepos_line; + + if (!(pos % 20)) + ucon64_gauge (start_time, ftell (fdat), size); + pos++; + } + fclose_fdat (); + + if (pos > 0) + { + qsort (idx_entries, pos, sizeof (st_idx_entry_t), idx_compare); + if (ucon64_fwrite (idx_entries, 0, pos * sizeof (st_idx_entry_t), fname_index, "wb") + != (int) (pos * sizeof (st_idx_entry_t))) + { + fputc ('\n', stderr); + fprintf (stderr, ucon64_msg[WRITE_ERROR], fname_index); + } + ucon64_gauge (start_time, size, size); + } + + if (n_duplicates > 0) + printf ("\n" + "\n" + "WARNING: DAT file contains %d duplicate CRC32%s\n" + " Warnings have been written to \"%s\"", + n_duplicates, n_duplicates != 1 ? "s" : "", errorfname); + if (errorfile) + { + fclose (errorfile); + errorfile = NULL; + } + fputs ("\n\n", stdout); + } + free (idx_entries); + + return 0; +} + + +#if 0 +st_ucon64_dat_t * +ucon64_dat_flush (st_ucon64_dat_t *dat) +{ + memset (dat, 0, sizeof (st_ucon64_dat_t)); + ucon64.dat = NULL; + return NULL; +} +#endif + + +void +ucon64_dat_nfo (const st_ucon64_dat_t *dat, int display_version) +{ + char buf[MAXBUFSIZE], *p = NULL; + int n; + + if (!dat) + { + printf (ucon64_msg[DAT_NOT_FOUND], ucon64.crc32); + return; + } + + fputs ("DAT info:\n", stdout); + // console type? + if (dat->console_usage != NULL) + { + strcpy (buf, dat->console_usage); + // fix ugly multi-line console "usages" (PC-Engine) + if ((p = strchr (buf, '\n')) != NULL) + *p = 0; + printf (" %s\n", buf); + } + + printf (" %s\n", dat->name); + + if (dat->country) + printf (" %s\n", dat->country); + + /* + The DAT files are not consistent. Some include the file suffix, but + others don't. We want to display the canonical file name only if it + really differs from the canonical game name (usually file name without + suffix). + */ + n = strlen (dat->fname); + p = (char *) get_suffix (dat->fname); + if (!(stricmp (p, ".nes") && // NES + stricmp (p, ".fds") && // NES FDS + stricmp (p, ".gb") && // Game Boy + stricmp (p, ".gbc") && // Game Boy Color + stricmp (p, ".gba") && // Game Boy Advance + stricmp (p, ".smc") && // SNES + stricmp (p, ".sc") && // Sega Master System + stricmp (p, ".sg") && // Sega Master System + stricmp (p, ".sms") && // Sega Master System + stricmp (p, ".gg") && // Game Gear + stricmp (p, ".smd") && // Genesis + stricmp (p, ".v64"))) // Nintendo 64 + ((char *) dat->fname)[strlen (dat->fname) - strlen (p)] = 0; + + if (stricmp (dat->name, dat->fname) != 0) + printf (" Filename: %s\n", dat->fname); + + printf (" %d Bytes (%.4f Mb)\n", (int) dat->fsize, TOMBIT_F (dat->fsize)); + + if (dat->misc[0]) + printf (" %s\n", dat->misc); + + if (display_version) + { + if (stristr (dat->datfile, dat->version)) + printf (" %s (%s, %s)\n", + dat->datfile, + dat->date, + dat->refname); + else + printf (" %s (%s, %s, %s)\n", + dat->datfile, + dat->version, + dat->date, + dat->refname); + } +} + + +static void +ucon64_close_datfile (void) +{ + int n; + + if (ucon64_datfile) + { + fclose (ucon64_datfile); + printf (ucon64_msg[WROTE], ucon64_dat_fname); + ucon64_datfile = NULL; + + for (n = 0; n < ucon64_n_files; n++) + { + free (ucon64_mkdat_entries[n].fname); + ucon64_mkdat_entries[n].fname = NULL; + } + ucon64_n_files = 0; + } +} + + +int +ucon64_create_dat (const char *dat_file_name, const char *filename, + int buheader_len) +{ + static int first_file = 1, console; + int n, x; + static char *console_name; + char fname[FILENAME_MAX], *ptr; + time_t time_t_val; + struct tm *t; + + if (first_file) + { + char *plugin = ""; + + first_file = 0; + console = ucon64.console; + switch (ucon64.console) + { + case UCON64_3DO: + console_name = "3DO"; + break; + case UCON64_ATA: + console_name = "NES"; + break; + case UCON64_CD32: + console_name = "CD32"; + break; + case UCON64_CDI: + console_name = "CD-i"; + break; + case UCON64_COLECO: + console_name = "Coleco"; + break; + case UCON64_DC: + console_name = "Dreamcast"; + break; + case UCON64_GB: + console_name = "Game Boy"; + break; + case UCON64_GBA: + console_name = "Game Boy Advance"; + break; + case UCON64_GC: + console_name = "Game Cube"; + break; + case UCON64_GEN: + console_name = "Genesis/Mega Drive"; + plugin = "genesis.dll"; + break; + case UCON64_INTELLI: + console_name = "Intellivision"; + break; + case UCON64_JAG: + console_name = "Jaguar"; + break; + case UCON64_LYNX: + console_name = "Lynx"; + break; + case UCON64_MAME: + console_name = "M.A.M.E."; + plugin = "arcade.dll"; + break; + case UCON64_N64: + console_name = "Nintendo 64"; + plugin = "n64.dll"; + break; + case UCON64_NES: + console_name = "NES"; + plugin = "nes.dll"; + break; + case UCON64_NG: + console_name = "Neo Geo"; + plugin = "arcade.dll"; + break; + case UCON64_NGP: + console_name = "Neo Geo Pocket"; + break; + case UCON64_PCE: + console_name = "PC-Engine"; + break; + case UCON64_PS2: + console_name = "Playstation 2"; + break; + case UCON64_PSX: + console_name = "Playstation"; + break; + case UCON64_S16: + console_name = "S16"; + break; + case UCON64_SAT: + console_name = "Saturn"; + break; + case UCON64_SMS: + console_name = "SMS/Game Gear"; + break; + case UCON64_SNES: + console_name = "SNES"; + plugin = "snes.dll"; // be sure to use the new SNES plug-in (RC 2.62) + break; + case UCON64_SWAN: + console_name = "Wonderswan"; + break; + case UCON64_VBOY: + console_name = "Virtual Boy"; + break; + case UCON64_VEC: + console_name = "Vectrex"; + break; + case UCON64_XBOX: + console_name = "XBox"; + break; + default: + fputs (ucon64_msg[CONSOLE_ERROR], stderr); + exit (1); + break; + } + + if (!(ucon64_mkdat_entries = (st_mkdat_entry_t *) + malloc (MAX_GAMES_FOR_CONSOLE * sizeof (st_mkdat_entry_t)))) + { + fprintf (stderr, ucon64_msg[BUFFER_ERROR], + MAX_GAMES_FOR_CONSOLE * sizeof (st_mkdat_entry_t)); + exit (1); + } + + strcpy (ucon64_dat_fname, dat_file_name); + ucon64_file_handler (ucon64_dat_fname, NULL, OF_FORCE_BASENAME); + if (!(ucon64_datfile = fopen (ucon64_dat_fname, "wb"))) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], ucon64_dat_fname); + exit (1); + } + register_func (ucon64_close_datfile); + + time_t_val = time (NULL); + t = localtime (&time_t_val); + // RomCenter uses files in DOS text format, so we generate a file in that format + fprintf (ucon64_datfile, "[CREDITS]\r\n" + "author=uCON64\r\n" + "email=noisyb@gmx.net\r\n" + "homepage=uCON64 homepage\r\n" + "url=ucon64.sf.net\r\n" + "version=%s-%s\r\n" + "date=%d/%d/%d\r\n" + "comment=%s DAT file generated by uCON64\r\n" + "[DAT]\r\n" + "version=2.50\r\n" // required by RomCenter! + "plugin=%s\r\n" + "[EMULATOR]\r\n" + "refname=%s\r\n" + "version=\r\n" + "[GAMES]\r\n", + UCON64_VERSION_S, console_name, + t->tm_mday, t->tm_mon + 1, t->tm_year + 1900, + console_name, + plugin, + console_name); + } // first_file + + if (ucon64_n_files == MAX_GAMES_FOR_CONSOLE) + { + fprintf (stderr, + "INTERNAL ERROR: MAX_GAMES_FOR_CONSOLE is too small (%d)\n", + MAX_GAMES_FOR_CONSOLE); + exit (1); + } + strcpy (fname, basename2 (filename)); + + // Check the console type + n = 0; + if (ucon64.console != console) + { + if (ucon64.quiet == -1) + printf ("WARNING: Skipping (!%s) ", console_name); + else + return -1; + } + else + { + // Check if the CRC32 is unique. We don't want to be as stupid as + // the tool used to create the GoodDAT files. + // Yes, a plain and simple linear search. Analysing the files is orders + // of magnitude slower than this search + for (; n < ucon64_n_files; n++) + if (ucon64_mkdat_entries[n].crc32 == ucon64.crc32) + break; + if (n != ucon64_n_files) + { + if (ucon64.quiet < 1) // better print this by default + fputs ("WARNING: Skipping (duplicate) ", stdout); + else + return -1; + } + } + + fputs (filename, stdout); + if (ucon64.quiet == -1) // -v was specified + if (ucon64.fname_arch[0]) + printf (" (%s)", ucon64.fname_arch); + fputc ('\n', stdout); + + if (ucon64.console != console) // ucon64.quiet == -1 + return -1; + if (n != ucon64_n_files) + { + if (ucon64.quiet < 1) // better print this by default + printf (" First file with this CRC32 (0x%x) is:\n" + " \"%s\"\n", ucon64.crc32, ucon64_mkdat_entries[n].fname); + return -1; + } + + // Store the CRC32 to check if a file is unique + ucon64_mkdat_entries[ucon64_n_files].crc32 = ucon64.crc32; + /* + Also store the name of the file to display a helpful error message if a + file is not unique (a duplicate). We store the current filename inside the + archive as well, to be even more helpful :-) + */ + x = strlen (fname) + (ucon64.fname_arch[0] ? strlen (ucon64.fname_arch) + 4 : 1); + if (!(ucon64_mkdat_entries[ucon64_n_files].fname = (char *) malloc (x))) + { // + 3 for " ()" + fprintf (stderr, ucon64_msg[BUFFER_ERROR], x); // + 1 for ASCII-z + exit (1); + } + sprintf (ucon64_mkdat_entries[ucon64_n_files].fname, "%s%s%s%s", + fname, + ucon64.fname_arch[0] ? " (" : "", + ucon64.fname_arch[0] ? ucon64.fname_arch : "", + ucon64.fname_arch[0] ? ")" : ""); + + ptr = (char *) get_suffix (fname); + if (*ptr) + *ptr = 0; + fprintf (ucon64_datfile, DAT_FIELD_SEPARATOR_S "%s" // set file name + DAT_FIELD_SEPARATOR_S "%s" // set full name + DAT_FIELD_SEPARATOR_S "%s" // clone file name + DAT_FIELD_SEPARATOR_S "%s" // clone full name + DAT_FIELD_SEPARATOR_S "%s" // rom file name + DAT_FIELD_SEPARATOR_S "%08x" // RC quirck: leading zeroes are required + DAT_FIELD_SEPARATOR_S "%d" + DAT_FIELD_SEPARATOR_S // merged clone name + DAT_FIELD_SEPARATOR_S // merged rom name + DAT_FIELD_SEPARATOR_S "\r\n", + fname, + fname, + fname, + fname, + fname, + ucon64.crc32, + ucon64.file_size - buheader_len); + ucon64_n_files++; + return 0; +} diff --git a/ucon64/2.0/src/ucon64_dat.h b/ucon64/2.0/src/ucon64_dat.h new file mode 100644 index 0000000..ae20aea --- /dev/null +++ b/ucon64/2.0/src/ucon64_dat.h @@ -0,0 +1,69 @@ +/* +ucon64_dat.h - support for DAT files as known from RomCenter, GoodXXXX, etc. + +Copyright (c) 1999 - 2003 NoisyB +Copyright (c) 2003 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef UCON64_DAT_H +#define UCON64_DAT_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +extern const st_getopt2_t ucon64_dat_usage[]; + +typedef struct +{ + uint32_t crc32; // "official" CRC32 checksum of the ROM + int8_t console; // integer for the console system + char name[2 * 80]; // name of the ROM + const char *maker; // maker of the ROM + const char *country; // country of the ROM + char misc[25 * 80]; // miscellaneous information about the ROM + char fname[FILENAME_MAX]; // filename of the ROM + uint32_t fsize; // size in bytes + + char datfile[FILENAME_MAX]; // name of the dat file + char author[100]; // author of dat file + char version[100]; // version of dat file + char date[20]; // date of dat file + char comment[25 * 80]; // comment of dat file + char refname[100]; // ref name + + const char *console_usage; // console system usage + const char *copier_usage; // backup unit usage +} st_ucon64_dat_t; + +/* + ucon64_dat_search() search dat files for crc and return ucon64_dat_t + ucon64_dat_total_entries() return # of ROMs in all DAT's + ucon64_dat_view() display the complete dat collection + ucon64_dat_indexer() create or update index file for DAT's + ucon64_dat_flush() flush contents of ucon64_dat_t + ucon64_dat_nfo() view contents of ucon64_dat_t +*/ +extern st_ucon64_dat_t *ucon64_dat_search (uint32_t crc32, st_ucon64_dat_t *dat); +extern unsigned int ucon64_dat_total_entries (void); +extern int ucon64_dat_view (int console, int verbose); +extern int ucon64_dat_indexer (void); +//extern st_ucon64_dat_t *ucon64_dat_flush (st_ucon64_dat_t *dat); +extern void ucon64_dat_nfo (const st_ucon64_dat_t *dat, int display_version); +extern int ucon64_create_dat (const char *dat_file_name, const char *filename, + int buheader_len); + +#endif // UCON64_DAT_H diff --git a/ucon64/2.0/src/ucon64_defines.h b/ucon64/2.0/src/ucon64_defines.h new file mode 100644 index 0000000..6c1b0d4 --- /dev/null +++ b/ucon64/2.0/src/ucon64_defines.h @@ -0,0 +1,400 @@ +/* +ucon64_defines.h - definitions for uCON64 + +Copyright (c) 2002 - 2003 NoisyB +Copyright (c) 2002 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef UCON64_DEFINES_H +#define UCON64_DEFINES_H + +// Please make sure that NO definition except FALSE has 0 as value! +#if (!defined TRUE || !defined FALSE) +#define FALSE 0 +#define TRUE (!FALSE) +#endif + +#if (!defined MIN || !defined MAX) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#define LIB_VERSION(ver, rel, seq) (((ver) << 16) | ((rel) << 8) | (seq)) +#define NULL_TO_EMPTY(str) ((str) ? (str) : ("")) +//#define RANDOM(min, max) ((rand () % (max - min)) + min) +#define OFFSET(a, offset) ((((unsigned char *) &(a)) + (offset))[0]) + +#define UCON64_UNKNOWN (-1) +#define UCON64_UNKNOWN_S "Unknown" +#define NULL_TO_UNKNOWN_S(str) ((str) ? (str) : (UCON64_UNKNOWN_S)) + +#define UCON64_VERSION_S "2.0.0" + +/* program version counter */ +//#define UCON64_VERSION (200) + +/* version of config file layout */ +#define UCON64_CONFIG_VERSION (207) + +#define MBIT (131072) +#define TOMBIT(x) ((int)(x) / MBIT) +#define TOMBIT_F(x) ((float)(x) / MBIT) + +#define MAXROMSIZE (512 * MBIT) + +#ifndef MAXBUFSIZE +#define MAXBUFSIZE 32768 +#endif // MAXBUFSIZE + +// maximum number of arguments uCON64 takes from the cmdline +#define UCON64_MAX_ARGS 512 + +#define UCON64_OPTION (1000) +#define UCON64_CONSOLE (0) + +// options (consoles) +// these defines shall not exceed 0xffff since they share the same integer +// with the workflow flags (below) in st_getopt2_t +enum +{ + UCON64_3DO = UCON64_CONSOLE + 1, + UCON64_ATA, + UCON64_CD32, + UCON64_CDI, + UCON64_COLECO, + UCON64_DC, + UCON64_GB, + UCON64_GBA, + UCON64_GC, + UCON64_GEN, + UCON64_GP32, + UCON64_INTELLI, + UCON64_JAG, + UCON64_LYNX, + UCON64_MAME, + UCON64_N64, + UCON64_NES, + UCON64_NG, + UCON64_NGP, + UCON64_PCE, + UCON64_PS2, + UCON64_PSX, + UCON64_S16, + UCON64_SAT, + UCON64_SMS, + // don't mix the following with UCON64_GG (Game Genie), used only for --mgdgg + UCON64_GAMEGEAR, + UCON64_SNES, + UCON64_SWAN, + UCON64_VBOY, + UCON64_VEC, + UCON64_XBOX, +}; + +// options +enum +{ + UCON64_1991 = UCON64_OPTION + 1, + UCON64_A, + UCON64_B, + UCON64_B0, + UCON64_B1, + UCON64_BAT, + UCON64_BIN, + UCON64_BIOS, + UCON64_BOT, + UCON64_BS, + UCON64_C, + UCON64_CHK, + UCON64_CODE, + UCON64_COL, + UCON64_CRC, + UCON64_CRCHD, + UCON64_CRP, + UCON64_CS, + UCON64_CTRL, + UCON64_CTRL2, + UCON64_CMNT, + UCON64_DB, + UCON64_DBS, + UCON64_DBUH, + UCON64_DBV, + UCON64_DINT, + UCON64_DMIRR, + UCON64_DNSRT, + UCON64_DUAL, + UCON64_DUMPINFO, + UCON64_E, + UCON64_EROM, + UCON64_F, + UCON64_FDS, + UCON64_FDSL, + UCON64_FFE, + UCON64_FIG, + UCON64_FIGS, + UCON64_FILE, + UCON64_FIND, + UCON64_FINDI, + UCON64_FINDR, + UCON64_FRONTEND, + UCON64_GBX, + UCON64_GD3, + UCON64_GD3S, + UCON64_GG, + UCON64_GGD, + UCON64_GGE, + UCON64_HD, + UCON64_HDN, + UCON64_HELP, + UCON64_HEX, + UCON64_HI, + UCON64_I, + UCON64_ID, + UCON64_IDPPF, + UCON64_INES, + UCON64_INESHD, + UCON64_INS, + UCON64_INSN, + UCON64_INT, + UCON64_INT2, + UCON64_ISPAD, + UCON64_J, + UCON64_K, + UCON64_L, + UCON64_LNX, + UCON64_LOGO, + UCON64_LS, + UCON64_LSD, + UCON64_LSRAM, + UCON64_LSV, + UCON64_LYX, + UCON64_MAPR, + UCON64_MD5, + UCON64_MGD, + UCON64_MGDGG, + UCON64_MGH, + UCON64_MIRR, + UCON64_MKA, + UCON64_MKDAT, + UCON64_MKI, + UCON64_MKIP, + UCON64_MKPPF, + UCON64_MSG, + UCON64_MULTI, +// UCON64_MVS, + UCON64_N, + UCON64_N2, + UCON64_N2GB, + UCON64_NA, + UCON64_NBAK, + UCON64_NBAT, + UCON64_NBS, + UCON64_NCOL, + UCON64_NHD, + UCON64_NHI, + UCON64_NINT, + UCON64_NPPF, + UCON64_NROT, + UCON64_NS, + UCON64_NSWP, + UCON64_NTSC, + UCON64_NVRAM, + UCON64_O, + UCON64_P, + UCON64_PAD, + UCON64_PADHD, + UCON64_PADN, + UCON64_PAL, + UCON64_PARSE, + UCON64_PASOFAMI, + UCON64_PATCH, + UCON64_PATTERN, + UCON64_POKE, + UCON64_PORT, + UCON64_PPF, + UCON64_PRINT, + UCON64_Q, + UCON64_QQ, // already reserved ;-) + UCON64_REGION, + UCON64_RENAME, + UCON64_RROM, + UCON64_RR83, + UCON64_RL, + UCON64_ROM, + UCON64_ROTL, + UCON64_ROTR, + UCON64_RU, + UCON64_S, + UCON64_SAM, + UCON64_SCAN, + UCON64_SCR, + UCON64_SGB, + UCON64_SHA1, + UCON64_SMC, + UCON64_SMD, + UCON64_SMDS, + UCON64_SRAM, + UCON64_SSC, + UCON64_SSIZE, + UCON64_STP, + UCON64_STPN, + UCON64_STRIP, + UCON64_SWAP, + UCON64_SWAP2, + UCON64_SWC, + UCON64_SWCS, + UCON64_SWP, + UCON64_TEST, + UCON64_UFO, + UCON64_UFOS, + UCON64_UNIF, + UCON64_UNSCR, + UCON64_USMS, + UCON64_V, + UCON64_V64, + UCON64_VER, + UCON64_VMS, + UCON64_VRAM, + UCON64_XCD64, + UCON64_XCD64B, + UCON64_XCD64C, + UCON64_XCD64E, + UCON64_XCD64F, + UCON64_XCD64M, + UCON64_XCD64P, + UCON64_XCD64S, + UCON64_XCMC, + UCON64_XCMCM, + UCON64_XCMCT, + UCON64_XDEX, + UCON64_XDJR, + UCON64_XF2A, + UCON64_XF2AB, + UCON64_XF2AC, + UCON64_XF2AMULTI, + UCON64_XF2AS, + UCON64_XFAL, + UCON64_XFALB, + UCON64_XFALC, + UCON64_XFALMULTI, + UCON64_XFALS, + UCON64_XFALM, // actually only necessary for the Windows + UCON64_XFIG, // ports, but might be useful for others too + UCON64_XFIGS, + UCON64_XFIGC, + UCON64_XGBX, + UCON64_XGBXB, + UCON64_XGBXS, + UCON64_XGBXM, + UCON64_XGD3, + UCON64_XGD3R, + UCON64_XGD3S, + UCON64_XGD6, + UCON64_XGD6R, + UCON64_XGD6S, + UCON64_XGG, + UCON64_XGGB, + UCON64_XGGS, + UCON64_XLIT, + UCON64_XMCCL, + UCON64_XMCD, + UCON64_XMD, + UCON64_XMDB, + UCON64_XMDS, + UCON64_XMSG, + UCON64_XPCE, + UCON64_XPL, + UCON64_XPLI, + UCON64_XPLM, + UCON64_XSF, + UCON64_XSFS, + UCON64_XSMC, + UCON64_XSMCR, + UCON64_XSMD, + UCON64_XSMDS, + UCON64_XSWC, + UCON64_XSWC2, + UCON64_XSWC_IO, + UCON64_XSWCR, + UCON64_XSWCS, + UCON64_XSWCC, + UCON64_XV64, + UCON64_Z64, + + UCON64_FORCE63, + UCON64_GUI, + + // Keep these (libdiscmage) options separate + UCON64_DISC = UCON64_OPTION + 250, + UCON64_MKCUE, + UCON64_MKSHEET, + UCON64_MKTOC, + UCON64_RIP, + UCON64_BIN2ISO, + UCON64_ISOFIX, + UCON64_XCDRW, + UCON64_CDMAGE +}; + +/* + uCON64 workflow flags + + WF_PROBE probe for console type + WF_INIT init ROM info (ucon64_init()) necessary + w/o this flag WF_NFO and WF_NFO_AFTER + will be ignored + WF_NFO show info output before processing rom + WF_NFO_AFTER show info output AFTER processing rom + WF_NO_ROM for this option no ROM is required + WF_NO_CRC32 no CRC32 calculation necessary for this option; this + overrides even WF_INIT, WF_NFO and WF_NFO_AFTER + WF_STOP a "stop" option: + - -multi (and -xfalmulti) takes more than one file as + argument, but should be executed only once. + - stop after sending one ROM to a copier ("multizip") + - stop after applying a patch so that the patch file won't + be interpreted as ROM + WF_PAR this option requires a parallel port + WF_USB this option requires a USB port + WF_SERIAL this option requires a serial port + WF_NO_SPLIT this option does not work with split ROMs + WF_DEFAULT same as WF_INIT | WF_PROBE | WF_NFO + + example: + WF_NFO | WF_MFO_AFTER + a ROM is required and info will be shown before and + after it has been processed + + important: + we shift these flags 16 bit because they share the same integer with + those UCON64_ defines in st_getopt2_t +*/ +#define WF_DEFAULT (WF_PROBE | WF_INIT | WF_NFO) +#define WF_PROBE (1) +#define WF_INIT (1 << 1) +#define WF_NFO (1 << 2) +#define WF_STOP (1 << 3) +#define WF_NFO_AFTER (1 << 4) +#define WF_NO_ROM (1 << 5) +#define WF_PAR (1 << 6) +#define WF_USB (1 << 7) +#define WF_SERIAL (1 << 8) +#define WF_NO_CRC32 (1 << 9) +#define WF_NO_SPLIT (1 << 10) +#define WF_SWITCH (1 << 11) + +#endif // UCON64_DEFINES_H diff --git a/ucon64/2.0/src/ucon64_misc.c b/ucon64/2.0/src/ucon64_misc.c new file mode 100644 index 0000000..e6787e2 --- /dev/null +++ b/ucon64/2.0/src/ucon64_misc.c @@ -0,0 +1,2780 @@ +/* +ucon64_misc.c - miscellaneous functions for uCON64 + +Copyright (c) 1999 - 2004 NoisyB +Copyright (c) 2001 - 2004 dbjh +Copyright (c) 2001 Caz +Copyright (c) 2002 - 2003 Jan-Erik Karlsson (Amiga) + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef _WIN32 +#include +#endif +#include "misc/misc.h" +#include "misc/string.h" +#include "misc/property.h" +#include "misc/bswap.h" +#include "misc/chksum.h" +#include "misc/file.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "misc/getopt2.h" // st_getopt2_t +#include "ucon64.h" +#include "ucon64_opts.h" +#include "ucon64_misc.h" +#include "ucon64_dat.h" +#include "console/console.h" +#include "backup/backup.h" +#include "patch/patch.h" + + +/* + uCON64 "workflow" objects + + We want to do things compile-time. Using ucon64_wf is necessary for VC 6. GCC + (3) accepts casts in struct initialisations. +*/ +st_ucon64_obj_t ucon64_wf[] = + { + {0, WF_SWITCH}, // WF_OBJ_ALL_SWITCH + {0, WF_DEFAULT}, // WF_OBJ_ALL_DEFAULT + {0, WF_DEFAULT | WF_NO_SPLIT}, // WF_OBJ_ALL_DEFAULT_NO_SPLIT + {0, WF_STOP}, // WF_OBJ_ALL_STOP + {0, WF_STOP | WF_NO_ROM}, // WF_OBJ_ALL_STOP_NO_ROM + {0, WF_DEFAULT | WF_STOP | WF_NO_ROM}, // WF_OBJ_ALL_DEFAULT_STOP_NO_ROM + {0, WF_INIT}, // WF_OBJ_ALL_INIT + {0, WF_INIT | WF_PROBE}, // WF_OBJ_ALL_INIT_PROBE + {0, WF_INIT | WF_PROBE | WF_STOP}, // WF_OBJ_ALL_INIT_PROBE_STOP, + {0, WF_INIT | WF_PROBE | WF_NO_ROM}, // WF_OBJ_ALL_INIT_PROBE_NO_ROM + {0, WF_INIT | WF_PROBE | WF_NO_SPLIT}, // WF_OBJ_ALL_INIT_PROBE_NO_SPLIT + {0, WF_INIT | WF_PROBE | WF_NO_CRC32}, // WF_OBJ_ALL_INIT_PROBE_NO_CRC32 + {0, WF_INIT | WF_NO_SPLIT}, // WF_OBJ_ALL_INIT_NO_SPLIT + + {UCON64_DC, WF_SWITCH}, // WF_OBJ_DC_SWITCH + {UCON64_DC, WF_DEFAULT}, // WF_OBJ_DC_DEFAULT + {UCON64_DC, WF_NO_ROM}, // WF_OBJ_DC_NO_ROM + {UCON64_GB, WF_SWITCH}, // WF_OBJ_GB_SWITCH + {UCON64_GB, WF_DEFAULT}, // WF_OBJ_GB_DEFAULT + {UCON64_GBA, WF_SWITCH}, // WF_OBJ_GBA_SWITCH + {UCON64_GBA, WF_DEFAULT}, // WF_OBJ_GBA_DEFAULT + {UCON64_GEN, WF_SWITCH}, // WF_OBJ_GEN_SWITCH + {UCON64_GEN, WF_DEFAULT}, // WF_OBJ_GEN_DEFAULT + {UCON64_GEN, WF_DEFAULT | WF_NO_SPLIT}, // WF_OBJ_GEN_DEFAULT_NO_SPLIT + {UCON64_JAG, WF_SWITCH}, // WF_OBJ_JAG_SWITCH + {UCON64_LYNX, WF_SWITCH}, // WF_OBJ_LYNX_SWITCH + {UCON64_LYNX, WF_DEFAULT}, // WF_OBJ_LYNX_DEFAULT + {UCON64_N64, WF_SWITCH}, // WF_OBJ_N64_SWITCH + {UCON64_N64, WF_DEFAULT}, // WF_OBJ_N64_DEFAULT + {UCON64_N64, WF_INIT | WF_PROBE}, // WF_OBJ_N64_INIT_PROBE + {UCON64_NG, WF_SWITCH}, // WF_OBJ_NG_SWITCH + {UCON64_NG, WF_DEFAULT}, // WF_OBJ_NG_DEFAULT + {UCON64_NES, WF_SWITCH}, // WF_OBJ_NES_SWITCH + {UCON64_NES, WF_DEFAULT}, // WF_OBJ_NES_DEFAULT + {UCON64_NGP, WF_SWITCH}, // WF_OBJ_NGP_SWITCH + {UCON64_PCE, WF_SWITCH}, // WF_OBJ_PCE_SWITCH + {UCON64_PCE, WF_DEFAULT}, // WF_OBJ_PCE_DEFAULT + {UCON64_PSX, WF_SWITCH}, // WF_OBJ_PSX_SWITCH + {UCON64_SMS, WF_SWITCH}, // WF_OBJ_SMS_SWITCH + {UCON64_SMS, WF_DEFAULT | WF_NO_SPLIT}, // WF_OBJ_SMS_DEFAULT_NO_SPLIT + {UCON64_SNES, WF_SWITCH}, // WF_OBJ_SNES_SWITCH + {UCON64_SNES, WF_DEFAULT}, // WF_OBJ_SNES_DEFAULT + {UCON64_SNES, WF_DEFAULT | WF_NO_SPLIT}, // WF_OBJ_SNES_DEFAULT_NO_SPLIT + {UCON64_SNES, WF_NO_ROM}, // WF_OBJ_SNES_NO_ROM + {UCON64_SNES, WF_INIT | WF_PROBE}, // WF_OBJ_SNES_INIT_PROBE + {UCON64_SWAN, WF_SWITCH}, // WF_OBJ_SWAN_SWITCH + + {UCON64_N64, WF_STOP | WF_NO_ROM}, // WF_OBJ_N64_STOP_NO_ROM + {UCON64_N64, WF_DEFAULT | WF_STOP}, // WF_OBJ_N64_DEFAULT_STOP + {UCON64_N64, WF_DEFAULT | WF_STOP | WF_NO_ROM}, // WF_OBJ_N64_DEFAULT_STOP_NO_ROM + {UCON64_GEN, WF_STOP | WF_NO_ROM}, // WF_OBJ_GEN_STOP_NO_ROM + {UCON64_GEN, WF_DEFAULT | WF_STOP | WF_NO_SPLIT | WF_NO_ROM}, // WF_OBJ_GEN_DEFAULT_STOP_NO_SPLIT_NO_ROM + {UCON64_GBA, WF_STOP | WF_NO_ROM}, // WF_OBJ_GBA_STOP_NO_ROM + {UCON64_GBA, WF_DEFAULT | WF_STOP}, // WF_OBJ_GBA_DEFAULT_STOP + {UCON64_GBA, WF_DEFAULT | WF_STOP | WF_NO_ROM}, // WF_OBJ_GBA_DEFAULT_STOP_NO_ROM + {UCON64_SNES, WF_STOP | WF_NO_ROM}, // WF_OBJ_SNES_STOP_NO_ROM + {UCON64_SNES, WF_DEFAULT | WF_STOP | WF_NO_ROM}, // WF_OBJ_SNES_DEFAULT_STOP_NO_ROM + {UCON64_SNES, WF_DEFAULT | WF_STOP | WF_NO_SPLIT | WF_NO_ROM}, // WF_OBJ_SNES_DEFAULT_STOP_NO_SPLIT_NO_ROM + {UCON64_GB, WF_STOP | WF_NO_ROM}, // WF_OBJ_GB_STOP_NO_ROM + {UCON64_GB, WF_DEFAULT | WF_STOP | WF_NO_ROM}, // WF_OBJ_GB_DEFAULT_STOP_NO_ROM + {UCON64_LYNX, WF_STOP | WF_NO_ROM}, // WF_OBJ_LYNX_STOP_NO_ROM + {UCON64_PCE, WF_DEFAULT | WF_STOP | WF_NO_SPLIT | WF_NO_ROM}, // WF_OBJ_PCE_DEFAULT_STOP_NO_SPLIT_NO_ROM + {UCON64_NGP, WF_STOP | WF_NO_ROM}, // WF_OBJ_NGP_STOP_NO_ROM + {UCON64_NGP, WF_DEFAULT | WF_STOP | WF_NO_ROM}, // WF_OBJ_NGP_DEFAULT_STOP_NO_ROM + {UCON64_NES, WF_STOP | WF_NO_ROM}, // WF_OBJ_NES_STOP_NO_ROM + {UCON64_NES, WF_DEFAULT | WF_STOP | WF_NO_SPLIT}, // WF_OBJ_NES_DEFAULT_STOP_NO_SPLIT + {UCON64_SMS, WF_STOP | WF_NO_ROM}, // WF_OBJ_SMS_STOP_NO_ROM + {UCON64_SMS, WF_DEFAULT | WF_STOP | WF_NO_SPLIT | WF_NO_ROM}, // WF_OBJ_SMS_DEFAULT_STOP_NO_SPLIT_NO_ROM + + {UCON64_GC, WF_SWITCH}, // WF_OBJ_GC_SWITCH + {UCON64_S16, WF_SWITCH}, // WF_OBJ_S16_SWITCH + {UCON64_ATA, WF_SWITCH}, // WF_OBJ_ATA_SWITCH + {UCON64_COLECO, WF_SWITCH}, // WF_OBJ_COLECO_SWITCH + {UCON64_VBOY, WF_SWITCH}, // WF_OBJ_VBOY_SWITCH + {UCON64_VEC, WF_SWITCH}, // WF_OBJ_VEC_SWITCH + {UCON64_INTELLI, WF_SWITCH}, // WF_OBJ_INTELLI_SWITCH + {UCON64_GP32, WF_SWITCH}, // WF_OBJ_GP32_SWITCH + {UCON64_PS2, WF_SWITCH}, // WF_OBJ_PS2_SWITCH + {UCON64_XBOX, WF_SWITCH}, // WF_OBJ_XBOX_SWITCH + {UCON64_SAT, WF_SWITCH}, // WF_OBJ_SAT_SWITCH + {UCON64_3DO, WF_SWITCH}, // WF_OBJ_3DO_SWITCH + {UCON64_CD32, WF_SWITCH}, // WF_OBJ_CD32_SWITCH + {UCON64_CDI, WF_SWITCH}, // WF_OBJ_CDI_SWITCH + }; + +#ifdef USE_DISCMAGE +#ifdef DLOPEN +#include "misc/dlopen.h" + +static void *libdm; +static uint32_t (*dm_get_version_ptr) (void) = NULL; +static const char *(*dm_get_version_s_ptr) (void) = NULL; +static void (*dm_set_gauge_ptr) (void (*) (int, int)) = NULL; +static void (*dm_nfo_ptr) (const dm_image_t *, int, int) = NULL; + +static FILE *(*dm_fdopen_ptr) (dm_image_t *, int, const char *) = NULL; +static dm_image_t *(*dm_open_ptr) (const char *, uint32_t) = NULL; +static dm_image_t *(*dm_reopen_ptr) (const char *, uint32_t, dm_image_t *) = NULL; +static int (*dm_close_ptr) (dm_image_t *) = NULL; + +static int (*dm_disc_read_ptr) (const dm_image_t *) = NULL; +static int (*dm_disc_write_ptr) (const dm_image_t *) = NULL; + +static int (*dm_read_ptr) (char *, int, int, const dm_image_t *) = NULL; +static int (*dm_write_ptr) (const char *, int, int, const dm_image_t *) = NULL; + +static dm_image_t *(*dm_toc_read_ptr) (dm_image_t *, const char *) = NULL; +static int (*dm_toc_write_ptr) (const dm_image_t *) = NULL; + +static dm_image_t *(*dm_cue_read_ptr) (dm_image_t *, const char *) = NULL; +static int (*dm_cue_write_ptr) (const dm_image_t *) = NULL; + +static int (*dm_rip_ptr) (const dm_image_t *, int, uint32_t) = NULL; +#endif // DLOPEN + +const st_getopt2_t libdm_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "All disc-based consoles", + NULL + }, + { + "disc", 0, 0, UCON64_DISC, + NULL, "force recognition; NEEDED", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "rip", 1, 0, UCON64_RIP, + "N", "rip/dump track N from IMAGE", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, +#if 0 + { + "filerip", 1, 0, UCON64_FILERIP, + "N", "rip/dump files from a track N in IMAGE", + NULL + }, + { + "cdmage", 1, 0, UCON64_CDMAGE, + "N", "like " OPTION_LONG_S "rip but writes always (padded) sectors with 2352 Bytes;\n" + "this is what CDmage would do", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, +#endif + { + "bin2iso", 1, 0, UCON64_BIN2ISO, + "N", "convert track N to ISO (if possible) by resizing\n" + "sectors to 2048 Bytes", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "isofix", 1, 0, UCON64_ISOFIX, + "N", "fix corrupted track N (if possible)\n" + "if PVD points to a bad DR offset it will add padding data\n" + "so actual DR gets located in right absolute address", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "mkcue", 0, 0, UCON64_MKCUE, + NULL, "generate CUE sheet for IMAGE or existing TOC sheet", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "mktoc", 0, 0, UCON64_MKTOC, + NULL, "generate TOC sheet for IMAGE or existing CUE sheet", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + // hidden option + "mksheet", 0, 0, UCON64_MKSHEET, + NULL, /* "same as " OPTION_LONG_S "mktoc and " OPTION_LONG_S "mkcue" */ NULL, + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; +#endif // USE_DISCMAGE + + +/* + This is a string pool. gcc 2.9x generates something like this itself, but it + seems gcc 3.x does not. By using a string pool the executable will be + smaller than without it. + It's also handy in order to be consistent with messages. +*/ +const char *ucon64_msg[] = + { + "ERROR: Communication with backup unit failed\n" // PARPORT_ERROR + "TIP: Check cables and connection\n" + " Turn the backup unit off and on\n" +// " Split ROMs must be joined first\n" // handled with WF_NO_SPLIT + " Use " OPTION_LONG_S "port={3bc, 378, 278, ...} to specify a parallel port address\n" + " Set the port to SPP (standard, normal) mode in your BIOS as some backup\n" + " units do not support EPP and ECP style parallel ports\n" + " Read the backup unit's manual\n", + + "ERROR: Could not auto detect the right ROM/IMAGE/console type\n" // CONSOLE_ERROR + "TIP: If this is a ROM or CD IMAGE you might try to force the recognition\n" + " The force recognition option for SNES would be " OPTION_LONG_S "snes\n", + + "Wrote output to: %s\n", // WROTE + "ERROR: Can't open \"%s\" for reading\n", // OPEN_READ_ERROR + "ERROR: Can't open \"%s\" for writing\n", // OPEN_WRITE_ERROR + "ERROR: Can't read from \"%s\"\n", // READ_ERROR + "ERROR: Can't write to \"%s\"\n", // WRITE_ERROR + "ERROR: Not enough memory for buffer (%d bytes)\n", // BUFFER_ERROR + "ERROR: Not enough memory for ROM buffer (%d bytes)\n", // ROM_BUFFER_ERROR + "ERROR: Not enough memory for file buffer (%d bytes)\n", // FILE_BUFFER_ERROR + "DAT info: No ROM with 0x%08x as checksum found\n", // DAT_NOT_FOUND + "WARNING: Support for DAT files is disabled, because \"ucon64_datdir\" (either\n" // DAT_NOT_ENABLED + " in the configuration file or the environment) points to an incorrect\n" + " directory. Read the FAQ for more information.\n", + "Reading config file %s\n", // READ_CONFIG_FILE + "NOTE: %s not found or too old, support for discmage disabled\n", // NO_LIB + NULL + }; + +const st_getopt2_t unknown_usage[] = + { + {NULL, 0, 0, 0, NULL, "Unknown backup unit/emulator", NULL}, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + gc_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Nintendo Game Cube/Panasonic Gamecube Q" + /*"2001/2002 Nintendo http://www.nintendo.com"*/, + NULL + }, + { + "gc", 0, 0, UCON64_GC, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_GC_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + s16_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Sega System 16(A/B)/Sega System 18/dual 68000" + /*"1987/19XX/19XX SEGA http://www.sega.com"*/, + NULL + }, + { + "s16", 0, 0, UCON64_S16, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_S16_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + atari_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Atari VCS 2600(aka Stella)/Atari 5200 SuperSystem/Atari CX7800/Atari 2600 Jr" + /*"1977/1982/1984/1986 Atari"*/, + NULL + }, + { + "ata", 0, 0, UCON64_ATA, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_ATA_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + coleco_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "ColecoVision"/*"1982"*/, + NULL + }, + { + "coleco", 0, 0, UCON64_COLECO, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_COLECO_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + vboy_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Nintendo Virtual Boy"/*"19XX Nintendo http://www.nintendo.com"*/, + NULL + }, + { + "vboy", 0, 0, UCON64_VBOY, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_VBOY_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + vectrex_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Vectrex"/*"1982"*/, + NULL + }, + { + "vec", 0, 0, UCON64_VEC, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_VEC_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + intelli_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Intellivision"/*"1979 Mattel"*/, + NULL + }, + { + "intelli", 0, 0, UCON64_INTELLI, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_INTELLI_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + gp32_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "GP32 Game System"/*"2002 Gamepark http://www.gamepark.co.kr"*/, + NULL + }, + { + "gp32", 0, 0, UCON64_GP32, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_GP32_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + ps2_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Playstation 2"/*"2000 Sony http://www.playstation.com"*/, + NULL + }, + { + "ps2", 0, 0, UCON64_PS2, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_PS2_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + xbox_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "XBox"/*"2001 Microsoft http://www.xbox.com"*/, + NULL + }, + { + "xbox", 0, 0, UCON64_XBOX, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_XBOX_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + sat_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Saturn"/*"1994 SEGA http://www.sega.com"*/, + NULL + }, + { + "sat", 0, 0, UCON64_SAT, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_SAT_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + real3do_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Real3DO"/*"1993 Panasonic/Goldstar/Philips"*/, + NULL + }, + { + "3do", 0, 0, UCON64_3DO, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_3DO_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + cd32_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "CD32"/*"1993 Commodore"*/, + NULL + }, + { + "cd32", 0, 0, UCON64_CD32, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_CD32_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + cdi_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "CD-i"/*"1991 Philips"*/, + NULL + }, + { + "cdi", 0, 0, UCON64_CDI, + NULL, "force recognition", + &ucon64_wf[WF_OBJ_CDI_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + vc4000_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Interton VC4000"/*"~1980"*/, + NULL + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + odyssey2_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "G7400+/Odyssey2"/*"1978"*/, + NULL + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + channelf_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "FC Channel F"/*"1976"*/, + NULL + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + odyssey_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Magnavox Odyssey"/*"1972 Ralph Baer (USA)"*/, + NULL + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + gamecom_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Game.com"/*"? Tiger"*/, + NULL + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }, + mame_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "M.A.M.E. (Multiple Arcade Machine Emulator)", + NULL + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + +#if 0 +Adv. Vision +Arcadia +Astrocade +Indrema +Microvision +N-Gage 2003 Nokia http://www.n-gage.com +Nuon +RCA Studio 2 +RDI Halcyon +Telstar +XE System +#endif + +const st_getopt2_t ucon64_options_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Options", + NULL + }, + { + "o", 1, 0, UCON64_O, + "DIRECTORY", "specify output directory", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "nbak", 0, 0, UCON64_NBAK, + NULL, "prevents backup files (*.BAK)", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, +#ifdef USE_ANSI_COLOR + { + "ncol", 0, 0, UCON64_NCOL, + NULL, "disable ANSI colors in output", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, +#endif +#if defined USE_PARALLEL || defined USE_USB + { + "port", 1, 0, UCON64_PORT, + "PORT", "specify " +#ifdef USE_USB + "USB" +#endif +#if defined USE_PARALLEL && defined USE_USB + " or " +#endif +#ifdef USE_PARALLEL + "parallel" +#endif + " PORT={" +#ifdef USE_USB + "USB0, USB1, " +#endif +#ifdef USE_PARALLEL + "3bc, 378, 278, " +#endif + "...}", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, +#endif // defined USE_PARALLEL || defined USE_USB + { + "hdn", 1, 0, UCON64_HDN, + "N", "force ROM has backup unit/emulator header with size of N Bytes", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "hd", 0, 0, UCON64_HD, + NULL, "same as " OPTION_LONG_S "hdn=512\n" + "most backup units use a header with a size of 512 Bytes", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "nhd", 0, 0, UCON64_NHD, + NULL, "force ROM has no backup unit/emulator header", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "ns", 0, 0, UCON64_NS, + NULL, "force ROM is not split", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "e", 0, 0, UCON64_E, +#ifdef __MSDOS__ + NULL, "emulate/run ROM (check ucon64.cfg for all Emulator settings)", +#else + NULL, "emulate/run ROM (check .ucon64rc for all Emulator settings)", +#endif + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "crc", 0, 0, UCON64_CRC, + NULL, "show CRC32 value of ROM", +#if 0 + "; this will also force calculation for\n" + "files bigger than %d Bytes (%.4f Mb)" +#endif + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_NO_CRC32] + }, + { + "sha1", 0, 0, UCON64_SHA1, + NULL, "show SHA1 value of ROM", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_NO_CRC32] + }, + { + "md5", 0, 0, UCON64_MD5, + NULL, "show MD5 value of ROM", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_NO_CRC32] + }, + { + "ls", 0, 0, UCON64_LS, + NULL, "generate ROM list for all recognized ROMs", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE] + }, + { + "lsv", 0, 0, UCON64_LSV, + NULL, "like " OPTION_LONG_S "ls but more verbose", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE] + }, + { + "hex", 2, 0, UCON64_HEX, +#ifdef __MSDOS__ + "ST", "show ROM as hexdump; use \"ucon64 " OPTION_LONG_S "hex ...|more\"" +#else + "ST", "show ROM as hexdump; use \"ucon64 " OPTION_LONG_S "hex ...|less\"" // less is more ;-) +#endif + "\nST is the optional start value in bytes", + NULL + }, + { + "dual", 2, 0, UCON64_DUAL, // TODO: Think of a decent name - dbjh +#ifdef __MSDOS__ + "ST", "show ROM as dualdump; use \"ucon64 " OPTION_LONG_S "dual ...|more\"", +#else + "ST", "show ROM as dualdump; use \"ucon64 " OPTION_LONG_S "dual ...|less\"", +#endif + NULL + }, + { + "code", 2, 0, UCON64_CODE, +#ifdef __MSDOS__ + "ST", "show ROM as code; use \"ucon64 " OPTION_LONG_S "code ...|more\"", +#else + "ST", "show ROM as code; use \"ucon64 " OPTION_LONG_S "code ...|less\"", +#endif + NULL + }, + { + "print", 2, 0, UCON64_PRINT, +#ifdef __MSDOS__ + "ST", "show ROM in printable characters; use \"ucon64 " OPTION_LONG_S "print ...|more\"", +#else + "ST", "show ROM in printable characters; use \"ucon64 " OPTION_LONG_S "print ...|less\"", +#endif + NULL + }, + { + "find", 1, 0, UCON64_FIND, + "STRING", "find STRING in ROM (wildcard: '?')", + &ucon64_wf[WF_OBJ_ALL_INIT] + }, + { + "findi", 1, 0, UCON64_FINDI, + "STR", "like " OPTION_LONG_S "find but ignores the case of alpha bytes", + &ucon64_wf[WF_OBJ_ALL_INIT] + }, + { + "findr", 1, 0, UCON64_FINDR, + "STR", "like " OPTION_LONG_S "find but looks also for shifted/relative similarities\n" + "(wildcard: disabled)", + &ucon64_wf[WF_OBJ_ALL_INIT] + }, + { + "c", 1, 0, UCON64_C, + "FILE", "compare FILE with ROM for differences", + NULL + }, + { + "cs", 1, 0, UCON64_CS, + "FILE", "compare FILE with ROM for similarities", + NULL + }, + { + "help", 0, 0, UCON64_HELP, + NULL, "display this help and exit", + &ucon64_wf[WF_OBJ_ALL_STOP] + }, + { + "version", 0, 0, UCON64_VER, + NULL, "output version information and exit", + &ucon64_wf[WF_OBJ_ALL_STOP] + }, + { + "q", 0, 0, UCON64_Q, + NULL, "be quiet (don't show ROM info)", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, +#if 0 + { + "qq", 0, 0, UCON64_QQ, + NULL, "be even more quiet", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, +#endif + { + "v", 0, 0, UCON64_V, + NULL, "be more verbose (show backup unit headers also)", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + +const st_getopt2_t ucon64_options_without_usage[] = + { + { + "crchd", 0, 0, UCON64_CRCHD, // backward compat. + NULL, NULL, + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_NO_CRC32] + }, + { + "file", 1, 0, UCON64_FILE, // obsolete? + NULL, NULL, + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "frontend", 0, 0, UCON64_FRONTEND, // no usage? + NULL, NULL, + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "?", 0, 0, UCON64_HELP, // same as --help + NULL, NULL, + &ucon64_wf[WF_OBJ_ALL_STOP] + }, + { + "h", 0, 0, UCON64_HELP, // same as --help + NULL, NULL, + &ucon64_wf[WF_OBJ_ALL_STOP] + }, + { + "id", 0, 0, UCON64_ID, // currently only used in snes.c + NULL, NULL, + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "rom", 0, 0, UCON64_ROM, // obsolete? + NULL, NULL, + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + { + "83", 0, 0, UCON64_RR83, // is now "rr83" + NULL, NULL, + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE_NO_SPLIT] + }, +#if 0 + { + "xcdrw", 0, 0, UCON64_XCDRW, // obsolete + NULL, NULL, + &ucon64_wf[WF_OBJ_ALL_DEFAULT_STOP_NO_ROM] + }, + { + "cdmage", 1, 0, UCON64_CDMAGE, // obsolete + NULL, NULL, + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, +#endif + // these consoles are (still) not supported + { + "3do", 0, 0, UCON64_3DO, + NULL, NULL, + &ucon64_wf[WF_OBJ_3DO_SWITCH] + }, + { + "gp32", 0, 0, UCON64_GP32, + NULL, NULL, + &ucon64_wf[WF_OBJ_GP32_SWITCH] + }, + { + "intelli", 0, 0, UCON64_INTELLI, + NULL, NULL, + &ucon64_wf[WF_OBJ_INTELLI_SWITCH] + }, + { + "ps2", 0, 0, UCON64_PS2, + NULL, NULL, + &ucon64_wf[WF_OBJ_PS2_SWITCH] + }, + { + "s16", 0, 0, UCON64_S16, + NULL, NULL, + &ucon64_wf[WF_OBJ_S16_SWITCH] + }, + { + "sat", 0, 0, UCON64_SAT, + NULL, NULL, + &ucon64_wf[WF_OBJ_SAT_SWITCH] + }, + { + "vboy", 0, 0, UCON64_VBOY, + NULL, NULL, + &ucon64_wf[WF_OBJ_VBOY_SWITCH] + }, + { + "vec", 0, 0, UCON64_VEC, + NULL, NULL, + &ucon64_wf[WF_OBJ_VEC_SWITCH] + }, + { + "xbox", 0, 0, UCON64_XBOX, + NULL, NULL, + &ucon64_wf[WF_OBJ_XBOX_SWITCH] + }, + { + "coleco", 0, 0, UCON64_COLECO, + NULL, NULL, + &ucon64_wf[WF_OBJ_COLECO_SWITCH] + }, + { + "gc", 0, 0, UCON64_GC, + NULL, NULL, + &ucon64_wf[WF_OBJ_GC_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + +const st_getopt2_t ucon64_padding_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Padding", + NULL + }, + { + "ispad", 0, 0, UCON64_ISPAD, + NULL, "check if ROM is padded", + &ucon64_wf[WF_OBJ_ALL_INIT_NO_SPLIT] + }, + { + "pad", 0, 0, UCON64_PAD, + NULL, "pad ROM to next Mb", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "p", 0, 0, UCON64_P, + NULL, "same as " OPTION_LONG_S "pad", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "padn", 1, 0, UCON64_PADN, + "N", "pad ROM to N Bytes (put Bytes with value 0x00 after end)", + &ucon64_wf[WF_OBJ_ALL_DEFAULT] + }, + { + "strip", 1, 0, UCON64_STRIP, + "N", "strip N Bytes from end of ROM", + NULL + }, + { + "stpn", 1, 0, UCON64_STPN, + "N", "strip N Bytes from start of ROM", + NULL + }, + { + "stp", 0, 0, UCON64_STP, + NULL, "same as " OPTION_LONG_S "stpn=512\n" + "most backup units use a header with a size of 512 Bytes", + NULL + }, + { + "insn", 1, 0, UCON64_INSN, + "N", "insert N Bytes (0x00) before ROM", + NULL + }, + { + "ins", 0, 0, UCON64_INS, + NULL, "same as " OPTION_LONG_S "insn=512\n" + "most backup units use a header with a size of 512 Bytes", + NULL + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + +const st_getopt2_t ucon64_patching_usage[] = + { + { + NULL, 0, 0, 0, + NULL, "Patching", + NULL + }, + { + "poke", 1, 0, UCON64_POKE, + "OFF:V", "change byte at file offset OFF to value V (both in hexadecimal)", + NULL + }, + { + "pattern", 1, 0, UCON64_PATTERN, + "FILE", "change ROM based on patterns specified in FILE", + &ucon64_wf[WF_OBJ_ALL_INIT_PROBE] + }, + { + "patch", 1, 0, UCON64_PATCH, + "PATCH", "specify the PATCH for the following options\n" + "use this option or uCON64 expects the last commandline\n" + "argument to be the name of the PATCH file", + &ucon64_wf[WF_OBJ_ALL_SWITCH] + }, + {NULL, 0, 0, 0, NULL, NULL, NULL} + }; + +char *ucon64_temp_file = NULL; +int (*ucon64_testsplit_callback) (const char *filename) = NULL; + +// _publisher_ strings for SNES, GB, GBC and GBA games +const char *nintendo_maker[NINTENDO_MAKER_LEN] = + { + NULL, "Nintendo", "Rocket Games/Ajinomoto", "Imagineer-Zoom", "Gray Matter", + "Zamuse", "Falcom", NULL, "Capcom", "Hot B Co.", + "Jaleco", "Coconuts Japan", "Coconuts Japan/G.X.Media", + "Micronet", "Technos", + "Mebio Software", "Shouei System", "Starfish", NULL, "Mitsui Fudosan/Dentsu", + NULL, "Warashi Inc.", NULL, "Nowpro", NULL, + "Game Village", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, // 0Z + NULL, "Starfish", "Infocom", "Electronic Arts Japan", NULL, + "Cobra Team", "Human/Field", "KOEI", "Hudson Soft", "S.C.P./Game Village", + "Yanoman", NULL, "Tecmo Products", "Japan Glary Business", "Forum/OpenSystem", + "Virgin Games (Japan)", "SMDE", NULL, NULL, "Daikokudenki", + NULL, NULL, NULL, NULL, NULL, + "Creatures Inc.", "TDK Deep Impresion", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, // 1Z + "Destination Software/KSS", "Sunsoft/Tokai Engineering", + "POW (Planning Office Wada)/VR 1 Japan", "Micro World", NULL, + "San-X", "Enix", "Loriciel/Electro Brain", "Kemco Japan", "Seta Co., Ltd.", + "Culture Brain", NULL, "Palsoft", "Visit Co., Ltd.", "Intec", + "System Sacom", "Poppo", "Ubisoft Japan", NULL, "Media Works", + "NEC InterChannel", "Tam", "Gajin/Jordan", "Smilesoft", NULL, + NULL, "Mediakite", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, // 2Z + "Viacom", "Carrozzeria", "Dynamic", NULL, "Magifact", + "Hect", "Codemasters", "Taito/GAGA Communications", "Laguna", + "Telstar Fun & Games/Event/Taito", + NULL, "Arcade Zone Ltd.", "Entertainment International/Empire Software", "Loriciel", + "Gremlin Graphics", + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, // 3Z + "Seika Corp.", "UBI SOFT Entertainment Software", "Sunsoft US", NULL, "Life Fitness", + NULL, "System 3", "Spectrum Holobyte", NULL, "IREM", + NULL, "Raya Systems", "Renovation Products", "Malibu Games", NULL, + "Eidos/U.S. Gold", "Playmates Interactive", NULL, NULL, "Fox Interactive", + "Time Warner Interactive", NULL, NULL, NULL, NULL, + NULL, "Disney Interactive", NULL, "Black Pearl", NULL, + "Advanced Productions", NULL, NULL, "GT Interactive", "RARE", + "Crave Entertainment", // 4Z + "Absolute Entertainment", "Acclaim", "Activision", "American Sammy", "Take 2/GameTek", + "Hi Tech", "LJN Ltd.", NULL, "Mattel", NULL, + "Mindscape/Red Orb Entertainment", "Romstar", "Taxan", "Midway/Tradewest", NULL, + "American Softworks Corp.", "Majesco Sales Inc.", "3DO", NULL, NULL, + "Hasbro", "NewKidCo", "Telegames", "Metro3D", NULL, + "Vatical Entertainment", "LEGO Media", NULL, "Xicat Interactive", "Cryo Interactive", + NULL, NULL, "Red Storm Entertainment", "Microids", NULL, + "Conspiracy/Swing", // 5Z + "Titus", "Virgin Interactive", "Maxis", NULL, "LucasArts Entertainment", + NULL, NULL, "Ocean", NULL, "Electronic Arts", + NULL, "Laser Beam", NULL, NULL, "Elite Systems", + "Electro Brain", "The Learning Company", "BBC", NULL, "Software 2000", + NULL, "BAM! Entertainment", "Studio 3", NULL, NULL, + NULL, "Classified Games", NULL, "TDK Mediactive", NULL, + "DreamCatcher", "JoWood Produtions", "SEGA", "Wannado Edition", + "LSP (Light & Shadow Prod.)", + "ITE Media", // 6Z + "Infogrames", "Interplay", "JVC (US)", "Parker Brothers", NULL, + "SCI (Sales Curve Interactive)/Storm", NULL, NULL, "THQ Software", "Accolade Inc.", + "Triffix Entertainment", NULL, "Microprose Software", + "Universal Interactive/Sierra/Simon & Schuster", NULL, + "Kemco", "Rage Software", "Encore", NULL, "Zoo", + "BVM", "Simon & Schuster Interactive", "Asmik Ace Entertainment Inc./AIA", + "Empire Interactive", NULL, + NULL, "Jester Interactive", NULL, NULL, "Scholastic", + "Ignition Entertainment", NULL, "Stadlbauer", NULL, NULL, + NULL, // 7Z + "Misawa", "Teichiku", "Namco Ltd.", "LOZC", "KOEI", + NULL, "Tokuma Shoten Intermedia", "Tsukuda Original", "DATAM-Polystar", NULL, + NULL, "Bulletproof Software", "Vic Tokai Inc.", NULL, "Character Soft", + "I'Max", "Saurus", NULL, NULL, "General Entertainment", + NULL, NULL, "I'Max", "Success", NULL, + "SEGA Japan", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, // 8Z + "Takara", "Chun Soft", "Video System Co., Ltd./McO'River", "BEC", NULL, + "Varie", "Yonezawa/S'pal", "Kaneko", NULL, "Victor Interactive Software/Pack in Video", + "Nichibutsu/Nihon Bussan", "Tecmo", "Imagineer", NULL, NULL, + "Nova", "Den'Z", "Bottom Up", NULL, "TGL (Technical Group Laboratory)", + NULL, "Hasbro Japan", NULL, "Marvelous Entertainment", NULL, + "Keynet Inc.", "Hands-On Entertainment", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, // 9Z + "Telenet", "Hori", NULL, NULL, "Konami", + "K.Amusement Leasing Co.", "Kawada", "Takara", NULL, "Technos Japan Corp.", + "JVC (Europe/Japan)/Victor Musical Industries", NULL, "Toei Animation", "Toho", NULL, + "Namco", "Media Rings Corp.", "J-Wing", NULL, "Pioneer LDC", + "KID", "Mediafactory", NULL, NULL, NULL, + "Infogrames Hudson", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, // AZ + "Acclaim Japan", "ASCII Co./Nexoft" /*/Activision*/, "Bandai", NULL, "Enix", + NULL, "HAL Laboratory/Halken", "SNK", NULL, "Pony Canyon Hanbai", + "Culture Brain", "Sunsoft", "Toshiba EMI", "Sony Imagesoft", NULL, + "Sammy", "Magical", "Visco", NULL, "Compile", + NULL, "MTO Inc.", NULL, "Sunrise Interactive", NULL, + "Global A Entertainment", "Fuuki", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, // BZ + "Taito", NULL, "Kemco", "Square", "Tokuma Shoten", + "Data East", "Tonkin House", NULL, "KOEI", NULL, + "Konami/Ultra/Palcom", "NTVIC/VAP", "Use Co., Ltd.", "Meldac", + "Pony Canyon (Japan)/FCI (US)", + "Angel/Sotsu Agency/Sunrise", "Yumedia/Aroma Co., Ltd.", NULL, NULL, "Boss", + "Axela/Crea-Tech", "Sekaibunka-Sha/Sumire kobo/Marigul Management Inc.", + "Konami Computer Entertainment Osaka", NULL, NULL, + "Enterbrain", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, // CZ + "Taito/Disco", "Sofel", "Quest Corp.", "Sigma", "Ask Kodansha", + NULL, "Naxat", "Copya System", "Capcom Co., Ltd.", "Banpresto", + "TOMY", "Acclaim/LJN Japan", NULL, "NCS", "Human Entertainment", + "Altron", "Jaleco", "Gaps Inc.", NULL, NULL, + NULL, NULL, NULL, "Elf", NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, // DZ + "Jaleco", NULL, "Yutaka", "Varie", "T&ESoft", + "Epoch Co., Ltd.", NULL, "Athena", "Asmik", "Natsume", + "King Records", "Atlus", "Epic/Sony Records (Japan)", NULL, + "IGS (Information Global Service)", + NULL, "Chatnoir", "Right Stuff", NULL, NULL, + NULL, "Spike", "Konami Computer Entertainment Tokyo", "Alphadream Corp.", NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, // EZ + "A Wave", "Motown Software", "Left Field Entertainment", "Extreme Ent. Grp.", + "TecMagik", + NULL, NULL, NULL, NULL, "Cybersoft", + NULL, "Psygnosis", NULL, NULL, "Davidson/Western Tech.", + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, // FZ + NULL, "PCCW Japan", NULL, NULL, "KiKi Co. Ltd.", + "Open Sesame Inc.", "Sims", "Broccoli", "Avex", NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, // GZ + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, // HZ + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, "Yojigen", NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL + }; // IZ + + +#ifdef USE_DISCMAGE +int +ucon64_load_discmage (void) +{ + uint32_t version; +#ifdef DLOPEN + get_property_fname (ucon64.configfile, "discmage_path", ucon64.discmage_path, ""); + + // if ucon64.discmage_path points to an existing file then load it + if (!access (ucon64.discmage_path, F_OK)) + { + libdm = open_module (ucon64.discmage_path); + + dm_get_version_ptr = (uint32_t (*) (void)) get_symbol (libdm, "dm_get_version"); + version = dm_get_version_ptr (); + if (version < LIB_VERSION (UCON64_DM_VERSION_MAJOR, + UCON64_DM_VERSION_MINOR, + UCON64_DM_VERSION_STEP)) + { + printf ("WARNING: Your libdiscmage is too old (%u.%u.%u)\n" + " You need at least version %u.%u.%u\n\n", + (unsigned int) version >> 16, + (unsigned int) ((version >> 8) & 0xff), + (unsigned int) (version & 0xff), + UCON64_DM_VERSION_MAJOR, + UCON64_DM_VERSION_MINOR, + UCON64_DM_VERSION_STEP); + return 0; + } + else + { + dm_get_version_s_ptr = (const char *(*) (void)) get_symbol (libdm, "dm_get_version_s"); + dm_set_gauge_ptr = (void (*) (void (*) (int, int))) get_symbol (libdm, "dm_set_gauge"); + + dm_open_ptr = (dm_image_t *(*) (const char *, uint32_t)) get_symbol (libdm, "dm_open"); + dm_reopen_ptr = (dm_image_t *(*) (const char *, uint32_t, dm_image_t *)) + get_symbol (libdm, "dm_reopen"); + dm_fdopen_ptr = (FILE *(*) (dm_image_t *, int, const char *)) + get_symbol (libdm, "dm_fdopen"); + dm_close_ptr = (int (*) (dm_image_t *)) get_symbol (libdm, "dm_close"); + dm_nfo_ptr = (void (*) (const dm_image_t *, int, int)) get_symbol (libdm, "dm_nfo"); + + dm_read_ptr = (int (*) (char *, int, int, const dm_image_t *)) get_symbol (libdm, "dm_read"); + dm_write_ptr = (int (*) (const char *, int, int, const dm_image_t *)) get_symbol (libdm, "dm_write"); + + dm_disc_read_ptr = (int (*) (const dm_image_t *)) get_symbol (libdm, "dm_disc_read"); + dm_disc_write_ptr = (int (*) (const dm_image_t *)) get_symbol (libdm, "dm_disc_write"); + + dm_toc_read_ptr = (dm_image_t *(*) (dm_image_t *, const char *)) get_symbol (libdm, "dm_toc_read"); + dm_toc_write_ptr = (int (*) (const dm_image_t *)) get_symbol (libdm, "dm_toc_write"); + + dm_cue_read_ptr = (dm_image_t *(*) (dm_image_t *, const char *)) get_symbol (libdm, "dm_cue_read"); + dm_cue_write_ptr = (int (*) (const dm_image_t *)) get_symbol (libdm, "dm_cue_write"); + + dm_rip_ptr = (int (*) (const dm_image_t *, int, uint32_t)) get_symbol (libdm, "dm_rip"); + + return 1; + } + } + else + return 0; +#else // !defined DLOPEN +#ifdef DJGPP + { + /* + The following piece of code makes the DLL "search" behaviour a bit like + the search behaviour for Windows programs. A bit, because the import + library just opens the file with the name that is stored in + djimport_path. It won't search for the DXE in the Windows system + directory, nor will it search the directories of the PATH environment + variable. + */ + extern char djimport_path[FILENAME_MAX]; + char dir[FILENAME_MAX]; + int n, l; + + dirname2 (ucon64.argv[0], dir); + sprintf (djimport_path, "%s"FILE_SEPARATOR_S"%s", dir, "discmage.dxe"); + // this is specific to DJGPP - not necessary, but prevents confusion + l = strlen (djimport_path); + for (n = 0; n < l; n++) + if (djimport_path[n] == '/') + djimport_path[n] = '\\'; + } +#endif // DJGPP + version = dm_get_version (); + if (version < LIB_VERSION (UCON64_DM_VERSION_MAJOR, + UCON64_DM_VERSION_MINOR, + UCON64_DM_VERSION_STEP)) + { + printf ("WARNING: Your libdiscmage is too old (%u.%u.%u)\n" + " You need at least version %u.%u.%u\n\n", + (unsigned int) version >> 16, + (unsigned int) ((version >> 8) & 0xff), + (unsigned int) (version & 0xff), + UCON64_DM_VERSION_MAJOR, + UCON64_DM_VERSION_MINOR, + UCON64_DM_VERSION_STEP); + return 0; + } + return 1; // discmage could be "loaded" +#endif // !defined DLOPEN +} + + +int +libdm_gauge (int pos, int size) +{ + static time_t init_time = 0; + + if (!init_time || !pos /* || !size */) + init_time = time (0); + + return ucon64_gauge (init_time, pos, size); +} + + +#ifdef DLOPEN +uint32_t +dm_get_version (void) +{ + return dm_get_version_ptr (); +} + + +const char * +dm_get_version_s (void) +{ + return dm_get_version_s_ptr (); +} + + +void +dm_set_gauge (void (*a) (int, int)) +{ + dm_set_gauge_ptr (a); +} + + +FILE * +dm_fdopen (dm_image_t *a, int b, const char *c) +{ + return dm_fdopen_ptr (a, b, c); +} + + +dm_image_t * +dm_open (const char *a, uint32_t b) +{ + return dm_open_ptr (a, b); +} + + +dm_image_t * +dm_reopen (const char *a, uint32_t b, dm_image_t *c) +{ + return dm_reopen_ptr (a, b, c); +} + + +int +dm_close (dm_image_t *a) +{ + return dm_close_ptr (a); +} + + +void +dm_nfo (const dm_image_t *a, int b, int c) +{ + dm_nfo_ptr (a, b, c); +} + + +int +dm_disc_read (const dm_image_t *a) +{ + return dm_disc_read_ptr (a); +} + + +int +dm_disc_write (const dm_image_t *a) +{ + return dm_disc_write_ptr (a); +} + + +int +dm_read (char *a, int b, int c, const dm_image_t *d) +{ + return dm_read_ptr (a, b, c, d); +} + + +int +dm_write (const char *a, int b, int c, const dm_image_t *d) +{ + return dm_write_ptr (a, b, c, d); +} + + +dm_image_t * +dm_toc_read (dm_image_t *a, const char *b) +{ + return dm_toc_read_ptr (a, b); +} + + +int +dm_toc_write (const dm_image_t *a) +{ + return dm_toc_write_ptr (a); +} + + +dm_image_t * +dm_cue_read (dm_image_t *a, const char *b) +{ + return dm_cue_read_ptr (a, b); +} + + +int +dm_cue_write (const dm_image_t *a) +{ + return dm_cue_write_ptr (a); +} + + +int +dm_rip (const dm_image_t *a, int b, uint32_t c) +{ + return dm_rip_ptr (a, b, c); +} +#endif // DLOPEN +#endif // USE_DISCMAGE + + +int +unknown_init (st_rominfo_t *rominfo) +// init routine for all consoles missing in console/. +{ + ucon64.rominfo = rominfo; + ucon64.dat = NULL; +#ifdef USE_DISCMAGE + ucon64.image = NULL; +#endif + + return 0; +} + + +int +ucon64_file_handler (char *dest, char *src, int flags) +/* + We have to handle the following cases (for example -swc and rom.swc exists): + 1) ucon64 -swc rom.swc + a) with backup creation enabled + Create backup of rom.swc + postcondition: src == name of backup + b) with backup creation disabled + Create temporary backup of rom.swc by renaming rom.swc + postcondition: src == name of backup + 2) ucon64 -swc rom.fig + a) with backup creation enabled + Create backup of rom.swc + postcondition: src == rom.fig + b) with backup creation disabled + Do nothing + postcondition: src == rom.fig + + This function returns 1 if dest existed (in the directory specified with -o). + Otherwise it returns 0; +*/ +{ + struct stat dest_info; + + ucon64_output_fname (dest, flags); // call this function unconditionally + +#if 0 + // ucon64_temp_file will be reset in remove_temp_file() + ucon64_temp_file = NULL; +#endif + if (!access (dest, F_OK)) + { + stat (dest, &dest_info); + // *Trying* to make dest writable here avoids having to change all code + // that might (try to) operate on a read-only file + chmod (dest, dest_info.st_mode | S_IWUSR); + + if (src == NULL) + { + if (ucon64.backup) + printf ("Wrote backup to: %s\n", mkbak (dest, BAK_DUPE)); + return 1; + } + + if (one_file (src, dest)) + { // case 1 + if (ucon64.backup) + { // case 1a + strcpy (src, mkbak (dest, BAK_DUPE)); + printf ("Wrote backup to: %s\n", src); + } + else + { // case 1b + strcpy (src, mkbak (dest, BAK_MOVE)); + ucon64_temp_file = src; + } + } + else + { // case 2 + if (ucon64.backup) // case 2a + printf ("Wrote backup to: %s\n", mkbak (dest, BAK_DUPE)); + } + return 1; + } + return 0; +} + + +void +remove_temp_file (void) +{ + if (ucon64_temp_file) + { + printf ("Removing: %s\n", ucon64_temp_file); + remove (ucon64_temp_file); + ucon64_temp_file = NULL; + } +} + + +char * +ucon64_output_fname (char *requested_fname, int flags) +{ + char suffix[80], fname[FILENAME_MAX]; + + // We have to make a copy, because get_suffix() returns a pointer to a + // location in the original string + strncpy (suffix, get_suffix (requested_fname), sizeof (suffix))[sizeof (suffix) - 1] = 0; // in case suffix is >= 80 chars + + // OF_FORCE_BASENAME is necessary for options like -gd3. Of course that + // code should handle archives and come up with unique filenames for + // archives with more than one file. + if (!ucon64.fname_arch[0] || (flags & OF_FORCE_BASENAME)) + { + strcpy (fname, basename2 (requested_fname)); + sprintf (requested_fname, "%s%s", ucon64.output_path, fname); + } + else // an archive (for now: zip file) + sprintf (requested_fname, "%s%s", ucon64.output_path, ucon64.fname_arch); + + /* + Keep the requested suffix, but only if it isn't ".zip" or ".gz". This + because we currently don't write to zip or gzip files. Otherwise the output + file would have the suffix ".zip" or ".gz" while it isn't a zip or gzip + file. uCON64 handles such files correctly, because it looks at the file + data itself, but many programs don't. + If the flag OF_FORCE_SUFFIX was used we keep the suffix, even if it's + ".zip" or ".gz". Now ucon64_output_fname() can be used when renaming/moving + files. + */ + if (!(flags & OF_FORCE_SUFFIX) && + !(stricmp (suffix, ".zip") && stricmp (suffix, ".gz"))) + strcpy (suffix, ".tmp"); + set_suffix (requested_fname, suffix); + return requested_fname; +} + + +#if 1 +int +ucon64_testpad (const char *filename) +/* + Test if EOF is padded (repeated byte values) + This (new) version is not efficient for uncompressed files, but *much* more + efficient for compressed files. For example (a bad case), on a Celeron 850 + just viewing info about a zipped dump of Mario Party (U) takes more than 3 + minutes when the old version of ucon64_testpad() is used. A gzipped dump + can take more than 6 minutes. With this version it takes about 9 seconds for + the zipped dump and 12 seconds for the gzipped dump. +*/ +{ + int c = 0, blocksize, i, n = 0, start_n; + unsigned char buffer[MAXBUFSIZE]; + FILE *file = fopen (filename, "rb"); + + if (!file) + return -1; + + while ((blocksize = fread (buffer, 1, MAXBUFSIZE, file))) + { + if (buffer[blocksize - 1] != c) + { + c = buffer[blocksize - 1]; + n = 0; + } + start_n = n; + for (i = blocksize - 1; i >= 0; i--) + { + if (buffer[i] != c) + { + n -= start_n; + break; + } + else + { + /* + A file is either padded with 2 or more bytes or it isn't + padded at all. It can't be detected that a file is padded with + 1 byte. + */ + if (i == blocksize - 2) + n += 2; + else if (i < blocksize - 2) + n++; + // NOT else, because i == blocksize - 1 must initially be skipped + } + } + } + + fclose (file); + return n; +} +#else +int +ucon64_testpad (const char *filename) +// test if EOF is padded (repeating bytes) +{ + int pos = ucon64.file_size - 1, buf_pos = pos % MAXBUFSIZE, + c = ucon64_fgetc (filename, pos); + unsigned char buf[MAXBUFSIZE]; + FILE *fh = fopen (filename, "rb"); + + if (!fh) + return -1; + + for (pos -= buf_pos; !fseek (fh, pos, SEEK_SET) && pos > -1; + pos -= MAXBUFSIZE, buf_pos = MAXBUFSIZE) + { + fread (buf, 1, buf_pos, fh); + + for (; buf_pos > 0; buf_pos--) + if (buf[buf_pos - 1] != c) + { + fclose (fh); + + return ucon64.file_size - (pos + buf_pos) > 1 ? + ucon64.file_size - (pos + buf_pos) : 0; + } + } + + fclose (fh); + + return ucon64.file_size; // the whole file is "padded" +} +#endif + + +int +ucon64_gauge (time_t init_time, int pos, int size) +{ + return gauge (stdout, init_time, pos, size, ucon64.frontend ? GAUGE_PERCENT : GAUGE_DEFAULT); +} + + +int +ucon64_testsplit (const char *filename) +// test if ROM is split into parts based on the name of files +{ + int x, parts = 0, l; + char buf[FILENAME_MAX], *p = NULL; + + for (x = -1; x < 2; x += 2) + { + parts = 0; + strcpy (buf, filename); + p = strrchr (buf, '.'); + l = strlen (buf); + + if (p == NULL) // filename doesn't contain a period + p = buf + l - 1; + else + p += x; // if x == -1 change char before '.' + // else if x == 1 change char after '.' + if (buf > p || // filename starts with '.' (x == -1) + p - buf > l - 1) // filename ends with '.' (x == 1) + continue; + + while (!access (buf, F_OK)) + (*p)--; // "rewind" (find the first part) + (*p)++; + + while (!access (buf, F_OK)) // count split parts + { + if (ucon64_testsplit_callback) + ucon64_testsplit_callback (buf); + (*p)++; + parts++; + } + + if (parts > 1) + return parts; + } + + return 0; +} + + +// configfile handling +static int +ucon64_configfile_update (void) +{ + char buf[MAXBUFSIZE]; + + sprintf (buf, "%d", UCON64_CONFIG_VERSION); + set_property (ucon64.configfile, "version", buf, "uCON64 configuration"); + + return 0; +} + + +typedef struct +{ + int id; + const char *command; +} st_command_t; + + +static int +ucon64_configfile_create (void) +{ + const st_getopt2_t *options = ucon64.options; + const st_property_t props[] = + { + { + "backups", "1", + "create backups of files? (1=yes; 0=no)\n" + "before processing a ROM uCON64 will make a backup of it" + }, + { + "ansi_color", "1", + "use ANSI colors in output? (1=yes; 0=no)" + }, +#ifdef USE_PPDEV + { + "parport_dev", "/dev/parport0", + "parallel port" + }, +#elif defined AMIGA + { + "parport_dev", "parallel.device", + "parallel port" + }, + { + "parport", "0", + NULL + }, +#else + { + "parport", "378", + "parallel port" + }, +#endif + { + "discmage_path", +#if defined __MSDOS__ + "~\\discmage.dxe", // realpath2() expands the tilde +#elif defined __CYGWIN__ + "~/discmage.dll", +#elif defined _WIN32 + "~\\discmage.dll", +#elif defined __APPLE__ // Mac OS X actually + "~/.ucon64/discmage.dylib", +#elif defined __unix__ || defined __BEOS__ + "~/.ucon64/discmage.so", +#else + "", +#endif + "complete path to the discmage library for CD image support" + }, + { + "ucon64_configdir", +#if defined __MSDOS__ || defined __CYGWIN__ || defined _WIN32 + "~", // realpath2() expands the tilde +#elif defined __unix__ || defined __BEOS__ || defined __APPLE__ // Mac OS X actually + "~/.ucon64", +#else + "", +#endif + "directory with additional config files" + }, + { + "ucon64_datdir", +#if defined __MSDOS__ || defined __CYGWIN__ || defined _WIN32 + "~", // realpath2() expands the tilde +#elif defined __unix__ || defined __BEOS__ || defined __APPLE__ // Mac OS X actually + "~/.ucon64/dat", +#else + "", +#endif + "directory with DAT files" + }, + { + "f2afirmware", "f2afirm.hex", + "F2A support files\n" + "path to F2A USB firmware" + }, + { + "iclientu", "iclientu.bin", + "path to GBA client binary (for USB code)" + }, + { + "iclientp", "iclientp.bin", + "path to GBA client binary (for parallel port code)" + }, + { + "ilogo", "ilogo.bin", + "path to iLinker logo file" + }, + { + "gbaloader", "loader.bin", + "path to GBA multi-game loader" + }, + {NULL, NULL, NULL} + }; + + st_command_t emulate[] = + { + {UCON64_3DO, ""}, + {UCON64_ATA, ""}, + {UCON64_CD32, ""}, + {UCON64_CDI, ""}, + {UCON64_COLECO, ""}, + {UCON64_DC, ""}, + {UCON64_GB, "vgb -sound -sync 50 -sgb -scale 2"}, + {UCON64_GBA, "vgba -scale 2 -uperiod 6"}, + {UCON64_GC, ""}, + {UCON64_GEN, "dgen -f -S 2"}, + {UCON64_INTELLI, ""}, + {UCON64_JAG, ""}, + {UCON64_LYNX, ""}, + {UCON64_MAME, ""}, + {UCON64_N64, ""}, + {UCON64_NES, "tuxnes -E2 -rx11 -v -s/dev/dsp -R44100"}, + {UCON64_NG, ""}, + {UCON64_NGP, ""}, + {UCON64_PCE, ""}, + {UCON64_PS2, ""}, + {UCON64_PSX, "pcsx"}, + {UCON64_S16, ""}, + {UCON64_SAT, ""}, + {UCON64_SMS, ""}, + {UCON64_GAMEGEAR, ""}, + {UCON64_SNES, "snes9x -tr -sc -hires -dfr -r 7 -is -joymap1 2 3 5 0 4 7 6 1"}, + {UCON64_SWAN, ""}, + {UCON64_VBOY, ""}, + {UCON64_VEC, ""}, + {UCON64_XBOX, ""}, + {0, NULL} + }; + int x = 0, y = 0; + + ucon64_configfile_update (); + + set_property_array (ucon64.configfile, props); + + for (x = 0; emulate[x].command; x++) + for (y = 0; options[y].name || options[y].help; y++) + if (emulate[x].id == options[y].val) + { + char buf[MAXBUFSIZE]; + + sprintf (buf, "emulate_%s", options[y].name); + + set_property (ucon64.configfile, buf, emulate[x].command, !x ? + "emulate_=\n\n" + "You can also use CRC32 values for ROM specific emulation options:\n\n" + "emulate_0x=\n" + "emulate_=" : NULL); + + break; + } + + return 0; +} + + +int +ucon64_configfile (void) +{ + char buf[MAXBUFSIZE], *dirname; + int result = -1; + + dirname = getenv2 ("UCON64_HOME"); + if (!dirname[0]) + dirname = getenv2 ("HOME"); + sprintf (ucon64.configfile, "%s" FILE_SEPARATOR_S +#ifdef __MSDOS__ + "ucon64.cfg" +#else + ".ucon64rc" +#endif + , dirname); + +// if (!access (ucon64.configfile, F_OK)) +// fprintf (stderr, ucon64_msg[READ_CONFIG_FILE], ucon64.configfile); + + if (access (ucon64.configfile, F_OK) != 0) + { + FILE *fh; + + printf ("WARNING: %s not found: creating...", ucon64.configfile); + + if (!(fh = fopen (ucon64.configfile, "w"))) // opening the file in text mode + { // avoids trouble under DOS + printf ("FAILED\n\n"); + return -1; + } + fclose (fh); // we'll use set_property() from now + + result = ucon64_configfile_create (); + + if (!result) + { + sync (); + printf ("OK\n\n"); + } + else + printf ("FAILED\n\n"); + } + else if (get_property_int (ucon64.configfile, "version") < UCON64_CONFIG_VERSION) + { + strcpy (buf, ucon64.configfile); + set_suffix (buf, ".old"); + + printf ("NOTE: Updating config, old version will be renamed to %s...", buf); + + fcopy (ucon64.configfile, 0, fsizeof (ucon64.configfile), buf, "wb"); // "wb" is correct for copying + + result = ucon64_configfile_update (); + + if (!result) + { + sync (); + printf ("OK\n\n"); + } + else + printf ("FAILED\n\n"); + } + + return result; +} + + +static inline char * +to_func (char *s, int len, int (*func) (int)) +{ + char *p = s; + + for (; len > 0; p++, len--) + *p = func (*p); + + return s; +} + + +int +ucon64_rename (int mode) +{ + char buf[FILENAME_MAX + 1], buf2[FILENAME_MAX + 1], suffix[80]; + const char *p, *p2; + int good_name; + + buf[0] = 0; + strncpy (suffix, get_suffix (ucon64.rom), sizeof (suffix))[sizeof (suffix) - 1] = 0; // in case suffix is >= 80 chars + + switch (mode) + { + case UCON64_RROM: + if (ucon64.rominfo) + if (ucon64.rominfo->name) + { + strcpy (buf, ucon64.rominfo->name); + strtriml (strtrimr (buf)); + } + break; + + case UCON64_RENAME: // GoodXXXX style rename + if (ucon64.dat) + if (((st_ucon64_dat_t *) ucon64.dat)->fname) + { + p = (char *) get_suffix (((st_ucon64_dat_t *) ucon64.dat)->fname); + strcpy (buf, ((st_ucon64_dat_t *) ucon64.dat)->fname); + + // get_suffix() never returns NULL + if (p[0]) + if (strlen (p) < 5) + if (!(stricmp (p, ".nes") && // NES + stricmp (p, ".fds") && // NES FDS + stricmp (p, ".gb") && // Game Boy + stricmp (p, ".gbc") && // Game Boy Color + stricmp (p, ".gba") && // Game Boy Advance + stricmp (p, ".smc") && // SNES + stricmp (p, ".sc") && // Sega Master System + stricmp (p, ".sg") && // Sega Master System + stricmp (p, ".sms") && // Sega Master System + stricmp (p, ".gg") && // Game Gear + stricmp (p, ".smd") && // Genesis + stricmp (p, ".v64"))) // Nintendo 64 + buf[strlen (buf) - strlen (p)] = 0; + } + break; + + default: + return 0; // invalid mode + } + + if (!buf[0]) + return 0; + + if (ucon64.fname_len == UCON64_FORCE63) + buf[63] = 0; + else if (ucon64.fname_len == UCON64_RR83) + buf[8] = 0; + + // replace chars the fs might not like + strcpy (buf2, to_func (buf, strlen (buf), tofname)); + strcpy (buf, basename2 (ucon64.rom)); + + p = (char *) get_suffix (buf); + // Remove the suffix from buf (ucon64.rom). Note that this isn't fool-proof. + // However, this is the best solution, because several DAT files contain + // "canonical" file names with a suffix. That is a STUPID bug. + if (p) + buf[strlen (buf) - strlen (p)] = 0; + +#ifdef DEBUG +// printf ("buf: \"%s\"; buf2: \"%s\"\n", buf, buf2); +#endif + if (!strcmp (buf, buf2)) + // also process files with a correct name, so that -rename can be used to + // "weed" out good dumps when -o is used (like GoodXXXX without inplace + // command) + good_name = 1; + else + { + // Another test if the file already has a correct name. This is necessary + // for files without a "normal" suffix (e.g. ".smc"). Take for example a + // name like "Final Fantasy III (V1.1) (U) [!]". + strcat (buf, suffix); + if (!strcmp (buf, buf2)) + { + good_name = 1; + suffix[0] = 0; // discard "suffix" (part after period) + } + else + good_name = 0; + } + + // DON'T use set_suffix()! Consider file names (in the DAT file) like + // "Final Fantasy III (V1.1) (U) [!]". The suffix is ".1) (U) [!]"... + strcat (buf2, suffix); + + if (ucon64.fname_len == UCON64_RR83) + buf2[12] = 0; + + ucon64_output_fname (buf2, OF_FORCE_BASENAME | OF_FORCE_SUFFIX); + + p = basename2 (ucon64.rom); + p2 = basename2 (buf2); + + if (one_file (ucon64.rom, buf2) && !strcmp (p, p2)) + { // skip only if the letter case + printf ("Skipping \"%s\"\n", p); // also matches (Windows...) + return 0; + } + + if (!good_name) + /* + Note that the previous statement causes whatever file is present in the + dir specified with -o (or the current dir) to be overwritten (if the file + already has a correct name). This seems bad, but is actually better than + making a backup. It isn't so bad, because the file that gets overwritten + is either the same as the file it is overwritten with or doesn't deserve + its name. + Without this statement repeating a rename action for already renamed + files would result in a real mess. And I (dbjh) mean a *real* mess... + */ + if (!access (buf2, F_OK) && !strcmp (p, p2)) // a file with that name exists already? + ucon64_file_handler (buf2, NULL, OF_FORCE_BASENAME | OF_FORCE_SUFFIX); + + if (!good_name) + printf ("Renaming \"%s\" to \"%s\"\n", p, p2); + else + printf ("Moving \"%s\"\n", p); +#ifndef DEBUG + rename2 (ucon64.rom, buf2); // rename2() must be used! +#endif +#ifdef USE_ZLIB + unzip_current_file_nr = 0x7fffffff - 1; // dirty hack +#endif + return 0; +} + + +int +ucon64_e (void) +{ + int result = 0; + char buf[MAXBUFSIZE], value[MAXBUFSIZE], name[MAXBUFSIZE]; + const char *value_p = NULL; + const st_getopt2_t *p = NULL, *options = ucon64.options; + + if (access (ucon64.configfile, F_OK) != 0) + { + fprintf (stderr, "ERROR: %s does not exist\n", ucon64.configfile); + return -1; + } + + sprintf (name, "emulate_%08x", ucon64.crc32); // look for emulate_ + value_p = get_property (ucon64.configfile, name, value, NULL); + + if (value_p == NULL) + { + sprintf (name, "emulate_0x%08x", ucon64.crc32); // look for emulate_0x + value_p = get_property (ucon64.configfile, name, value, NULL); + } + + if (value_p == NULL) + if ((p = getopt2_get_index_by_val (options, ucon64.console))) + { + sprintf (name, "emulate_%s", p->name); // look for emulate_ + value_p = get_property (ucon64.configfile, name, value, NULL); + } + + if (value_p == NULL) + { + fprintf (stderr, "ERROR: Could not find the correct settings (%s) in\n" + " %s\n" + "TIP: If the wrong console was detected you might try to force recognition\n" + " The force recognition option for SNES would be " OPTION_LONG_S "snes\n", + name, ucon64.configfile); + return -1; + } + + sprintf (buf, "%s \"%s\"", value_p, ucon64.rom); + + puts (buf); + fflush (stdout); + sync (); + + result = system (buf) +#if !(defined __MSDOS__ || defined _WIN32) + >> 8 // the exit code is coded in bits 8-15 +#endif // (does not apply to DJGPP, MinGW & VC++) + ; + +#if 1 + // Snes9x (Linux) for example returns a non-zero value on a normal exit + // (3)... + // under WinDOS, system() immediately returns with exit code 0 when + // starting a Windows executable (as if fork() was called) it also + // returns 0 when the exe could not be started + if (result != 127 && result != -1 && result != 0) // 127 && -1 are system() errors, rest are exit codes + { + fprintf (stderr, "ERROR: The emulator returned an error (?) code: %d\n" + "TIP: If the wrong emulator was used you might try to force recognition\n" + " The force recognition option for SNES would be " OPTION_LONG_S "snes\n", + result); + } +#endif + return result; +} + + +#define PATTERN_BUFSIZE (64 * 1024) +/* + In order for this function to be really useful for general purposes + change_mem2() should be changed so that it will return detailed status + information. Since we don't use it for general purposes, this has not a high + priority. It will be updated as soon as there is a need. + The thing that currently goes wrong is that offsets that fall outside the + buffer (either positive or negative) won't result in a change. It will result + in memory corruption... +*/ +int +ucon64_pattern (st_rominfo_t *rominfo, const char *pattern_fname) +{ + char src_name[FILENAME_MAX], dest_name[FILENAME_MAX], + buffer[PATTERN_BUFSIZE]; + FILE *srcfile, *destfile; + int bytesread = 0, n, n_found = 0, n_patterns, overlap = 0; + st_cm_pattern_t *patterns = NULL; + + realpath2 (pattern_fname, src_name); + // First try the current directory, then the configuration directory + if (access (src_name, F_OK | R_OK) == -1) + sprintf (src_name, "%s" FILE_SEPARATOR_S "%s", ucon64.configdir, pattern_fname); + n_patterns = build_cm_patterns (&patterns, src_name, ucon64.quiet == -1 ? 1 : 0); + if (n_patterns == 0) + { + fprintf (stderr, "ERROR: No patterns found in %s\n", src_name); + cleanup_cm_patterns (&patterns, n_patterns); + return -1; + } + else if (n_patterns < 0) + { + char dir1[FILENAME_MAX], dir2[FILENAME_MAX]; + dirname2 (pattern_fname, dir1); + dirname2 (src_name, dir2); + + fprintf (stderr, "ERROR: Could not read from %s, not in %s nor in %s\n", + basename2 (pattern_fname), dir1, dir2); + // when build_cm_patterns() returns -1, cleanup_cm_patterns() should not be called + return -1; + } + + printf ("Found %d pattern%s in %s\n", n_patterns, n_patterns != 1 ? "s" : "", src_name); + + for (n = 0; n < n_patterns; n++) + { + if (patterns[n].search_size > overlap) + { + overlap = patterns[n].search_size; + if (overlap > PATTERN_BUFSIZE) + { + fprintf (stderr, + "ERROR: Pattern %d is too large, specify a shorter pattern\n", + n + 1); + cleanup_cm_patterns (&patterns, n_patterns); + return -1; + } + } + + if ((patterns[n].offset < 0 && patterns[n].offset <= -patterns[n].search_size) || + patterns[n].offset > 0) + printf ("WARNING: The offset of pattern %d falls outside the search pattern.\n" + " This can cause problems with the current implementation of --pattern.\n" + " Please consider enlarging the search pattern.\n", + n + 1); + } + overlap--; + + puts ("Searching for patterns..."); + + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + ucon64_file_handler (dest_name, src_name, 0); + if ((srcfile = fopen (src_name, "rb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], src_name); + return -1; + } + if ((destfile = fopen (dest_name, "wb")) == NULL) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], dest_name); + return -1; + } + if (rominfo->buheader_len) // copy header (if present) + { + n = rominfo->buheader_len; + while ((bytesread = fread (buffer, 1, MIN (n, PATTERN_BUFSIZE), srcfile))) + { + fwrite (buffer, 1, bytesread, destfile); + n -= bytesread; + } + } + + n = fread (buffer, 1, overlap, srcfile); // keep bytesread set to 0 + if (n < overlap) // DAMN special cases! + { + n_found += change_mem2 (buffer, n, patterns[n].search, + patterns[n].search_size, patterns[n].wildcard, + patterns[n].escape, patterns[n].replace, + patterns[n].replace_size, patterns[n].offset, + patterns[n].sets); + fwrite (buffer, 1, n, destfile); + n = -1; + } + else + do + { + if (bytesread) // the code also works without this if + { + for (n = 0; n < n_patterns; n++) + { + int x = 1 - patterns[n].search_size; + n_found += change_mem2 (buffer + overlap + x, + bytesread + patterns[n].search_size - 1, + patterns[n].search, patterns[n].search_size, + patterns[n].wildcard, patterns[n].escape, + patterns[n].replace, patterns[n].replace_size, + patterns[n].offset, patterns[n].sets); + } + fwrite (buffer, 1, bytesread, destfile); + memmove (buffer, buffer + bytesread, overlap); + } + } + while ((bytesread = fread (buffer + overlap, 1, PATTERN_BUFSIZE - overlap, srcfile))); + if (n != -1) + fwrite (buffer, 1, overlap, destfile); + + fclose (srcfile); + fclose (destfile); + cleanup_cm_patterns (&patterns, n_patterns); + + printf ("Found %d pattern%s\n", n_found, n_found != 1 ? "s" : ""); + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + return n_found; +} +#undef PATTERN_BUFSIZE + + +int +ucon64_bswap16_n (void *buffer, int n) +// bswap16() n bytes of buffer +{ + int i = n; + uint16_t *w = (uint16_t *) buffer; + + for (; i > 1; i -= 2, w++) + *w = bswap_16 (*w); + + return n; // return # of bytes swapped +} + + +static inline int +ucon64_fbswap16_func (void *buffer, int n, void *object) +// bswap16() n bytes of buffer +{ + (void) object; + return ucon64_bswap16_n (buffer, n); +} + + +static inline int +ucon64_fwswap32_func (void *buffer, int n, void *object) +// wswap32() n/2 words of buffer +{ + int i = n; + uint32_t *l = (uint32_t *) buffer; + (void) object; + + i >>= 1; // # words = # bytes / 2 + for (; i > 1; i -= 2, l++) + *l = wswap_32 (*l); + + return n; // return # of bytes swapped +} + + +void +ucon64_fbswap16 (const char *fname, size_t start, size_t len) +{ + quick_io_func (ucon64_fbswap16_func, MAXBUFSIZE, NULL, start, len, fname, "r+b"); +} + + +void +ucon64_fwswap32 (const char *fname, size_t start, size_t len) +{ + quick_io_func (ucon64_fwswap32_func, MAXBUFSIZE, NULL, start, len, fname, "r+b"); +} + + +typedef struct +{ + FILE *output; + int virtual_pos; + uint32_t flags; +} st_ucon64_dump_t; + + +static inline int +ucon64_dump_func (void *buffer, int n, void *object) +{ + st_ucon64_dump_t *o = (st_ucon64_dump_t *) object; + + dumper (o->output, buffer, n, o->virtual_pos, o->flags); + o->virtual_pos += n; + + return n; +} + + +void +ucon64_dump (FILE *output, const char *filename, size_t start, size_t len, + uint32_t flags) +{ + st_ucon64_dump_t o = {output, start, flags}; + + quick_io_func (ucon64_dump_func, MAXBUFSIZE, &o, start, len, filename, "rb"); +} + + +typedef struct +{ + const void *search; + uint32_t flags; + int searchlen; + int pos; + int found; +} st_ucon64_find_t; + + +static inline int +ucon64_find_func (void *buffer, int n, void *object) +{ + st_ucon64_find_t *o = (st_ucon64_find_t *) object; + char *ptr0 = (char *) buffer, *ptr1 = (char *) buffer; + int m; + static char match[MAXBUFSIZE - 1], compare[MAXBUFSIZE + 16 + 1]; + static int matchlen; + + // reset matchlen if this is the first call for a new file + if (o->found == -2) + { + o->found = -1; // -1 is default (return) value + matchlen = 0; + } + + // check if we can match the search string across the buffer boundary + for (m = 0; matchlen; matchlen--) + { + memcpy (compare, match + m++, matchlen); + memcpy (compare + matchlen, ptr1, ((o->searchlen + 0x0f) & ~0x0f) - matchlen); + if (memcmp2 (compare, o->search, o->searchlen, o->flags) == 0) + { + o->found = o->pos - matchlen; + if (!(o->flags & UCON64_FIND_QUIET)) + { + dumper (stdout, compare, (o->searchlen + 0x0f) & ~0x0f, o->found, DUMPER_HEX); + fputc ('\n', stdout); + } + } + } + + while (ptr1 - ptr0 < n) + { + ptr1 = (char *) memmem2 (ptr1, n - (ptr1 - ptr0), o->search, o->searchlen, + o->flags); + if (ptr1) + { + o->found = o->pos + ptr1 - ptr0; + if (!(o->flags & UCON64_FIND_QUIET)) + { + dumper (stdout, ptr1, (o->searchlen + 0x0f) & ~0x0f, o->found, DUMPER_HEX); + fputc ('\n', stdout); + } + ptr1++; + } + else + { + // try to find a partial match at the end of buffer + ptr1 = ptr0 + n - o->searchlen; + for (m = 1; m < o->searchlen; m++) + if (memcmp2 (ptr1 + m, o->search, o->searchlen - m, o->flags) == 0) + { + memcpy (match, ptr1 + m, o->searchlen - m); + matchlen = o->searchlen - m; + break; + } + if (!matchlen) // && o->flags & MEMMEM2_REL + { + match[0] = ptr0[n - 1]; // we must not split the string + matchlen = 1; // for a relative search + } + break; + } + } + + o->pos += n; + return n; +} + + +int +ucon64_find (const char *filename, size_t start, size_t len, + const char *search, int searchlen, uint32_t flags) +{ + int result = 0; + st_ucon64_find_t o = { search, flags, searchlen, start, -2 }; + // o.found == -2 signifies a new find operation (usually for a new file) + + if (searchlen < 1) + { + fprintf (stderr, "ERROR: No search string specified\n"); + exit (1); + } + else if (flags & MEMCMP2_REL) + if (searchlen < 2) + { + fprintf (stderr, "ERROR: Search string must be longer than 1 character for a relative search\n"); + exit (1); + } + if (searchlen > MAXBUFSIZE) + { + fprintf (stderr, "ERROR: Search string must be <= %d characters\n", MAXBUFSIZE); + exit (1); // see ucon64_find_func() for why + } + + if (!(flags & UCON64_FIND_QUIET)) + { + fputs (basename2 (filename), stdout); + if (ucon64.fname_arch[0]) + printf (" (%s)\n", basename2 (ucon64.fname_arch)); + else + fputc ('\n', stdout); + + // TODO: display "b?a" as "b" "a" + if (!(flags & (MEMCMP2_CASE | MEMCMP2_REL))) + printf ("Searching: \"%s\"\n\n", search); + else if (flags & MEMCMP2_CASE) + printf ("Case insensitive searching: \"%s\"\n\n", search); + else if (flags & MEMCMP2_REL) + { + char *p = (char *) search; + + printf ("Relative searching: \"%s\"\n\n", search); + for (; *(p + 1); p++) + printf ("'%c' - '%c' = %d\n", *p, *(p + 1), *p - *(p + 1)); + printf ("\n"); + } + } + + result = quick_io_func (ucon64_find_func, MAXBUFSIZE, &o, start, len, + filename, "rb"); + + return o.found; // return last occurrence or -1 +} + + +typedef struct +{ + s_sha1_ctx_t *m_sha1; + s_md5_ctx_t *m_md5; +// uint16_t *crc16; + unsigned int *crc32; +} st_ucon64_chksum_t; + + +static inline int +ucon64_chksum_func (void *buffer, int n, void *object) +{ + st_ucon64_chksum_t *o = (st_ucon64_chksum_t *) object; + + if (o->m_sha1) + sha1 (o->m_sha1, (const unsigned char *) buffer, n); + + if (o->m_md5) + md5_update (o->m_md5, (unsigned char *) buffer, n); + +// if (o->crc16) +// *(o->crc16) = crc16 (*(o->crc16), (const unsigned char *) buffer, n); + + if (o->crc32) + *(o->crc32) = crc32 (*(o->crc32), (const unsigned char *) buffer, n); + + return n; +} + + +int +ucon64_chksum (char *sha1_s, char *md5_s, unsigned int *crc32_i, // uint16_t *crc16_i, + const char *filename, size_t start) +{ + int i = 0, result; + s_sha1_ctx_t m_sha1; + s_md5_ctx_t m_md5; + st_ucon64_chksum_t o; + + memset (&o, 0, sizeof (st_ucon64_chksum_t)); + + if (sha1_s) + sha1_begin (o.m_sha1 = &m_sha1); + + if (md5_s) + md5_init (o.m_md5 = &m_md5, 0); + +// if (crc16_i) +// o.crc16 = crc16_i; + + if (crc32_i) + o.crc32 = crc32_i; + + result = quick_io_func (ucon64_chksum_func, MAXBUFSIZE, &o, start, + fsizeof (filename) - start, filename, "rb"); + if (sha1_s) + { + unsigned char buf[MAXBUFSIZE]; + + sha1_end (buf, &m_sha1); + for (*sha1_s = i = 0; i < 20; i++, sha1_s = strchr (sha1_s, 0)) + sprintf (sha1_s, "%02x", buf[i] & 0xff); + } + + if (md5_s) + { + md5_final (&m_md5); + for (*md5_s = i = 0; i < 16; i++, md5_s = strchr (md5_s, 0)) + sprintf (md5_s, "%02x", m_md5.digest[i]); + } + +// if (crc16_i) +// *(crc16_i) = *(o.crc16); + +// if (crc32_i) +// *(crc32_i) = *(o.crc32); + + return result; +} + + +#if 0 +#define FILEFILE_LARGE_BUF (1024 * 1024) + + +typedef struct +{ + FILE *output; + int pos0; + int pos; + int similar; + unsigned char *buffer; + const char *fname0; + const char *fname; + int found; +} st_ucon64_filefile_t; + + +static inline int +ucon64_filefile_func (void *buffer, int n, void *object) +{ + st_ucon64_filefile_t *o = (st_ucon64_filefile_t *) object; + int i = 0, j = 0, len = MIN (FILEFILE_LARGE_BUF, fsizeof (o->fname) - o->pos); + char *b = (char *) buffer; + + ucon64_fread (o->buffer, o->pos, len, o->fname); + + for (; i < n; i++) + if (o->similar == TRUE ? // find start + *(b + i) == *(o->buffer + i) : + *(b + i) != *(o->buffer + i)) + { + for (j = 0; i + j < n; j++) + if (o->similar == TRUE ? // find end (len) + *(b + i + j) != *(o->buffer + i + j) : + *(b + i + j) == *(o->buffer + i + j)) + break; + + fprintf (o->output, "%s:\n", o->fname0); + dumper (o->output, &b[i], j, o->pos0 + i, DUMPER_HEX); + + fprintf (o->output, "%s:\n", o->fname); + dumper (o->output, &o->buffer[i], j, o->pos + i, DUMPER_HEX); + + fputc ('\n', o->output); + + i += j; + o->found++; + } + + return n; +} + + +void +ucon64_filefile (const char *filename1, int start1, const char *filename2, + int start2, int similar) +{ + st_ucon64_filefile_t o; + + printf ("Comparing %s", basename2 (ucon64.rom)); + if (ucon64.fname_arch[0]) + printf (" (%s)", basename2 (ucon64.fname_arch)); + printf (" with %s\n", filename1); + + if (one_file (filename1, filename2)) + { + printf ("%s and %s refer to one file\n", filename1, filename2); + return; + } + + if (fsizeof (filename1) < start1 || fsizeof (filename2) < start2) + return; + + if (!(o.buffer = (unsigned char *) malloc (FILEFILE_LARGE_BUF))) + { + fputs ("ERROR: File not found/out of memory\n", stderr); + return; // it's logical to stop for this file + } + + o.fname0 = filename1; + o.pos0 = start1; + + o.fname = filename2; + o.pos = start2; + o.output = stdout; + o.similar = similar; + + o.found = 0; + + quick_io_func (ucon64_filefile_func, FILEFILE_LARGE_BUF, &o, start1, + fsizeof (filename1), filename1, "rb"); + + if (o.found) + printf ("Found %d %s\n", + o.found, + similar ? (o.found == 1 ? "similarity" : "similarities") : + (o.found == 1 ? "difference" : "differences")); +} +#else +#define FILEFILE_LARGE_BUF +// When verifying if the code produces the same output when FILEFILE_LARGE_BUF +// is defined as when it's not, be sure to use the same buffer size +void +ucon64_filefile (const char *filename1, int start1, const char *filename2, + int start2, int similar) +{ + int base, fsize1, fsize2, len, chunksize1, chunksize2, readok = 1, + bytesread1, bytesread2, bytesleft1, bytesleft2, n_bytes = 0; +#ifdef FILEFILE_LARGE_BUF + int bufsize = 1024 * 1024; + unsigned char *buf1, *buf2; +#else + int bufsize = MAXBUFSIZE; + unsigned char buf1[MAXBUFSIZE], buf2[MAXBUFSIZE]; +#endif + FILE *file1, *file2; + + printf ("Comparing %s", basename2 (ucon64.rom)); + if (ucon64.fname_arch[0]) + printf (" (%s)", basename2 (ucon64.fname_arch)); + printf (" with %s\n", filename1); + + if (one_file (filename1, filename2)) + { + printf ("%s and %s refer to one file\n\n", filename1, filename2); + return; + } + + fsize1 = fsizeof (filename1); // fsizeof() returns size in bytes + fsize2 = fsizeof (filename2); + if (fsize1 < start1 || fsize2 < start2) + return; + +#ifdef FILEFILE_LARGE_BUF + if (!(buf1 = (unsigned char *) malloc (bufsize))) + { + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], bufsize); + return; + } + + if (!(buf2 = (unsigned char *) malloc (bufsize))) + { + free (buf1); + fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], bufsize); + return; + } +#endif + + if (!(file1 = fopen (filename1, "rb"))) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename1); +#ifdef FILEFILE_LARGE_BUF + free (buf1); + free (buf2); +#endif + return ; + } + if (!(file2 = fopen (filename2, "rb"))) + { + fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename2); + fclose (file1); +#ifdef FILEFILE_LARGE_BUF + free (buf1); + free (buf2); +#endif + return; + } + + fseek (file1, start1, SEEK_SET); + fseek (file2, start2, SEEK_SET); + bytesleft1 = fsize1; + bytesread1 = 0; + bytesleft2 = fsize2; + bytesread2 = 0; + + while (bytesleft1 > 0 && bytesread1 < fsize2 && readok) + { + chunksize1 = fread (buf1, 1, bufsize, file1); + if (chunksize1 == 0) + readok = 0; + else + { + bytesread1 += chunksize1; + bytesleft1 -= chunksize1; + } + + while (bytesleft2 > 0 && bytesread2 < bytesread1 && readok) + { + chunksize2 = fread (buf2, 1, chunksize1, file2); + if (chunksize2 == 0) + readok = 0; + else + { + base = 0; + while (base < chunksize2) + { + if (similar == TRUE ? + buf1[base] == buf2[base] : + buf1[base] != buf2[base]) + { + for (len = 0; base + len < chunksize2; len++) + if (similar == TRUE ? + buf1[base + len] != buf2[base + len] : + buf1[base + len] == buf2[base + len]) + break; + + printf ("%s:\n", filename1); + dumper (stdout, &buf1[base], len, start1 + base + bytesread2, DUMPER_HEX); + printf ("%s:\n", filename2); + dumper (stdout, &buf2[base], len, start2 + base + bytesread2, DUMPER_HEX); + fputc ('\n', stdout); + base += len; + n_bytes += len; + } + else + base++; + } + + bytesread2 += chunksize2; + bytesleft2 -= chunksize2; + } + } + } + + fclose (file1); + fclose (file2); +#ifdef FILEFILE_LARGE_BUF + free (buf1); + free (buf2); +#endif + + printf ("Found %d %s\n\n", + n_bytes, + similar ? (n_bytes == 1 ? "similarity" : "similarities") : + (n_bytes == 1 ? "difference" : "differences")); + + return; +} +#endif diff --git a/ucon64/2.0/src/ucon64_misc.h b/ucon64/2.0/src/ucon64_misc.h new file mode 100644 index 0000000..f2c52c3 --- /dev/null +++ b/ucon64/2.0/src/ucon64_misc.h @@ -0,0 +1,303 @@ +/* +ucon64_misc.h - miscellaneous functions for uCON64 + +Copyright (c) 1999 - 2004 NoisyB +Copyright (c) 2001 - 2004 dbjh +Copyright (c) 2001 Caz + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef UCON64_MISC_H +#define UCON64_MISC_H + +#ifdef HAVE_CONFIG_H +#include "config.h" // USE_DISCMAGE +#endif +#include "ucon64.h" // st_rominfo_t + + +/* + UCON64_DM_VERSION_MAJOR + UCON64_DM_VERSION_MINOR + UCON64_DM_VERSION_STEP min. version of libdiscmage supported by uCON64 + + ucon64_load_discmage() load libdiscmage + libdm_usage usage for libdiscmage + libdm_gauge gauge for libdiscmage +*/ +#ifdef USE_DISCMAGE +#include "libdiscmage/libdiscmage.h" // dm_image_t + +#define UCON64_DM_VERSION_MAJOR 0 +#define UCON64_DM_VERSION_MINOR 0 +#define UCON64_DM_VERSION_STEP 7 + +extern const st_getopt2_t libdm_usage[]; +extern int ucon64_load_discmage (void); +extern int libdm_gauge (int pos, int size); +#endif + + +/* + defines for unknown backup units/emulators +*/ +typedef struct // st_unknown_header +{ + /* + Don't create fields that are larger than one byte! For example size_low and + size_high could be combined in one unsigned short int. However, this gives + problems with little endian vs. big endian machines (e.g. writing the header + to disk). + */ + unsigned char size_low; + unsigned char size_high; + unsigned char emulation; + unsigned char hirom; + unsigned char emulation1; + unsigned char emulation2; + unsigned char pad[2]; + unsigned char id1; + unsigned char id2; + unsigned char type; + unsigned char pad2[501]; +} st_unknown_header_t; + +#define UNKNOWN_HEADER_START 0 +#define UNKNOWN_HEADER_LEN (sizeof (st_unknown_header_t)) + +extern int unknown_init (st_rominfo_t *rominfo); + +/* + usage for consoles not directly supported by uCON64 +*/ +extern const st_getopt2_t unknown_usage[], + atari_usage[], + cd32_usage[], + cdi_usage[], + channelf_usage[], + coleco_usage[], + gamecom_usage[], + gc_usage[], + gp32_usage[], + intelli_usage[], + odyssey2_usage[], + odyssey_usage[], + ps2_usage[], + real3do_usage[], + s16_usage[], + sat_usage[], + vboy_usage[], + vc4000_usage[], + vectrex_usage[], + xbox_usage[], + mame_usage[], + + ucon64_options_usage[], + ucon64_options_without_usage[], + ucon64_padding_usage[], + ucon64_patching_usage[]; + +#define NINTENDO_MAKER_LEN 684 + +extern const char *nintendo_maker[]; + +/* + uCON64 "workflow" objects + + We want to do things compile-time. Using ucon64_wf is necessary for VC 6. GCC + (3) accepts casts in struct initialisations. +*/ +enum +{ + WF_OBJ_ALL_SWITCH = 0, + WF_OBJ_ALL_DEFAULT, + WF_OBJ_ALL_DEFAULT_NO_SPLIT, + WF_OBJ_ALL_STOP, + WF_OBJ_ALL_STOP_NO_ROM, + WF_OBJ_ALL_DEFAULT_STOP_NO_ROM, + WF_OBJ_ALL_INIT, + WF_OBJ_ALL_INIT_PROBE, + WF_OBJ_ALL_INIT_PROBE_STOP, + WF_OBJ_ALL_INIT_PROBE_NO_ROM, + WF_OBJ_ALL_INIT_PROBE_NO_SPLIT, + WF_OBJ_ALL_INIT_PROBE_NO_CRC32, + WF_OBJ_ALL_INIT_NO_SPLIT, + + WF_OBJ_DC_SWITCH, + WF_OBJ_DC_DEFAULT, + WF_OBJ_DC_NO_ROM, + WF_OBJ_GB_SWITCH, + WF_OBJ_GB_DEFAULT, + WF_OBJ_GBA_SWITCH, + WF_OBJ_GBA_DEFAULT, + WF_OBJ_GEN_SWITCH, + WF_OBJ_GEN_DEFAULT, + WF_OBJ_GEN_DEFAULT_NO_SPLIT, + WF_OBJ_JAG_SWITCH, + WF_OBJ_LYNX_SWITCH, + WF_OBJ_LYNX_DEFAULT, + WF_OBJ_N64_SWITCH, + WF_OBJ_N64_DEFAULT, + WF_OBJ_N64_INIT_PROBE, + WF_OBJ_NG_SWITCH, + WF_OBJ_NG_DEFAULT, + WF_OBJ_NES_SWITCH, + WF_OBJ_NES_DEFAULT, + WF_OBJ_NGP_SWITCH, + WF_OBJ_PCE_SWITCH, + WF_OBJ_PCE_DEFAULT, + WF_OBJ_PSX_SWITCH, + WF_OBJ_SMS_SWITCH, + WF_OBJ_SMS_DEFAULT_NO_SPLIT, + WF_OBJ_SNES_SWITCH, + WF_OBJ_SNES_DEFAULT, + WF_OBJ_SNES_DEFAULT_NO_SPLIT, + WF_OBJ_SNES_NO_ROM, + WF_OBJ_SNES_INIT_PROBE, + WF_OBJ_SWAN_SWITCH, + + WF_OBJ_N64_STOP_NO_ROM, + WF_OBJ_N64_DEFAULT_STOP, + WF_OBJ_N64_DEFAULT_STOP_NO_ROM, + WF_OBJ_GEN_STOP_NO_ROM, + WF_OBJ_GEN_DEFAULT_STOP_NO_SPLIT_NO_ROM, + WF_OBJ_GBA_STOP_NO_ROM, + WF_OBJ_GBA_DEFAULT_STOP, + WF_OBJ_GBA_DEFAULT_STOP_NO_ROM, + WF_OBJ_SNES_STOP_NO_ROM, + WF_OBJ_SNES_DEFAULT_STOP_NO_ROM, + WF_OBJ_SNES_DEFAULT_STOP_NO_SPLIT_NO_ROM, + WF_OBJ_GB_STOP_NO_ROM, + WF_OBJ_GB_DEFAULT_STOP_NO_ROM, + WF_OBJ_LYNX_STOP_NO_ROM, + WF_OBJ_PCE_DEFAULT_STOP_NO_SPLIT_NO_ROM, + WF_OBJ_NGP_STOP_NO_ROM, + WF_OBJ_NGP_DEFAULT_STOP_NO_ROM, + WF_OBJ_NES_STOP_NO_ROM, + WF_OBJ_NES_DEFAULT_STOP_NO_SPLIT, + WF_OBJ_SMS_STOP_NO_ROM, + WF_OBJ_SMS_DEFAULT_STOP_NO_SPLIT_NO_ROM, + + WF_OBJ_GC_SWITCH, + WF_OBJ_S16_SWITCH, + WF_OBJ_ATA_SWITCH, + WF_OBJ_COLECO_SWITCH, + WF_OBJ_VBOY_SWITCH, + WF_OBJ_VEC_SWITCH, + WF_OBJ_INTELLI_SWITCH, + WF_OBJ_GP32_SWITCH, + WF_OBJ_PS2_SWITCH, + WF_OBJ_XBOX_SWITCH, + WF_OBJ_SAT_SWITCH, + WF_OBJ_3DO_SWITCH, + WF_OBJ_CD32_SWITCH, + WF_OBJ_CDI_SWITCH, +}; + +extern st_ucon64_obj_t ucon64_wf[]; + +/* + uCON64 messages + + usage example: fprintf (stdout, ucon64_msg[WROTE], filename); +*/ +enum +{ + PARPORT_ERROR = 0, + CONSOLE_ERROR, + WROTE, + OPEN_READ_ERROR, + OPEN_WRITE_ERROR, + READ_ERROR, + WRITE_ERROR, + BUFFER_ERROR, // not enough memory + ROM_BUFFER_ERROR, + FILE_BUFFER_ERROR, + DAT_NOT_FOUND, + DAT_NOT_ENABLED, + READ_CONFIG_FILE, + NO_LIB +}; + +extern const char *ucon64_msg[]; + +/* + ucon64_file_handler() handles backups (before modifying the ROM) and ROMs + inside archives. Read the comment at the header to + see how it and the flags work + remove_temp_file() remove possible temp file created by ucon64_file_handler() + ucon64_output_fname() + ucon64_gauge() wrapper for misc.c/gauge() + ucon64_testpad() test if ROM is padded + ucon64_testsplit() test if ROM is split + ucon64_configfile() configfile handling + ucon64_rename() DAT or internal header based rename + ucon64_e() emulator "frontend" + ucon64_pattern() change file based on patterns specified in pattern_fname +*/ +#define OF_FORCE_BASENAME 1 +#define OF_FORCE_SUFFIX 2 + +extern int ucon64_file_handler (char *dest, char *src, int flags); +extern void remove_temp_file (void); +extern char *ucon64_output_fname (char *requested_fname, int flags); +extern int ucon64_gauge (time_t init_time, int pos, int size); +extern int ucon64_testpad (const char *filename); +extern int ucon64_testsplit (const char *filename); +extern int ucon64_configfile (); +extern int ucon64_rename (int mode); +extern int ucon64_e (void); +extern int ucon64_pattern (st_rominfo_t *rominfo, const char *pattern_fname); +extern char *ucon64_temp_file; +extern int (*ucon64_testsplit_callback) (const char *filename); + +/* + Some general file stuff that MUST NOT and WILL NOT be written again and again + + ucon64_fread() same as fread but takes start and src is a filename + ucon64_fwrite() same as fwrite but takes start and dest is a filename; mode + is the same as fopen() modes + ucon64_fgetc() same as fgetc but takes filename instead of FILE and a pos + ucon64_fputc() same as fputc but takes filename instead of FILE and a pos + buf,s,bs,b,f,m == buffer,start,blksize,blks,filename,mode + ucon64_bswap16_n() bswap16() n bytes of buffer + ucon64_fbswap16() bswap16() len bytes of file from start + ucon64_fwswap32() wswap32() len bytes of file from start + ucon64_dump() file oriented wrapper for memdump() (uses the same flags) + ucon64_find() file oriented wrapper for memsearch() (uses the same flags) + ucon64_chksum() file oriented wrapper for chksum() + if (!sha1) {sha1 won't be calculated!} + ucon64_filefile() compare two files for similarities or differencies +*/ +#define ucon64_fgetc(f, p) (quick_io_c(0, p, f, "rb")) +#define ucon64_fputc(f, p, b, m) (quick_io_c(b, p, f, m)) +#define ucon64_fread(b, s, l, f) (quick_io(b, s, l, f, "rb")) +#define ucon64_fwrite(b, s, l, f, m) (quick_io((void *) b, s, l, f, m)) + +extern int ucon64_bswap16_n (void *buffer, int n); +extern void ucon64_fbswap16 (const char *fname, size_t start, size_t len); +extern void ucon64_fwswap32 (const char *fname, size_t start, size_t len); +extern void ucon64_dump (FILE *output, const char *filename, size_t start, + size_t len, uint32_t flags); +// Be sure the following constant doesn't conflict with the MEMCMP2_* constants +#define UCON64_FIND_QUIET (1 << 31) +extern int ucon64_find (const char *filename, size_t start, size_t len, + const char *search, int searchlen, uint32_t flags); +extern int ucon64_chksum (char *sha1, char *md5, unsigned int *crc32, // uint16_t *crc16, + const char *filename, size_t start); +extern void ucon64_filefile (const char *filename1, int start1, + const char *filename2, int start2, int similar); +#endif // #ifndef UCON64_MISC_H diff --git a/ucon64/2.0/src/ucon64_opts.c b/ucon64/2.0/src/ucon64_opts.c new file mode 100644 index 0000000..4000b70 --- /dev/null +++ b/ucon64/2.0/src/ucon64_opts.c @@ -0,0 +1,2193 @@ +/* +ucon64_opts.c - switch()'es for all uCON64 options + +Copyright (c) 2002 - 2004 NoisyB +Copyright (c) 2002 - 2004 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "misc/misc.h" +#include "misc/getopt2.h" +#include "misc/file.h" +#include "misc/string.h" +#ifdef USE_ZLIB +#include "misc/archive.h" +#endif +#include "ucon64.h" +#include "ucon64_misc.h" +#include "ucon64_dat.h" +#include "console/console.h" +#include "patch/patch.h" +#include "backup/backup.h" +#include "misc/chksum.h" + + +#ifdef _MSC_VER +// Visual C++ doesn't allow inline in C source code +#define inline __inline +#endif + + +int +ucon64_switches (st_ucon64_t *p) +{ +#ifdef USE_DISCMAGE + int x = 0; +#endif + int c = p->option; + const char *optarg = p->optarg; + + /* + Handle options or switches that cause other _options_ to be ignored except + other options of the same class (so the order in which they were specified + matters). + We have to do this here (not in ucon64_options()) or else other options + might be executed before these. + */ + switch (c) + { + /* + Many tools ignore other options if --help has been specified. We do the + same (compare with GNU tools). + */ + case UCON64_HELP: + ucon64_usage (ucon64.argc, ucon64.argv); + exit (0); + + /* + It's also common to exit after displaying version information. + On some configurations printf is a macro (Red Hat Linux 6.2 + GCC 3.2), + so we can't use preprocessor directives in the argument list. + */ + case UCON64_VER: + printf ("version: %s (%s)\n" + "platform: %s\n", + UCON64_VERSION_S, __DATE__, + CURRENT_OS_S); + +#ifdef WORDS_BIGENDIAN + puts ("endianess: big"); +#else + puts ("endianess: little"); +#endif + +#ifdef DEBUG + puts ("debug: yes"); +#else + puts ("debug: no"); +#endif + +#ifdef USE_PARALLEL +#ifdef USE_PPDEV + puts ("parallel port backup unit support: yes (ppdev)"); +#else + puts ("parallel port backup unit support: yes"); +#endif // USE_PPDEV +#else + puts ("parallel port backup unit support: no"); +#endif + +#ifdef USE_USB + puts ("USB port backup unit support: yes"); +#else + puts ("USB port backup unit support: no"); +#endif + + +#ifdef USE_ANSI_COLOR + puts ("ANSI colors enabled: yes"); +#else + puts ("ANSI colors enabled: no"); +#endif + +#ifdef USE_ZLIB + puts ("gzip and zip support: yes"); +#else + puts ("gzip and zip support: no"); +#endif + + printf ("configuration file %s %s\n", + // display the existence only for the config file (really helps solving problems) + access (ucon64.configfile, F_OK) ? "(not present):" : "(present): ", + ucon64.configfile); + +#ifdef USE_DISCMAGE + printf ("discmage DLL: "); + +#ifdef DLOPEN + puts (ucon64.discmage_path); +#else +#if defined __MSDOS__ + printf ("discmage.dxe"); +#elif defined __CYGWIN__ || defined _WIN32 + printf ("discmage.dll"); +#elif defined __APPLE__ // Mac OS X actually + printf ("libdiscmage.dylib"); +#elif defined __unix__ || defined __BEOS__ + printf ("libdiscmage.so"); +#else + printf ("unknown"); +#endif + puts (", dynamically linked"); +#endif // DLOPEN + + printf ("discmage enabled: %s\n", + ucon64.discmage_enabled ? "yes" : "no"); + + if (ucon64.discmage_enabled) + { + x = dm_get_version (); + printf ("discmage version: %d.%d.%d (%s)\n", + x >> 16, x >> 8, x, dm_get_version_s ()); + } + else + puts ("discmage version: not available"); +#endif // USE_DISCMAGE + + printf ("configuration directory: %s\n" + "DAT file directory: %s\n" + "entries in DATabase: %d\n" + "DATabase enabled: %s\n", + ucon64.configdir, + ucon64.datdir, + ucon64_dat_total_entries (), + ucon64.dat_enabled ? "yes" : "no"); + exit (0); + break; + + case UCON64_FRONTEND: + ucon64.frontend = 1; // used by ucon64_gauge() + break; + + case UCON64_NBAK: + ucon64.backup = 0; + break; + +#ifdef USE_ANSI_COLOR + case UCON64_NCOL: + ucon64.ansi_color = 0; + break; +#endif + + case UCON64_RIP: + case UCON64_MKTOC: + case UCON64_MKCUE: + case UCON64_MKSHEET: + case UCON64_BIN2ISO: + case UCON64_ISOFIX: + case UCON64_XCDRW: + case UCON64_DISC: + case UCON64_CDMAGE: + ucon64.force_disc = 1; + break; + + case UCON64_NS: + ucon64.split = 0; + break; + + case UCON64_HD: + ucon64.buheader_len = UNKNOWN_HEADER_LEN; + break; + + case UCON64_HDN: + ucon64.buheader_len = strtol (optarg, NULL, 10); + break; + + case UCON64_NHD: + ucon64.buheader_len = 0; + break; + + case UCON64_SWP: // deprecated + case UCON64_INT: + ucon64.interleaved = 1; + break; + + case UCON64_INT2: + ucon64.interleaved = 2; + break; + + case UCON64_NSWP: // deprecated + case UCON64_NINT: + ucon64.interleaved = 0; + break; + + case UCON64_PORT: +#ifdef USE_USB + if (!strnicmp (optarg, "usb", 3)) + { + if (strlen (optarg) >= 4) + ucon64.usbport = strtol (optarg + 3, NULL, 10) + 1; // usb0 => ucon64.usbport = 1 + else // we automatically detect the + ucon64.usbport = 1; // USB port in the F2A code + + /* + We don't want to make uCON64 behave different if --port=USB{n} is + specified *after* a transfer option (instead of before one), so we + have to reset ucon64.parport_needed here. + */ + ucon64.parport_needed = 0; + } + else +#endif + ucon64.parport = strtol (optarg, NULL, 16); + break; + +#ifdef USE_PARALLEL + /* + We detect the presence of these options here so that we can drop + privileges ASAP. + Note that the libcd64 options are not listed here. We cannot drop + privileges before libcd64 is initialised (after cd64_t.devopen() has been + called). + */ + case UCON64_XCMC: + case UCON64_XCMCT: + case UCON64_XDEX: + case UCON64_XDJR: + case UCON64_XF2A: // could be for USB version + case UCON64_XF2AMULTI: // idem + case UCON64_XF2AC: // idem + case UCON64_XF2AS: // idem + case UCON64_XF2AB: // idem + case UCON64_XFAL: + case UCON64_XFALMULTI: + case UCON64_XFALC: + case UCON64_XFALS: + case UCON64_XFALB: + case UCON64_XFIG: + case UCON64_XFIGS: + case UCON64_XFIGC: + case UCON64_XGBX: + case UCON64_XGBXS: + case UCON64_XGBXB: + case UCON64_XGD3: + case UCON64_XGD3R: + case UCON64_XGD3S: + case UCON64_XGD6: + case UCON64_XGD6R: + case UCON64_XGD6S: + case UCON64_XGG: + case UCON64_XGGS: + case UCON64_XGGB: + case UCON64_XLIT: + case UCON64_XMCCL: + case UCON64_XMCD: + case UCON64_XMD: + case UCON64_XMDS: + case UCON64_XMDB: + case UCON64_XMSG: + case UCON64_XPCE: + case UCON64_XPL: + case UCON64_XPLI: + case UCON64_XSF: + case UCON64_XSFS: + case UCON64_XSMC: + case UCON64_XSMCR: + case UCON64_XSMD: + case UCON64_XSMDS: + case UCON64_XSWC: + case UCON64_XSWC2: + case UCON64_XSWCR: + case UCON64_XSWCS: + case UCON64_XSWCC: + case UCON64_XV64: +#ifdef USE_USB + if (!ucon64.usbport) // no pport I/O if F2A option and USB F2A +#endif + ucon64.parport_needed = 1; + /* + We want to make this possible: + 1.) ucon64 + 2.) ucon64 --port= + The above works "automatically". The following type of command used to + be possible, but has been deprecated: + 3.) ucon64 + It has been removed, because it caused problems when specifying additional + switches without specifying the parallel port address. For example: + ucon64 -xfal -xfalm + This would be interpreted as: + ucon64 -xfal -xfalm + If has a name that starts with a number an I/O port associated + with that number will be accessed which might well have unwanted + results. We cannot check for valid I/O port numbers, because the I/O + port of the parallel port can be mapped to almost any 16-bit number. + */ +#if 0 + if (ucon64.parport == UCON64_UNKNOWN) + if (ucon64.argc >= 4) + if (access (ucon64.argv[ucon64.argc - 1], F_OK)) + // Yes, we don't get here if ucon64.argv[ucon64.argc - 1] is [0x]278, + // [0x]378 or [0x]3bc and a file with the same name (path) exists. + ucon64.parport = strtol (ucon64.argv[ucon64.argc - 1], NULL, 16); +#endif + break; + +#ifdef USE_LIBCD64 + case UCON64_XCD64: + case UCON64_XCD64B: + case UCON64_XCD64C: + case UCON64_XCD64E: + case UCON64_XCD64F: + case UCON64_XCD64M: + case UCON64_XCD64S: + // We don't really need the parallel port. We just have to make sure that + // privileges aren't dropped. + ucon64.parport_needed = 2; + break; + + case UCON64_XCD64P: + ucon64.io_mode = strtol (optarg, NULL, 10); + break; +#endif + + case UCON64_XCMCM: + ucon64.io_mode = strtol (optarg, NULL, 10); + break; + + case UCON64_XFALM: + case UCON64_XGBXM: + case UCON64_XPLM: + ucon64.parport_mode = UCON64_EPP; + break; + + case UCON64_XSWC_IO: + ucon64.io_mode = strtol (optarg, NULL, 16); + + if (ucon64.io_mode & SWC_IO_ALT_ROM_SIZE) + puts ("WARNING: I/O mode not yet implemented"); +#if 0 // all these constants are defined by default + if (ucon64.io_mode & (SWC_IO_SPC7110 | SWC_IO_SDD1 | SWC_IO_SA1 | SWC_IO_MMX2)) + puts ("WARNING: Be sure to compile swc.c with the appropriate constants defined"); +#endif + + if (ucon64.io_mode > SWC_IO_MAX) + { + printf ("WARNING: Invalid value for MODE (0x%x), using 0\n", ucon64.io_mode); + ucon64.io_mode = 0; + } + else + { + printf ("I/O mode: 0x%03x", ucon64.io_mode); + if (ucon64.io_mode) + { + char flagstr[100]; + + flagstr[0] = 0; + if (ucon64.io_mode & SWC_IO_FORCE_32MBIT) + strcat (flagstr, "force 32 Mbit dump, "); + if (ucon64.io_mode & SWC_IO_ALT_ROM_SIZE) + strcat (flagstr, "alternative ROM size method, "); + if (ucon64.io_mode & SWC_IO_SUPER_FX) + strcat (flagstr, "Super FX, "); + if (ucon64.io_mode & SWC_IO_SDD1) + strcat (flagstr, "S-DD1, "); + if (ucon64.io_mode & SWC_IO_SA1) + strcat (flagstr, "SA-1, "); + if (ucon64.io_mode & SWC_IO_SPC7110) + strcat (flagstr, "SPC7110, "); + if (ucon64.io_mode & SWC_IO_DX2_TRICK) + strcat (flagstr, "DX2 trick, "); + if (ucon64.io_mode & SWC_IO_MMX2) + strcat (flagstr, "Mega Man X 2, "); + if (ucon64.io_mode & SWC_IO_DUMP_BIOS) + strcat (flagstr, "dump BIOS, "); + + if (flagstr[0]) + flagstr[strlen (flagstr) - 2] = 0; + printf (" (%s)", flagstr); + } + fputc ('\n', stdout); + } + break; +#endif // USE_PARALLEL + + case UCON64_PATCH: // falling through, so --patch is an alias for --file + case UCON64_FILE: + ucon64.file = optarg; + break; + + case UCON64_I: + case UCON64_B: + case UCON64_A: + case UCON64_NA: + case UCON64_PPF: + case UCON64_NPPF: + case UCON64_IDPPF: + if (!ucon64.file || !ucon64.file[0]) + ucon64.file = ucon64.argv[ucon64.argc - 1]; + break; + +#if 0 + case UCON64_ROM: + if (optarg) + ucon64.rom = optarg; + break; +#endif + + case UCON64_O: + { + struct stat fstate; + int dir = 0; + + if (!stat (optarg, &fstate)) + if (S_ISDIR (fstate.st_mode)) + { + strcpy (ucon64.output_path, optarg); + if (ucon64.output_path[strlen (ucon64.output_path) - 1] != FILE_SEPARATOR) + strcat (ucon64.output_path, FILE_SEPARATOR_S); + dir = 1; + } + + if (!dir) + puts ("WARNING: Argument for -o must be a directory. Using current directory instead"); + } + break; + + case UCON64_NHI: + ucon64.snes_hirom = 0; + break; + + case UCON64_HI: + ucon64.snes_hirom = SNES_HIROM; + break; + + case UCON64_EROM: + ucon64.snes_header_base = SNES_EROM; + break; + + case UCON64_BS: + ucon64.bs_dump = 1; + break; + + case UCON64_NBS: + ucon64.bs_dump = 0; + break; + + case UCON64_CTRL: + if (UCON64_ISSET (ucon64.controller)) + ucon64.controller |= 1 << strtol (optarg, NULL, 10); + else + ucon64.controller = 1 << strtol (optarg, NULL, 10); + break; + + case UCON64_CTRL2: + if (UCON64_ISSET (ucon64.controller2)) + ucon64.controller2 |= 1 << strtol (optarg, NULL, 10); + else + ucon64.controller2 = 1 << strtol (optarg, NULL, 10); + break; + + case UCON64_NTSC: + if (!UCON64_ISSET (ucon64.tv_standard)) + ucon64.tv_standard = 0; + else if (ucon64.tv_standard == 1) + ucon64.tv_standard = 2; // code for NTSC/PAL (NES UNIF) + break; + + case UCON64_PAL: + if (!UCON64_ISSET (ucon64.tv_standard)) + ucon64.tv_standard = 1; + else if (ucon64.tv_standard == 0) + ucon64.tv_standard = 2; // code for NTSC/PAL (NES UNIF) + break; + + case UCON64_BAT: + ucon64.battery = 1; + break; + + case UCON64_NBAT: + ucon64.battery = 0; + break; + + case UCON64_VRAM: + ucon64.vram = 1; + break; + + case UCON64_NVRAM: + ucon64.vram = 0; + break; + + case UCON64_MIRR: + ucon64.mirror = strtol (optarg, NULL, 10); + break; + + case UCON64_MAPR: + ucon64.mapr = optarg; // pass the _string_, it can be a + break; // board name + + case UCON64_CMNT: + ucon64.comment = optarg; + break; + + case UCON64_DUMPINFO: + ucon64.use_dump_info = 1; + ucon64.dump_info = optarg; + break; + + case UCON64_RR83: + ucon64.fname_len = UCON64_RR83; + break; + + case UCON64_FORCE63: + ucon64.fname_len = UCON64_FORCE63; + break; + + case UCON64_Q: + case UCON64_QQ: // for now -qq is equivalent to -q + ucon64.quiet = 1; + break; + + case UCON64_V: + ucon64.quiet = -1; + break; + + case UCON64_SSIZE: + ucon64.part_size = strtol (optarg, NULL, 10) * MBIT; + break; + + case UCON64_ID: + ucon64.id = 1; + break; + + case UCON64_REGION: + if (optarg[1] == 0 && toupper (optarg[0]) == 'X') // be insensitive to case + ucon64.region = 256; + else + ucon64.region = strtol (optarg, NULL, 10); + break; + + default: + break; + } + + return 0; +} + + +static inline char * +to_func (char *s, int len, int (*func) (int)) +{ + char *p = s; + + for (; len > 0; p++, len--) + *p = func (*p); + + return s; +} + + +static inline int +toprint (int c) +{ + if (isprint (c)) + return c; + + // characters that also work with printf() +#ifdef USE_ANSI_COLOR + if (c == '\x1b') + return ucon64.ansi_color ? c : '.'; +#endif + + return strchr ("\t\n\r", c) ? c : '.'; +} + + +int +ucon64_options (st_ucon64_t *p) +{ +#ifdef USE_PARALLEL + int enableRTS = -1; // for UCON64_XSWC & UCON64_XSWC2 +#endif + int value = 0, x = 0, padded; + unsigned int checksum; + char buf[MAXBUFSIZE], src_name[FILENAME_MAX], dest_name[FILENAME_MAX], + *ptr = NULL; + struct stat fstate; + int c = p->option; + const char *optarg = p->optarg; + + if (ucon64.rom) + { + strcpy (src_name, ucon64.rom); + strcpy (dest_name, ucon64.rom); + } + + switch (c) + { + case UCON64_CRCHD: // deprecated + value = UNKNOWN_HEADER_LEN; + case UCON64_CRC: + if (!value) + value = ucon64.rominfo ? ucon64.rominfo->buheader_len : ucon64.buheader_len; + fputs (basename2 (ucon64.rom), stdout); + if (ucon64.fname_arch[0]) + printf (" (%s)\n", basename2 (ucon64.fname_arch)); + else + fputc ('\n', stdout); + checksum = 0; + ucon64_chksum (NULL, NULL, &checksum, ucon64.rom, value); + printf ("Checksum (CRC32): 0x%08x\n\n", checksum); + break; + + case UCON64_SHA1: + if (!value) + value = ucon64.rominfo ? ucon64.rominfo->buheader_len : ucon64.buheader_len; + fputs (basename2 (ucon64.rom), stdout); + if (ucon64.fname_arch[0]) + printf (" (%s)\n", basename2 (ucon64.fname_arch)); + else + fputc ('\n', stdout); + ucon64_chksum (buf, NULL, NULL, ucon64.rom, value); + printf ("Checksum (SHA1): 0x%s\n\n", buf); + break; + + case UCON64_MD5: + if (!value) + value = ucon64.rominfo ? ucon64.rominfo->buheader_len : ucon64.buheader_len; + fputs (basename2 (ucon64.rom), stdout); + if (ucon64.fname_arch[0]) + printf (" (%s)\n", basename2 (ucon64.fname_arch)); + else + fputc ('\n', stdout); + ucon64_chksum (NULL, buf, NULL, ucon64.rom, value); + printf ("Checksum (MD5): 0x%s\n\n", buf); + break; + + case UCON64_RL: + strcpy (buf, basename2 (ucon64.rom)); + printf ("Renaming \"%s\" to ", buf); + strlwr (buf); + printf ("\"%s\"\n", buf); + ucon64_output_fname (buf, OF_FORCE_BASENAME | OF_FORCE_SUFFIX); + rename2 (ucon64.rom, buf); + strcpy ((char *) ucon64.rom, buf); + break; + + case UCON64_RU: + strcpy (buf, basename2 (ucon64.rom)); + printf ("Renaming \"%s\" to ", buf); + strupr (buf); + printf ("\"%s\"\n", buf); + ucon64_output_fname (buf, OF_FORCE_BASENAME | OF_FORCE_SUFFIX); + rename2 (ucon64.rom, buf); + strcpy ((char *) ucon64.rom, buf); + break; + + case UCON64_HEX: + ucon64_dump (stdout, ucon64.rom, + optarg ? MAX (strtol2 (optarg, NULL), 0) : 0, + ucon64.file_size, DUMPER_HEX); + break; + + case UCON64_DUAL: + ucon64_dump (stdout, ucon64.rom, + optarg ? MAX (strtol2 (optarg, NULL), 0) : 0, + ucon64.file_size, DUMPER_DUAL); + break; + + case UCON64_CODE: + ucon64_dump (stdout, ucon64.rom, + optarg ? MAX (strtol2 (optarg, NULL), 0) : 0, + ucon64.file_size, DUMPER_CODE); + break; + + case UCON64_PRINT: + ucon64_dump (stdout, ucon64.rom, + optarg ? MAX (strtol2 (optarg, NULL), 0) : 0, + ucon64.file_size, DUMPER_PRINT); + break; + + case UCON64_C: + ucon64_filefile (optarg, 0, ucon64.rom, 0, FALSE); + break; + + case UCON64_CS: + ucon64_filefile (optarg, 0, ucon64.rom, 0, TRUE); + break; + + case UCON64_FIND: + ucon64_find (ucon64.rom, value, ucon64.file_size, optarg, + strlen (optarg), MEMCMP2_WCARD ('?')); + break; + + case UCON64_FINDR: + ucon64_find (ucon64.rom, value, ucon64.file_size, optarg, + strlen (optarg), MEMCMP2_WCARD ('?') | MEMCMP2_REL); + break; + + case UCON64_FINDI: + ucon64_find (ucon64.rom, value, ucon64.file_size, optarg, strlen (optarg), + MEMCMP2_WCARD ('?') | MEMCMP2_CASE); + break; + + case UCON64_PADHD: // deprecated + value = UNKNOWN_HEADER_LEN; + case UCON64_P: + case UCON64_PAD: + if (!value && ucon64.rominfo) + value = ucon64.rominfo->buheader_len; + ucon64_file_handler (dest_name, src_name, 0); + + fcopy (src_name, 0, ucon64.file_size, dest_name, "wb"); + if (truncate2 (dest_name, ucon64.file_size + (MBIT - ((ucon64.file_size - value) % MBIT))) == -1) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], dest_name); // msg is not a typo + exit (1); + } + + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + break; + + case UCON64_PADN: + ucon64_file_handler (dest_name, src_name, 0); + + fcopy (src_name, 0, ucon64.file_size, dest_name, "wb"); + if (truncate2 (dest_name, strtol (optarg, NULL, 10) + + (ucon64.rominfo ? ucon64.rominfo->buheader_len : 0)) == -1) + { + fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], dest_name); // msg is not a typo + exit (1); + } + + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + break; + + case UCON64_ISPAD: + if ((padded = ucon64_testpad (ucon64.rom)) != -1) + { + if (!padded) + puts ("Padded: No\n"); + else + printf ("Padded: Maybe, %d Bytes (%.4f Mb)\n\n", padded, + (float) padded / MBIT); + } + break; + + case UCON64_STRIP: + ucon64_file_handler (dest_name, src_name, 0); + fcopy (src_name, 0, ucon64.file_size - strtol (optarg, NULL, 10), + dest_name, "wb"); + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + break; + + case UCON64_STP: + ucon64_file_handler (dest_name, src_name, 0); + fcopy (src_name, 512, ucon64.file_size, dest_name, "wb"); + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + break; + + case UCON64_STPN: + ucon64_file_handler (dest_name, src_name, 0); + fcopy (src_name, strtol (optarg, NULL, 10), ucon64.file_size, dest_name, "wb"); + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + break; + + case UCON64_INS: + ucon64_file_handler (dest_name, src_name, 0); + memset (buf, 0, 512); + ucon64_fwrite (buf, 0, 512, dest_name, "wb"); + fcopy (src_name, 0, ucon64.file_size, dest_name, "ab"); + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + break; + + case UCON64_INSN: + ucon64_file_handler (dest_name, src_name, 0); + value = strtol (optarg, NULL, 10); + if (value <= MAXBUFSIZE) + { + memset (buf, 0, value); + ucon64_fwrite (buf, 0, value, dest_name, "wb"); + } + else + { + int bytesleft = value, bytestowrite; + memset (buf, 0, MAXBUFSIZE); + while (bytesleft > 0) + { + bytestowrite = bytesleft <= MAXBUFSIZE ? bytesleft : MAXBUFSIZE; + ucon64_fwrite (buf, 0, bytestowrite, dest_name, + bytesleft == value ? "wb" : "ab"); // we have to use "wb" for + bytesleft -= bytestowrite; // the first iteration + } + } + fcopy (src_name, 0, ucon64.file_size, dest_name, "ab"); + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + break; + + case UCON64_A: + aps_apply (ucon64.rom, ucon64.file); + break; + + case UCON64_B: + bsl_apply (ucon64.rom, ucon64.file); + break; + + case UCON64_I: + ips_apply (ucon64.rom, ucon64.file); + break; + + case UCON64_PPF: + ppf_apply (ucon64.rom, ucon64.file); + break; + + case UCON64_MKA: + aps_create (optarg, ucon64.rom); // original, modified + break; + + case UCON64_MKI: + ips_create (optarg, ucon64.rom); // original, modified + break; + + case UCON64_MKPPF: + ppf_create (optarg, ucon64.rom); // original, modified + break; + + case UCON64_NA: + aps_set_desc (ucon64.rom, optarg); + break; + + case UCON64_NPPF: + ppf_set_desc (ucon64.rom, optarg); + break; + + case UCON64_IDPPF: + ppf_set_fid (ucon64.rom, optarg); + break; + + case UCON64_SCAN: + case UCON64_LSD: + if (ucon64.dat_enabled) + { + if (ucon64.crc32) + { + fputs (basename2 (ucon64.rom), stdout); + if (ucon64.fname_arch[0]) + printf (" (%s)\n", basename2 (ucon64.fname_arch)); + else + fputc ('\n', stdout); + // Use ucon64.fcrc32 for SNES & Genesis interleaved/N64 non-interleaved + printf ("Checksum (CRC32): 0x%08x\n", ucon64.fcrc32 ? + ucon64.fcrc32 : ucon64.crc32); + ucon64_dat_nfo ((st_ucon64_dat_t *) ucon64.dat, 1); + fputc ('\n', stdout); + } + } + else + printf (ucon64_msg[DAT_NOT_ENABLED]); + break; + + case UCON64_LSV: + if (ucon64.rominfo) + ucon64_nfo (); + break; + + case UCON64_LS: + if (ucon64.rominfo) + ptr = ucon64.rominfo->name; + + if (ucon64.dat) + { + if (!ptr) + ptr = ((st_ucon64_dat_t *) ucon64.dat)->name; + else if (!ptr[0]) + ptr = ((st_ucon64_dat_t *) ucon64.dat)->name; + } + + if (ptr) + if (ptr[0]) + { + if (stat (ucon64.rom, &fstate) != 0) + break; + strftime (buf, 13, "%b %d %Y", localtime (&fstate.st_mtime)); + printf ("%-31.31s %10d %s %s", to_func (ptr, strlen (ptr), toprint), + ucon64.file_size, buf, basename2 (ucon64.rom)); + if (ucon64.fname_arch[0]) + printf (" (%s)\n", basename2 (ucon64.fname_arch)); + else + fputc ('\n', stdout); + } + break; + + case UCON64_RENAME: + ucon64_rename (UCON64_RENAME); + break; + + case UCON64_RR83: + ucon64.fname_len = UCON64_RR83; + case UCON64_RROM: + ucon64_rename (UCON64_RROM); + break; + +#ifdef USE_DISCMAGE + case UCON64_BIN2ISO: + case UCON64_ISOFIX: + case UCON64_RIP: + case UCON64_CDMAGE: + if (ucon64.discmage_enabled) + { + uint32_t flags = 0; + + switch (c) + { + case UCON64_BIN2ISO: + flags |= DM_2048; // DM_2048 read sectors and convert to 2048 Bytes + break; + + case UCON64_ISOFIX: + flags |= DM_FIX; // DM_FIX read sectors and fix (if needed/possible) + break; + + case UCON64_CDMAGE: +// flags |= DM_CDMAGE; + break; + } + + ucon64.image = dm_reopen (ucon64.rom, 0, (dm_image_t *) ucon64.image); + if (ucon64.image) + { + int track = strtol (optarg, NULL, 10); + if (track < 1) + track = 1; + track--; // decrement for dm_rip() + + printf ("Writing track: %d\n\n", track + 1); + + dm_set_gauge ((void (*)(int, int)) &libdm_gauge); + dm_rip ((dm_image_t *) ucon64.image, track, flags); + fputc ('\n', stdout); + } + } + else + printf (ucon64_msg[NO_LIB], ucon64.discmage_path); + break; + + case UCON64_MKTOC: + case UCON64_MKCUE: + case UCON64_MKSHEET: + if (ucon64.discmage_enabled) + { + if (ucon64.image) + { + char buf[FILENAME_MAX]; + strcpy (buf, ((dm_image_t *) ucon64.image)->fname); + + if (c == UCON64_MKTOC || c == UCON64_MKSHEET) + { + set_suffix (buf, ".toc"); + ucon64_file_handler (buf, NULL, 0); + + if (!dm_toc_write ((dm_image_t *) ucon64.image)) + printf (ucon64_msg[WROTE], basename2 (buf)); + else + fputs ("ERROR: Could not generate toc sheet\n", stderr); + } + + if (c == UCON64_MKCUE || c == UCON64_MKSHEET) + { + set_suffix (buf, ".cue"); + ucon64_file_handler (buf, NULL, 0); + + if (!dm_cue_write ((dm_image_t *) ucon64.image)) + printf (ucon64_msg[WROTE], basename2 (buf)); + else + fputs ("ERROR: Could not generate cue sheet\n", stderr); + } + } + } + else + printf (ucon64_msg[NO_LIB], ucon64.discmage_path); + break; + + case UCON64_XCDRW: + if (ucon64.discmage_enabled) + { +// dm_set_gauge ((void *) &libdm_gauge); + if (!access (ucon64.rom, F_OK)) + dm_disc_write ((dm_image_t *) ucon64.image); + else + dm_disc_read ((dm_image_t *) ucon64.image); + } + else + printf (ucon64_msg[NO_LIB], ucon64.discmage_path); + break; +#endif // USE_DISCMAGE + + case UCON64_DB: + if (ucon64.quiet > -1) // -db + -v == -dbv + { + if (ucon64.dat_enabled) + { + ucon64_dat_view (ucon64.console, 0); + printf ("TIP: %s " OPTION_LONG_S "db " OPTION_LONG_S "nes" + " would show only information about known NES ROMs\n\n", + basename2 (ucon64.argv[0])); + } + else + fputs (ucon64_msg[DAT_NOT_ENABLED], stdout); + break; + } + + case UCON64_DBV: + if (ucon64.dat_enabled) + { + ucon64_dat_view (ucon64.console, 1); + printf ("TIP: %s " OPTION_LONG_S "dbv " OPTION_LONG_S "nes" + " would show only information about known NES ROMs\n\n", + basename2 (ucon64.argv[0])); + } + else + fputs (ucon64_msg[DAT_NOT_ENABLED], stdout); + break; + + case UCON64_DBS: + if (ucon64.dat_enabled) + { + ucon64.crc32 = 0; + sscanf (optarg, "%x", &ucon64.crc32); + + if (!(ucon64.dat = (st_ucon64_dat_t *) ucon64_dat_search (ucon64.crc32, NULL))) + { + printf (ucon64_msg[DAT_NOT_FOUND], ucon64.crc32); + printf ("TIP: Be sure to install the right DAT files in %s\n", ucon64.datdir); + } + else + { + ucon64_dat_nfo ((st_ucon64_dat_t *) ucon64.dat, 1); + printf ("\n" + "TIP: %s " OPTION_LONG_S "dbs" OPTARG_S "0x%08x " OPTION_LONG_S + "nes would search only for a NES ROM\n\n", + basename2 (ucon64.argv[0]), ucon64.crc32); + } + } + else + fputs (ucon64_msg[DAT_NOT_ENABLED], stdout); + break; + + case UCON64_MKDAT: + ucon64_create_dat (optarg, ucon64.rom, ucon64.rominfo ? ucon64.rominfo->buheader_len : 0); + break; + + case UCON64_MULTI: + switch (ucon64.console) + { + case UCON64_GBA: + gba_multi (strtol (optarg, NULL, 10) * MBIT, NULL); + break; + case UCON64_GEN: + genesis_multi (strtol (optarg, NULL, 10) * MBIT, NULL); + break; + case UCON64_PCE: + pcengine_multi (strtol (optarg, NULL, 10) * MBIT, NULL); + break; + case UCON64_SMS: // Sega Master System *and* Game Gear + sms_multi (strtol (optarg, NULL, 10) * MBIT, NULL); + break; + case UCON64_SNES: + snes_multi (strtol (optarg, NULL, 10) * MBIT, NULL); + break; + default: + return -1; + } + break; + + case UCON64_E: + ucon64_e (); + break; + + case UCON64_1991: + genesis_1991 (ucon64.rominfo); + break; + + case UCON64_B0: + lynx_b0 (ucon64.rominfo, optarg); + break; + + case UCON64_B1: + lynx_b1 (ucon64.rominfo, optarg); + break; + + case UCON64_BIN: + genesis_bin (ucon64.rominfo); + break; + + case UCON64_BIOS: + neogeo_bios (optarg); + break; + + case UCON64_BOT: + n64_bot (ucon64.rominfo, optarg); + break; + + case UCON64_CHK: + switch (ucon64.console) + { + case UCON64_GB: + gameboy_chk (ucon64.rominfo); + break; + case UCON64_GBA: + gba_chk (ucon64.rominfo); + break; + case UCON64_GEN: + genesis_chk (ucon64.rominfo); + break; + case UCON64_N64: + n64_chk (ucon64.rominfo); + break; + case UCON64_SMS: + sms_chk (ucon64.rominfo); + break; + case UCON64_SNES: + snes_chk (ucon64.rominfo); + break; + case UCON64_SWAN: + swan_chk (ucon64.rominfo); + break; + default: +// The next msg has already been printed +// fputs (ucon64_msg[CONSOLE_ERROR], stderr); + return -1; + } + break; + + case UCON64_COL: + snes_col (optarg); + break; + + case UCON64_CRP: + gba_crp (ucon64.rominfo, optarg); + break; + + case UCON64_DBUH: + snes_buheader_info (ucon64.rominfo); + break; + + case UCON64_SWAP: + case UCON64_DINT: + switch (ucon64.console) + { + case UCON64_NES: + nes_dint (); + break; + case UCON64_PCE: + pcengine_swap (ucon64.rominfo); + break; + case UCON64_SNES: + snes_dint (ucon64.rominfo); + break; + default: // Nintendo 64 + puts ("Converting file..."); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (src_name, 0, ucon64.file_size, dest_name, "wb"); + ucon64_fbswap16 (dest_name, 0, ucon64.file_size); + printf (ucon64_msg[WROTE], dest_name); + break; + } + break; + + case UCON64_SWAP2: + // --swap2 is currently used only for Nintendo 64 + puts ("Converting file..."); + ucon64_file_handler (dest_name, NULL, 0); + fcopy (src_name, 0, ucon64.file_size, dest_name, "wb"); + ucon64_fwswap32 (dest_name, 0, ucon64.file_size); + printf (ucon64_msg[WROTE], dest_name); + break; + + case UCON64_DMIRR: + snes_demirror (ucon64.rominfo); + break; + + case UCON64_DNSRT: + snes_densrt (ucon64.rominfo); + break; + + case UCON64_F: + switch (ucon64.console) + { + case UCON64_GEN: + genesis_f (ucon64.rominfo); + break; + case UCON64_N64: + n64_f (ucon64.rominfo); + break; + case UCON64_PCE: + pcengine_f (ucon64.rominfo); + break; + case UCON64_SNES: + snes_f (ucon64.rominfo); + break; + default: +// The next msg has already been printed +// fputs (ucon64_msg[CONSOLE_ERROR], stderr); + return -1; + } + break; + + case UCON64_FDS: + nes_fds (); + break; + + case UCON64_FDSL: + nes_fdsl (ucon64.rominfo, NULL); + break; + + case UCON64_FFE: + nes_ffe (ucon64.rominfo); + break; + + case UCON64_FIG: + snes_fig (ucon64.rominfo); + break; + + case UCON64_FIGS: + snes_figs (ucon64.rominfo); + break; + + case UCON64_GBX: + gameboy_gbx (ucon64.rominfo); + break; + + case UCON64_GD3: + snes_gd3 (ucon64.rominfo); + break; + + case UCON64_GD3S: + snes_gd3s (ucon64.rominfo); + break; + + case UCON64_GG: + switch (ucon64.console) + { + case UCON64_GB: + case UCON64_GEN: + case UCON64_NES: + case UCON64_SMS: + case UCON64_SNES: + gg_apply (ucon64.rominfo, optarg); + break; + default: + fputs ("ERROR: Cannot apply Game Genie code for this ROM/console\n", stderr); +// The next msg has already been printed +// fputs (ucon64_msg[CONSOLE_ERROR], stderr); + return -1; + } + break; + + case UCON64_GGD: + gg_display (ucon64.rominfo, optarg); + break; + + case UCON64_GGE: + gg_display (ucon64.rominfo, optarg); + break; + + case UCON64_INES: + nes_ines (); + break; + + case UCON64_INESHD: + nes_ineshd (ucon64.rominfo); + break; + +#if 0 + case UCON64_IP: + break; +#endif + + case UCON64_VMS: + break; + + case UCON64_PARSE: + dc_parse (optarg); + break; + + case UCON64_MKIP: + dc_mkip (); + break; + + case UCON64_J: + switch (ucon64.console) + { + case UCON64_GEN: + genesis_j (ucon64.rominfo); + break; + case UCON64_NES: + nes_j (NULL); + break; + case UCON64_SNES: + snes_j (ucon64.rominfo); + break; + default: +// The next msg has already been printed +// fputs (ucon64_msg[CONSOLE_ERROR], stderr); + return -1; + } + break; + + case UCON64_K: + snes_k (ucon64.rominfo); + break; + + case UCON64_L: + snes_l (ucon64.rominfo); + break; + + case UCON64_LNX: + lynx_lnx (ucon64.rominfo); + break; + + case UCON64_LOGO: + switch (ucon64.console) + { + case UCON64_GB: + gameboy_logo (ucon64.rominfo); + break; + case UCON64_GBA: + gba_logo (ucon64.rominfo); + break; + default: +// The next msg has already been printed +// fputs (ucon64_msg[CONSOLE_ERROR], stderr); + return -1; + } + break; + + case UCON64_LSRAM: + n64_sram (ucon64.rominfo, optarg); + break; + + case UCON64_LYX: + lynx_lyx (ucon64.rominfo); + break; + + case UCON64_MGD: + switch (ucon64.console) + { + case UCON64_GB: + gameboy_mgd (ucon64.rominfo); + break; + case UCON64_GEN: + genesis_mgd (ucon64.rominfo); + break; + case UCON64_NG: + neogeo_mgd (); + break; + case UCON64_PCE: + pcengine_mgd (ucon64.rominfo); + break; + case UCON64_SMS: + sms_mgd (ucon64.rominfo, UCON64_SMS); + break; + case UCON64_SNES: + snes_mgd (ucon64.rominfo); + break; + default: +// The next msg has already been printed +// fputs (ucon64_msg[CONSOLE_ERROR], stderr); + return -1; + } + break; + + case UCON64_MGDGG: + sms_mgd (ucon64.rominfo, UCON64_GAMEGEAR); + break; + + case UCON64_MSG: + pcengine_msg (ucon64.rominfo); + break; + + case UCON64_N: +// if (strlen (optarg) == 0) +// break; + switch (ucon64.console) + { + case UCON64_GB: + gameboy_n (ucon64.rominfo, optarg); + break; + case UCON64_GBA: + gba_n (ucon64.rominfo, optarg); + break; + case UCON64_GEN: + genesis_n (ucon64.rominfo, optarg); + break; + case UCON64_LYNX: + lynx_n (ucon64.rominfo, optarg); + break; + case UCON64_N64: + n64_n (ucon64.rominfo, optarg); + break; + case UCON64_NES: + nes_n (optarg); + break; + case UCON64_SNES: + snes_n (ucon64.rominfo, optarg); + break; + default: +// The next msg has already been printed +// fputs (ucon64_msg[CONSOLE_ERROR], stderr); + return -1; + } + break; + + case UCON64_N2: +// if (strlen (optarg) == 0) +// break; + genesis_n2 (ucon64.rominfo, optarg); + break; + + case UCON64_N2GB: + gameboy_n2gb (ucon64.rominfo, optarg); + break; + + case UCON64_NROT: + lynx_nrot (ucon64.rominfo); + break; + + case UCON64_PASOFAMI: + nes_pasofami (); + break; + + case UCON64_PATTERN: + ucon64_pattern (ucon64.rominfo, optarg); + break; + + case UCON64_POKE: + ucon64_file_handler (dest_name, src_name, 0); + fcopy (src_name, 0, ucon64.file_size, dest_name, "wb"); + + sscanf (optarg, "%x:%x", &x, &value); + if (x >= ucon64.file_size) + { + fprintf (stderr, "ERROR: Offset 0x%x is too large\n", x); + remove (dest_name); + break; + } + fputc ('\n', stdout); + buf[0] = ucon64_fgetc (dest_name, x); + dumper (stdout, buf, 1, x, DUMPER_HEX); + + ucon64_fputc (dest_name, x, value, "r+b"); + + buf[0] = value; + dumper (stdout, buf, 1, x, DUMPER_HEX); + fputc ('\n', stdout); + + printf (ucon64_msg[WROTE], dest_name); + remove_temp_file (); + break; + + case UCON64_ROTL: + lynx_rotl (ucon64.rominfo); + break; + + case UCON64_ROTR: + lynx_rotr (ucon64.rominfo); + break; + + case UCON64_S: + switch (ucon64.console) + { + case UCON64_GEN: + genesis_s (ucon64.rominfo); + break; + case UCON64_NES: + nes_s (); + break; + case UCON64_NG: + neogeo_s (); + break; + case UCON64_SNES: + snes_s (ucon64.rominfo); + break; + default: +// The next msg has already been printed +// fputs (ucon64_msg[CONSOLE_ERROR], stderr); + return -1; + } + break; + + case UCON64_SAM: + neogeo_sam (optarg); + break; + + case UCON64_SCR: + dc_scramble (); + break; + + case UCON64_SGB: + gameboy_sgb (ucon64.rominfo); + break; + + case UCON64_SMC: + snes_smc (ucon64.rominfo); + break; + + case UCON64_SMD: + switch (ucon64.console) + { + case UCON64_GEN: + genesis_smd (ucon64.rominfo); + break; + case UCON64_SMS: + sms_smd (ucon64.rominfo); + break; + default: + return -1; + } + break; + + case UCON64_SMDS: + switch (ucon64.console) + { + case UCON64_GEN: + genesis_smds (); + break; + case UCON64_SMS: + sms_smds (); + break; + default: + return -1; + } + break; + + case UCON64_SRAM: + gba_sram (); + break; + + case UCON64_SSC: + gameboy_ssc (ucon64.rominfo); + break; + + case UCON64_SWC: + snes_swc (ucon64.rominfo); + break; + + case UCON64_SWCS: + snes_swcs (ucon64.rominfo); + break; + + case UCON64_UFO: + snes_ufo (ucon64.rominfo); + break; + + case UCON64_UFOS: + snes_ufos (ucon64.rominfo); + break; + + case UCON64_UNIF: + nes_unif (); + break; + + case UCON64_UNSCR: + dc_unscramble (); + break; + + case UCON64_USMS: + n64_usms (ucon64.rominfo, optarg); + break; + + case UCON64_V64: + n64_v64 (ucon64.rominfo); + break; + +#ifdef USE_PARALLEL + /* + It doesn't make sense to continue after executing a (send) backup option + ("multizip"). Don't return, but use break instead. ucon64_execute_options() + checks if an option was used that should stop uCON64. + */ +#ifdef USE_LIBCD64 + case UCON64_XCD64: + if (access (ucon64.rom, F_OK) != 0) + cd64_read_rom (ucon64.rom, 64); + else + cd64_write_rom (ucon64.rom); + fputc ('\n', stdout); + break; + + case UCON64_XCD64C: + if (!access (ucon64.rom, F_OK) && ucon64.backup) + printf ("Wrote backup to: %s\n", mkbak (ucon64.rom, BAK_MOVE)); + cd64_read_rom (ucon64.rom, strtol (optarg, NULL, 10)); + fputc ('\n', stdout); + break; + + case UCON64_XCD64B: + cd64_write_bootemu (ucon64.rom); + fputc ('\n', stdout); + break; + + case UCON64_XCD64S: + if (access (ucon64.rom, F_OK) != 0) + cd64_read_sram (ucon64.rom); + else + cd64_write_sram (ucon64.rom); + fputc ('\n', stdout); + break; + + case UCON64_XCD64F: + if (access (ucon64.rom, F_OK) != 0) + cd64_read_flashram (ucon64.rom); + else + cd64_write_flashram (ucon64.rom); + fputc ('\n', stdout); + break; + + case UCON64_XCD64E: + if (access (ucon64.rom, F_OK) != 0) + cd64_read_eeprom (ucon64.rom); + else + cd64_write_eeprom (ucon64.rom); + fputc ('\n', stdout); + break; + + case UCON64_XCD64M: + if (access (ucon64.rom, F_OK) != 0) + cd64_read_mempack (ucon64.rom, strtol (optarg, NULL, 10)); + else + cd64_write_mempack (ucon64.rom, strtol (optarg, NULL, 10)); + fputc ('\n', stdout); + break; +#endif + + case UCON64_XCMC: + if (!access (ucon64.rom, F_OK) && ucon64.backup) + printf ("Wrote backup to: %s\n", mkbak (ucon64.rom, BAK_MOVE)); + cmc_read_rom (ucon64.rom, ucon64.parport, ucon64.io_mode); // ucon64.io_mode contains speed value + fputc ('\n', stdout); + break; + + case UCON64_XCMCT: + cmc_test (strtol (optarg, NULL, 10), ucon64.parport, ucon64.io_mode); + fputc ('\n', stdout); + break; + + case UCON64_XDEX: + if (access (ucon64.rom, F_OK) != 0) + dex_read_block (ucon64.rom, strtol (optarg, NULL, 10), ucon64.parport); + else + dex_write_block (ucon64.rom, strtol (optarg, NULL, 10), ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XDJR: + if (access (ucon64.rom, F_OK) != 0) + doctor64jr_read (ucon64.rom, ucon64.parport); + else + { + if (!ucon64.rominfo->interleaved) + fputs ("ERROR: This ROM doesn't seem to be interleaved but the Doctor V64 Junior only\n" + " supports interleaved ROMs. Convert to a Doctor V64 compatible format\n", + stderr); + else + doctor64jr_write (ucon64.rom, ucon64.parport); + } + fputc ('\n', stdout); + break; + + case UCON64_XFAL: + if (access (ucon64.rom, F_OK) != 0) + fal_read_rom (ucon64.rom, ucon64.parport, 32); + else + fal_write_rom (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XFALMULTI: + tmpnam2 (src_name); + ucon64_temp_file = src_name; + register_func (remove_temp_file); + // gba_multi() calls ucon64_file_handler() so the directory part will be + // stripped from src_name. The directory should be used though. + if (!ucon64.output_path[0]) + { + dirname2 (src_name, ucon64.output_path); + if (ucon64.output_path[strlen (ucon64.output_path) - 1] != FILE_SEPARATOR) + strcat (ucon64.output_path, FILE_SEPARATOR_S); + } + if (gba_multi (strtol (optarg, NULL, 10) * MBIT, src_name) == 0) + { // Don't try to start a transfer if there was a problem + fputc ('\n', stdout); + ucon64.file_size = fsizeof (src_name); + fal_write_rom (src_name, ucon64.parport); + } + + unregister_func (remove_temp_file); + remove_temp_file (); + fputc ('\n', stdout); + break; + + case UCON64_XFALC: + if (!access (ucon64.rom, F_OK) && ucon64.backup) + printf ("Wrote backup to: %s\n", mkbak (ucon64.rom, BAK_MOVE)); + fal_read_rom (ucon64.rom, ucon64.parport, strtol (optarg, NULL, 10)); + fputc ('\n', stdout); + break; + + case UCON64_XFALS: + if (access (ucon64.rom, F_OK) != 0) + fal_read_sram (ucon64.rom, ucon64.parport, UCON64_UNKNOWN); + else + fal_write_sram (ucon64.rom, ucon64.parport, UCON64_UNKNOWN); + fputc ('\n', stdout); + break; + + case UCON64_XFALB: + if (access (ucon64.rom, F_OK) != 0) + fal_read_sram (ucon64.rom, ucon64.parport, strtol (optarg, NULL, 10)); + else + fal_write_sram (ucon64.rom, ucon64.parport, strtol (optarg, NULL, 10)); + fputc ('\n', stdout); + break; + + case UCON64_XFIG: + if (access (ucon64.rom, F_OK) != 0) // file does not exist -> dump cartridge + fig_read_rom (ucon64.rom, ucon64.parport); + else + { + if (!ucon64.rominfo->buheader_len) + fputs ("ERROR: This ROM has no header. Convert to a FIG compatible format\n", + stderr); + else if (ucon64.rominfo->interleaved) + fputs ("ERROR: This ROM seems to be interleaved but the FIG doesn't support\n" + " interleaved ROMs. Convert to a FIG compatible format\n", + stderr); + else // file exists -> send it to the copier + fig_write_rom (ucon64.rom, ucon64.parport); + } + fputc ('\n', stdout); + break; + + case UCON64_XFIGS: + if (access (ucon64.rom, F_OK) != 0) // file does not exist -> dump SRAM contents + fig_read_sram (ucon64.rom, ucon64.parport); + else // file exists -> restore SRAM + fig_write_sram (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XFIGC: + if (access (ucon64.rom, F_OK) != 0) // file does not exist -> dump cart SRAM contents + fig_read_cart_sram (ucon64.rom, ucon64.parport); + else // file exists -> restore SRAM + fig_write_cart_sram (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XGBX: + if (access (ucon64.rom, F_OK) != 0) // file does not exist -> dump cartridge/flash card + gbx_read_rom (ucon64.rom, ucon64.parport); + else // file exists -> send it to the programmer + gbx_write_rom (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XGBXS: + if (access (ucon64.rom, F_OK) != 0) + gbx_read_sram (ucon64.rom, ucon64.parport, -1); + else + gbx_write_sram (ucon64.rom, ucon64.parport, -1); + fputc ('\n', stdout); + break; + + case UCON64_XGBXB: + if (access (ucon64.rom, F_OK) != 0) + gbx_read_sram (ucon64.rom, ucon64.parport, strtol (optarg, NULL, 10)); + else + gbx_write_sram (ucon64.rom, ucon64.parport, strtol (optarg, NULL, 10)); + fputc ('\n', stdout); + break; + + case UCON64_XGD3: + if (access (ucon64.rom, F_OK) != 0) // file does not exist -> dump cartridge + gd3_read_rom (ucon64.rom, ucon64.parport); // dumping is not yet supported + else + { + if (!ucon64.rominfo->buheader_len) + fputs ("ERROR: This ROM has no header. Convert to a Game Doctor compatible format\n", + stderr); + else // file exists -> send it to the copier + gd3_write_rom (ucon64.rom, ucon64.parport, ucon64.rominfo); + } + fputc ('\n', stdout); + break; + + case UCON64_XGD3S: + if (access (ucon64.rom, F_OK) != 0) // file does not exist -> dump SRAM contents + gd3_read_sram (ucon64.rom, ucon64.parport); // dumping is not yet supported + else // file exists -> restore SRAM + gd3_write_sram (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XGD3R: + if (access (ucon64.rom, F_OK) != 0) + gd3_read_saver (ucon64.rom, ucon64.parport); + else + gd3_write_saver (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XGD6: + if (access (ucon64.rom, F_OK) != 0) + gd6_read_rom (ucon64.rom, ucon64.parport); // dumping is not yet supported + else + { + if (!ucon64.rominfo->buheader_len) + fputs ("ERROR: This ROM has no header. Convert to a Game Doctor compatible format\n", + stderr); + else + gd6_write_rom (ucon64.rom, ucon64.parport, ucon64.rominfo); + } + fputc ('\n', stdout); + break; + + case UCON64_XGD6S: + if (access (ucon64.rom, F_OK) != 0) + gd6_read_sram (ucon64.rom, ucon64.parport); + else + gd6_write_sram (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XGD6R: + if (access (ucon64.rom, F_OK) != 0) + gd6_read_saver (ucon64.rom, ucon64.parport); + else + gd6_write_saver (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XGG: + if (access (ucon64.rom, F_OK) != 0) + smsgg_read_rom (ucon64.rom, ucon64.parport, 32 * MBIT); + else + { + if (ucon64.rominfo->buheader_len) + fputs ("ERROR: This ROM has a header. Remove it with -stp or -mgd\n", + stderr); + else if (ucon64.rominfo->interleaved) + fputs ("ERROR: This ROM seems to be interleaved, but uCON64 doesn't support\n" + " sending interleaved ROMs to the SMS-PRO/GG-PRO. Convert ROM with -mgd\n", + stderr); + else + smsgg_write_rom (ucon64.rom, ucon64.parport); + } + fputc ('\n', stdout); + break; + + case UCON64_XGGS: + if (access (ucon64.rom, F_OK) != 0) + smsgg_read_sram (ucon64.rom, ucon64.parport, -1); + else + smsgg_write_sram (ucon64.rom, ucon64.parport, -1); + fputc ('\n', stdout); + break; + + case UCON64_XGGB: + if (access (ucon64.rom, F_OK) != 0) + smsgg_read_sram (ucon64.rom, ucon64.parport, strtol (optarg, NULL, 10)); + else + smsgg_write_sram (ucon64.rom, ucon64.parport, strtol (optarg, NULL, 10)); + fputc ('\n', stdout); + break; + + case UCON64_XLIT: + if (!access (ucon64.rom, F_OK) && ucon64.backup) + printf ("Wrote backup to: %s\n", mkbak (ucon64.rom, BAK_MOVE)); + lynxit_read_rom (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XMCCL: + if (!access (ucon64.rom, F_OK) && ucon64.backup) + printf ("Wrote backup to: %s\n", mkbak (ucon64.rom, BAK_MOVE)); + mccl_read (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XMCD: + if (!access (ucon64.rom, F_OK) && ucon64.backup) + printf ("Wrote backup to: %s\n", mkbak (ucon64.rom, BAK_MOVE)); + mcd_read_rom (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XMD: + if (access (ucon64.rom, F_OK) != 0) // file does not exist -> dump flash card + md_read_rom (ucon64.rom, ucon64.parport, 64 * MBIT); // reads 32 Mbit if Sharp card + else // file exists -> send it to the MD-PRO + { + if (ucon64.rominfo->buheader_len) // binary with header is possible + fputs ("ERROR: This ROM has a header. Remove it with -stp or -bin\n", + stderr); + else if (genesis_get_file_type () != BIN) + fputs ("ERROR: This ROM is not in binary/BIN/RAW format. uCON64 only supports sending\n" + " binary files to the MD-PRO. Convert ROM with -bin\n", + stderr); + else + md_write_rom (ucon64.rom, ucon64.parport); + } + fputc ('\n', stdout); + break; + + case UCON64_XMDS: + if (access (ucon64.rom, F_OK) != 0) // file does not exist -> dump SRAM contents + md_read_sram (ucon64.rom, ucon64.parport, -1); + else // file exists -> restore SRAM + md_write_sram (ucon64.rom, ucon64.parport, -1); + fputc ('\n', stdout); + break; + + case UCON64_XMDB: + if (access (ucon64.rom, F_OK) != 0) + md_read_sram (ucon64.rom, ucon64.parport, strtol (optarg, NULL, 10)); + else + md_write_sram (ucon64.rom, ucon64.parport, strtol (optarg, NULL, 10)); + fputc ('\n', stdout); + break; + + case UCON64_XMSG: + if (access (ucon64.rom, F_OK) != 0) + msg_read_rom (ucon64.rom, ucon64.parport); + else + { + if (!ucon64.rominfo->buheader_len) + fputs ("ERROR: This ROM has no header. Convert to an MSG compatible format\n", + stderr); + else if (ucon64.rominfo->interleaved) + fputs ("ERROR: This ROM seems to be bit-swapped but the MSG doesn't support\n" + " bit-swapped ROMs. Convert to an MSG compatible format\n", + stderr); + else + msg_write_rom (ucon64.rom, ucon64.parport); + } + fputc ('\n', stdout); + break; + + case UCON64_XPCE: + if (access (ucon64.rom, F_OK) != 0) + pce_read_rom (ucon64.rom, ucon64.parport, 32 * MBIT); + else + pce_write_rom (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XPL: + if (access (ucon64.rom, F_OK) != 0) + pl_read_rom (ucon64.rom, ucon64.parport); + else + pl_write_rom (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XPLI: + pl_info (ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XSF: + if (access (ucon64.rom, F_OK) != 0) // file does not exist -> dump flash card + sf_read_rom (ucon64.rom, ucon64.parport, 64 * MBIT); + else // file exists -> send it to the Super Flash + sf_write_rom (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XSFS: + if (access (ucon64.rom, F_OK) != 0) // file does not exist -> dump SRAM contents + sf_read_sram (ucon64.rom, ucon64.parport); + else // file exists -> restore SRAM + sf_write_sram (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XSMC: // we don't use WF_NO_ROM => no need to check for file + if (!ucon64.rominfo->buheader_len) + fputs ("ERROR: This ROM has no header. Convert to an SMC compatible format with -ffe\n", + stderr); + else + smc_write_rom (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XSMCR: + if (access (ucon64.rom, F_OK) != 0) + smc_read_rts (ucon64.rom, ucon64.parport); + else + smc_write_rts (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XSMD: + if (access (ucon64.rom, F_OK) != 0) // file does not exist -> dump cartridge + smd_read_rom (ucon64.rom, ucon64.parport); + else // file exists -> send it to the copier + { + if (!ucon64.rominfo->buheader_len) + fputs ("ERROR: This ROM has no header. Convert to an SMD compatible format\n", + stderr); + else if (!ucon64.rominfo->interleaved) + fputs ("ERROR: This ROM doesn't seem to be interleaved but the SMD only supports\n" + " interleaved ROMs. Convert to an SMD compatible format\n", + stderr); + else + smd_write_rom (ucon64.rom, ucon64.parport); + } + fputc ('\n', stdout); + break; + + case UCON64_XSMDS: + if (access (ucon64.rom, F_OK) != 0) // file does not exist -> dump SRAM contents + smd_read_sram (ucon64.rom, ucon64.parport); + else // file exists -> restore SRAM + smd_write_sram (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XSWC: + enableRTS = 0; // falling through + case UCON64_XSWC2: + if (access (ucon64.rom, F_OK) != 0) // file does not exist -> dump cartridge + swc_read_rom (ucon64.rom, ucon64.parport, ucon64.io_mode); + else + { + if (!ucon64.rominfo->buheader_len) + fputs ("ERROR: This ROM has no header. Convert to an SWC compatible format\n", + stderr); + else if (ucon64.rominfo->interleaved) + fputs ("ERROR: This ROM seems to be interleaved but the SWC doesn't support\n" + " interleaved ROMs. Convert to an SWC compatible format\n", + stderr); + else + { + if (enableRTS != 0) + enableRTS = 1; + // file exists -> send it to the copier + swc_write_rom (ucon64.rom, ucon64.parport, enableRTS); + } + } + fputc ('\n', stdout); + break; + + case UCON64_XSWCS: + if (access (ucon64.rom, F_OK) != 0) // file does not exist -> dump SRAM contents + swc_read_sram (ucon64.rom, ucon64.parport); + else // file exists -> restore SRAM + swc_write_sram (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XSWCC: + if (access (ucon64.rom, F_OK) != 0) // file does not exist -> dump SRAM contents + swc_read_cart_sram (ucon64.rom, ucon64.parport, ucon64.io_mode); + else // file exists -> restore SRAM + swc_write_cart_sram (ucon64.rom, ucon64.parport, ucon64.io_mode); + fputc ('\n', stdout); + break; + + case UCON64_XSWCR: + if (access (ucon64.rom, F_OK) != 0) + swc_read_rts (ucon64.rom, ucon64.parport); + else + swc_write_rts (ucon64.rom, ucon64.parport); + fputc ('\n', stdout); + break; + + case UCON64_XV64: + if (access (ucon64.rom, F_OK) != 0) + doctor64_read (ucon64.rom, ucon64.parport); + else + { + if (!ucon64.rominfo->interleaved) + fputs ("ERROR: This ROM doesn't seem to be interleaved but the Doctor V64 only\n" + " supports interleaved ROMs. Convert to a Doctor V64 compatible format\n", + stderr); + else + doctor64_write (ucon64.rom, ucon64.rominfo->buheader_len, + ucon64.file_size, ucon64.parport); + } + fputc ('\n', stdout); + break; +#endif // USE_PARALLEL + +#if defined USE_PARALLEL || defined USE_USB + case UCON64_XF2A: + if (access (ucon64.rom, F_OK) != 0) + f2a_read_rom (ucon64.rom, 32); + else + f2a_write_rom (ucon64.rom, UCON64_UNKNOWN); + fputc ('\n', stdout); + break; + + case UCON64_XF2AMULTI: + f2a_write_rom (NULL, strtol (optarg, NULL, 10) * MBIT); + fputc ('\n', stdout); + break; + + case UCON64_XF2AC: + if (!access (ucon64.rom, F_OK) && ucon64.backup) + printf ("Wrote backup to: %s\n", mkbak (ucon64.rom, BAK_MOVE)); + f2a_read_rom (ucon64.rom, strtol (optarg, NULL, 10)); + fputc ('\n', stdout); + break; + + case UCON64_XF2AS: + if (access (ucon64.rom, F_OK) != 0) + f2a_read_sram (ucon64.rom, UCON64_UNKNOWN); + else + f2a_write_sram (ucon64.rom, UCON64_UNKNOWN); + fputc ('\n', stdout); + break; + + case UCON64_XF2AB: + if (access (ucon64.rom, F_OK) != 0) + f2a_read_sram (ucon64.rom, strtol (optarg, NULL, 10)); + else + f2a_write_sram (ucon64.rom, strtol (optarg, NULL, 10)); + fputc ('\n', stdout); + break; +#endif // USE_PARALLEL || USE_USB + + case UCON64_Z64: + n64_z64 (ucon64.rominfo); + break; + + default: + break; + } + + return 0; +} diff --git a/ucon64/2.0/src/ucon64_opts.h b/ucon64/2.0/src/ucon64_opts.h new file mode 100644 index 0000000..ea87102 --- /dev/null +++ b/ucon64/2.0/src/ucon64_opts.h @@ -0,0 +1,26 @@ +/* +ucon64_opts.h - switch()'es for all uCON64 options + +Copyright (c) 2002 - 2004 NoisyB +Copyright (c) 2002 - 2003 dbjh + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#ifndef UCON64_OPTS_H +#define UCON64_OPTS_H +extern int ucon64_switches (st_ucon64_t *p); +extern int ucon64_options (st_ucon64_t *p); +#endif // UCON64_OPTS_H