From: David Griffith Date: Wed, 22 Feb 2017 02:53:40 +0000 (-0800) Subject: Merge remote-tracking branch 'origin/ao-curses' X-Git-Url: https://scope-eye.net/git/?a=commitdiff_plain;h=caf63b49c4bf9d46632fde40937892b0cae6441e;p=liskon_frotz.git Merge remote-tracking branch 'origin/ao-curses' Conflicts: .gitignore ChangeLog Makefile PORTING README doc/dfrotz.6 doc/frotz.6 src/common/fastmem.c src/common/frotz.h src/common/main.c src/common/process.c src/common/screen.c src/curses/ux_audio_none.c src/curses/ux_blorb.c src/curses/ux_file.c src/curses/ux_frotz.h src/curses/ux_init.c src/curses/ux_screen.c src/curses/ux_text.c src/dos/bcinit.c src/dumb/dumb_init.c --- caf63b49c4bf9d46632fde40937892b0cae6441e diff --cc .gitignore index 83e077f,f9dba8a..67dd8c1 --- a/.gitignore +++ b/.gitignore @@@ -1,12 -1,6 +1,11 @@@ frotz dfrotz *.o +*.O +*.OBJ +*.BAK +*.SWP +*.DSK *.a - defines.h - /tags - /tmp + src/*/defines.h + src/common/git_hash.h diff --cc ChangeLog index 1aeb68d,c83ff25..2314774 --- a/ChangeLog +++ b/ChangeLog @@@ -1,38 -1,26 +1,46 @@@ -Summary of changes between Unix Frotz 2.43 and Unix Frotz 2.44: -=============================================================== +Summary of changes between Frotz 2.44 and Frotz 2.45: +===================================================== -Unix Frotz 2.43 was released on (date) +Frotz 2.45 was released on ???? - NEW FEATURE + NEW FEATURES -- Game files can be loaded from Blorb files. - -- Sound effects are now loaded from Blorb files. +- Added -R option for restoring save file directly from the command line. + - Sound effects in AIFF, MOD, and OGG formats supported per the Blorb specs. + ++- Sound driver selection is automated through the use of libao. ++ + +Summary of changes between Frotz 2.43 and Frotz 2.44: +===================================================== + +Frotz 2.44 was released on Sunday May 17, 2015. + +NEW FEATURES + +- Blorb support implemented. No support for audio in Blorb files yet. + +- Added an -m option for Dumb Frotz to suppress the [MORE] prompt. + +- Makefile and code for compiling for 16-bit DOS. + + - DOS Frotz now distributed in the same source file as Unix Frotz. + -ENHANCEMENTS -- Sound driver selection is automated through the use of libao. +BUG FIXES -- Makefile cleaned up. +- Fixed a problem with saving and restoring in certain games. -- Documentation updated. +- Added some fflush() calls to make Dumb Frotz more suitable for bot use. + +- Removed old-style save support. It was getting hard to manage. Only + the Quetzal save file format is supported now. + +- Fixed assorted minor 64-bitness problems. + ++- Makefile cleaned up. + Summary of changes between Unix Frotz 2.42 and Unix Frotz 2.43: =============================================================== diff --cc Makefile index 46fce59,fc3fa67..1d7a865 --- a/Makefile +++ b/Makefile @@@ -76,28 -59,7 +65,13 @@@ CURSES = -lncurse #LIB = -L/usr/pkg/lib #LIB = -L/usr/freeware/lib #LIB = -L/5usr/lib - #LIB = -L/path/to/libncurses.so - - # One of these must always be uncommented. If your vendor-supplied - # curses library won't work, comment out the first option and uncomment - # the second. - # - CURSES = -lcurses - #CURSES = -lncurses - - # Uncomment this if your need to use ncurses instead of the - # vendor-supplied curses library. This just tells the compile process - # which header to include, so don't worry if ncurses is all you have - # (like on Linux). You'll be fine. - # - #CURSES_DEF = -DUSE_NCURSES_H +# Uncomment this if you're compiling Unix Frotz on a machine that lacks +# the strrchr() libc library call. If you don't know what this means, +# leave it alone. +# +#STRRCHR_DEF = -DNO_STRRCHR + # Uncomment this if you're compiling Unix Frotz on a machine that lacks # the memmove(3) system call. If you don't know what this means, leave it # alone. @@@ -227,21 -172,51 +184,51 @@@ all: $(NAME) d$(NAME .SUFFIXES: .c .o .h $(COMMON_OBJECT): %.o: %.c - $(CC) $(CFLAGS) $(OPTS) $(COMMON_DEFS) -o $@ -c $< + $(CC) $(OPTS) -o $@ -c $< $(BLORB_OBJECT): %.o: %.c - $(CC) $(OPTS) -o $@ -c $< + $(CC) $(CFLAGS) $(OPTS) -o $@ -c $< $(DUMB_OBJECT): %.o: %.c - $(CC) $(OPTS) -o $@ -c $< + $(CC) $(CFLAGS) $(OPTS) -o $@ -c $< $(CURSES_OBJECT): %.o: %.c - $(CC) $(CFLAGS) $(OPTS) $(CURSES_DEFS) $(INCL) -o $@ -c $< + $(CC) $(OPTS) -o $@ -c $< + + + #################################### + # Get the defines set up just right + # + $(COMMON_DIR)/defines.h: + @echo "Generating $@" + @echo "#define VERSION \"$(VERSION)\"" > $@ - $(SDL_OBJECT): %.o: %.c - $(CC) $(CFLAGS) $(OPTS) $(SDL_DEFS) $(INCL) -o $@ -c $< + $(CURSES_DIR)/defines.h: + @echo "Generating $@" + @echo "#define CONFIG_DIR \"$(CONFIG_DIR)\"" >> $@ + @echo "#define SOUND \"$(SOUND)\"" >> $@ + @echo "#define SAMPLERATE $(SAMPLERATE)" >> $@ + @echo "#define BUFFSIZE $(BUFFSIZE)" >> $@ + @echo "#define DEFAULT_CONVERTER $(DEFAULT_CONVERTER)" >> $@ + ifeq ($(SOUND), none) + @echo "#define NO_SOUND" >> $@ + endif + ifndef SOUND + @echo "#define NO_SOUND" >> $@ + endif + + ifdef COLOR + @echo "#define COLOR_SUPPORT" >> $@ + endif + + ifdef NO_MEMMOVE + @echo "#define NO_MEMMOVE" >> $@ + endif + + + ######################################################################## # If you're going to make this target manually, you'd better know which # config target to make first. # @@@ -249,11 -224,11 +236,11 @@@ common_lib: $(COMMON_TARGET $(COMMON_TARGET): $(COMMON_OBJECT) @echo @echo "Archiving common code..." - ar rc $(COMMON_TARGET) $(COMMON_OBJECT) - ranlib $(COMMON_TARGET) + $(AR) rc $(COMMON_TARGET) $(COMMON_OBJECT) + $(RANLIB) $(COMMON_TARGET) @echo - curses_lib: config_curses $(CURSES_TARGET) + curses_lib: $(CURSES_TARGET) $(CURSES_TARGET): $(CURSES_OBJECT) @echo @echo "Archiving curses interface code..." @@@ -265,55 -240,41 +252,35 @@@ dumb_lib: $(DUMB_TARGET $(DUMB_TARGET): $(DUMB_OBJECT) @echo @echo "Archiving dumb interface code..." - ar rc $(DUMB_TARGET) $(DUMB_OBJECT) - ranlib $(DUMB_TARGET) + $(AR) rc $(DUMB_TARGET) $(DUMB_OBJECT) + $(RANLIB) $(DUMB_TARGET) @echo - sdl_lib: $(SDL_TARGET) - $(SDL_TARGET): $(SDL_OBJECT) - @echo - @echo "Archiving SDL interface code..." - $(AR) rc $(SDL_TARGET) $(SDL_OBJECT) - $(RANLIB) $(SDL_TARGET) - @echo - blorb_lib: $(BLORB_TARGET) $(BLORB_TARGET): $(BLORB_OBJECT) @echo @echo "Archiving Blorb file handling code..." - ar rc $(BLORB_TARGET) $(BLORB_OBJECT) - ranlib $(BLORB_TARGET) + $(AR) rc $(BLORB_TARGET) $(BLORB_OBJECT) + $(RANLIB) $(BLORB_TARGET) @echo - - soundcard.h: - @if [ ! -f $(SRCDIR)/soundcard.h ] ; then \ - sh $(SRCDIR)/misc/findsound.sh $(SRCDIR); \ - fi - install: $(NAME) - strip $(BINNAME)$(EXTENSION) - install -d $(PREFIX)/bin - install -d $(MAN_PREFIX)/man/man6 - install -c -m 755 $(BINNAME)$(EXTENSION) $(PREFIX)/bin - install -c -m 644 doc/$(NAME).6 $(MAN_PREFIX)/man/man6 + @install -D -m 755 $(BINNAME)$(EXTENSION) "$(DESTDIR)$(PREFIX)/bin/$(BINNAME)$(EXTENSION)" + @install -D -m 644 doc/$(NAME).6 "$(DESTDIR)$(MAN_PREFIX)/man/man6/$(NAME).6" uninstall: - rm -f $(PREFIX)/bin/$(NAME) - rm -f $(MAN_PREFIX)/man/man6/$(NAME).6 + @rm -f "$(DESTDIR)$(PREFIX)/bin/$(NAME)" + @rm -f "$(DESTDIR)$(MAN_PREFIX)/man/man6/$(NAME).6" - deinstall: uninstall - install_dumb: d$(NAME) - strip d$(BINNAME)$(EXTENSION) - install -d $(PREFIX)/bin - install -d $(MAN_PREFIX)/man/man6 - install -c -m 755 d$(BINNAME)$(EXTENSION) $(PREFIX)/bin - install -c -m 644 doc/d$(NAME).6 $(MAN_PREFIX)/man/man6 + @install -D -m 755 d$(BINNAME)$(EXTENSION) "$(DESTDIR)$(PREFIX)/bin/d$(BINNAME)$(EXTENSION)" + @install -D -m 644 doc/d$(NAME).6 "$(DESTDIR)$(MAN_PREFIX)/man/man6/d$(NAME).6" uninstall_dumb: - rm -f $(PREFIX)/bin/d$(NAME) - rm -f $(MAN_PREFIX)/man/man6/d$(NAME).6 + @rm -f "$(DESTDIR)$(PREFIX)/bin/d$(NAME)" + @rm -f "$(DESTDIR)$(MAN_PREFIX)/man/man6/d$(NAME).6" - deinstall_dumb: uninstall_dumb - - distro: dist - - dist: distclean + dist: distclean hash mkdir $(distdir) @for file in `ls`; do \ if test $$file != $(distdir); then \ diff --cc README index 6dc62e6,e21ed64..f844c40 --- a/README +++ b/README @@@ -1,4 -1,4 +1,4 @@@ - FROTZ V2.44 - An interpreter for all Infocom and other Z-machine games. -FROTZ V2.45 - An interpreter for all Infocom and other Z-machine games. ++FROTZ V2.45pre - An interpreter for all Infocom and other Z-machine games. Complies with standard 1.0 of Graham Nelson's specification. Originally written by Stefan Jokisch in 1995-1997. diff --cc TODO index ee35e29,250b641..af871c6 --- a/TODO +++ b/TODO @@@ -1,3 -1,17 +1,8 @@@ -I have mixing sortof working. Still to be done is to convert the sample -rate of the AIFF data to 44100, which is what is produced in playmod() -and playogg(). Another concern is how I should take care of a situation -in which playogg() or playmod() ends before the AIFF sample does. -Libsamplerate appears to be a good library for doing this, but I don't -know yet how to use it. - - - + Redo how the core and interfaces use the f_setup structure. Failure to + do this properly led to a segfault when dumb frotz saves a game. + Look at using a memset() or calloc() there. + + Rework os_process_arguments() in src/curses/ux_init.c to make Frotz more convenient to use as a BBS door or MUD module. diff --cc doc/dfrotz.6 index 8bd2694,7f1c3d5..2d17bfc --- a/doc/dfrotz.6 +++ b/doc/dfrotz.6 @@@ -1,5 -1,5 +1,5 @@@ .\" -*- nroff -*- - .TH DFROTZ 6 2.44 -.TH DFROTZ 6 2.45 ++.TH DFROTZ 6 2.45pre .SH NAME dfrotz \- interpreter for Infocom and other Z-Machine games (dumb interface) diff --cc doc/frotz.6 index 5407cb4,a4c12c1..42c014d --- a/doc/frotz.6 +++ b/doc/frotz.6 @@@ -1,5 -1,5 +1,5 @@@ .\" -*- nroff -*- - .TH FROTZ 6 2.44 -.TH FROTZ 6 2.45 ++.TH FROTZ 6 2.45pre .SH NAME frotz \- interpreter for Infocom and other Z-Machine games diff --cc src/common/fastmem.c index 75b042d,5d770c4..1e78790 --- a/src/common/fastmem.c +++ b/src/common/fastmem.c @@@ -522,9 -446,10 +519,9 @@@ static void free_undo (int count * Close the story file and deallocate memory. * */ - void reset_memory (void) { - if (story_fp) + if (story_fp != NULL) fclose (story_fp); story_fp = NULL; diff --cc src/curses/ux_audio.c index 0000000,1c8bd11..4648ac5 mode 000000,100644..100644 --- a/src/curses/ux_audio.c +++ b/src/curses/ux_audio.c @@@ -1,0 -1,879 +1,878 @@@ + /* + * ux_audio.c - Unix interface, sound support + * + * This file is part of Frotz. + * + * Frotz 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. + * + * Frotz 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + + #define __UNIX_PORT_FILE + + #include + #include + #include + #include + #include + #include + #include + #include + + #ifdef USE_NCURSES_H + #include + #else + #include + #endif + + #include "ux_frotz.h" + #include "ux_locks.h" + + #ifndef NO_SOUND + + #include + #include + #include + #include + #include + #include + + #define MAX(x,y) ((x)>(y)) ? (x) : (y) + #define MIN(x,y) ((x)<(y)) ? (x) : (y) + + enum sound_type { + FORM, + OGGV, + MOD + }; + + typedef struct { + FILE *fp; + bb_result_t result; + enum sound_type type; + int number; + int vol; + int repeats; + } EFFECT; + + static void *playaiff(EFFECT *); + static void *playmusic(EFFECT *); + static void *playmod(EFFECT *); + static void *playogg(EFFECT *); + + static void floattopcm16(short *, float *, int); + static void pcm16tofloat(float *, short *, int); + static void stereoize(float *, float *, size_t); + + static int mypower(int, int); + static char *getfiledata(FILE *, long *); + static void *mixer(void *); + + static pthread_t mixer_id; + static pthread_t playaiff_id; + static pthread_t playmusic_id; + static pthread_mutex_t mutex; + static sem_t playaiff_okay; + static sem_t playmusic_okay; + + bool bleep_playing = FALSE; + bool bleep_stop = FALSE; + + int bleepcount; + int bleepnum; + + bool music_playing = FALSE; + bool music_stop = FALSE; + + typedef struct + { + sem_t full; + sem_t empty; + float *samples; + int nsamples; + } audiobuffer; + + audiobuffer bleep_buffer; + audiobuffer music_buffer; + + void audiobuffer_init(audiobuffer *ab) + { + sem_init(&ab->full, 0, 0); + sem_init(&ab->empty, 0, 0); + sem_post(&ab->empty); + ab->samples = malloc(BUFFSIZE * 2 * sizeof(float)); + ab->nsamples = 0; + } + + + /* + * os_init_sound + * + * Do any required setup for sound output. + * Here we start a thread to act as a mixer. + * + */ + void os_init_sound(void) + { + int err; + static pthread_attr_t attr; + + pthread_mutex_init(&mutex, NULL); + audiobuffer_init(&music_buffer); + audiobuffer_init(&bleep_buffer); + sem_init(&playaiff_okay, 0, 0); + sem_init(&playmusic_okay, 0, 0); + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + err = pthread_create(&(mixer_id), &attr, (void *) &mixer, NULL); + if (err != 0) { + printf("Can't create mixer thread :[%s]", strerror(err)); + exit(1); + } + } + + + /* + * os_beep + * + * Play a beep sound. Ideally, the sound should be high- (number == 1) + * or low-pitched (number == 2). + * + */ + void os_beep (int number) + { + int i = number; + i++; + + beep(); + }/* os_beep */ + + + /* + * os_prepare_sample + * + * Load the sample from the disk. + * + * Actually it's more efficient these days to load and play a sound in + * the same operation. This function therefore does nothing. + * + */ + void os_prepare_sample (int number) + { + int i = number; + i++; + + return; + }/* os_prepare_sample */ + + + /* + * os_start_sample + * + * Play the given sample at the given volume (ranging from 1 to 8 and + * 255 meaning a default volume). The sound is played once or several + * times in the background (255 meaning forever). In Z-code 3 the + * repeats value is always 0 and the number of repeats is taken from + * the sound file itself. The end_of_sound function is called as soon + * as the sound finishes. + * + */ + void os_start_sample (int number, int volume, int repeats, zword eos) + { + bb_result_t resource; + EFFECT myeffect; + int err; + static pthread_attr_t attr; + zword foo = eos; + + foo++; + + if (blorb_map == NULL) return; + + if (bb_err_None != bb_load_resource(blorb_map, bb_method_FilePos, &resource, bb_ID_Snd, number)) + return; + + myeffect.fp = blorb_fp; + myeffect.result = resource; + myeffect.vol = volume; + myeffect.repeats = repeats; + myeffect.number = number; + + pthread_attr_init(&attr); + + if (blorb_map->chunks[resource.chunknum].type == bb_make_id('F','O','R','M')) + myeffect.type = FORM; + else if (blorb_map->chunks[resource.chunknum].type == bb_make_id('M','O','D',' ')) + myeffect.type = MOD; + else if (blorb_map->chunks[resource.chunknum].type == bb_make_id('O','G','G','V')) + myeffect.type = OGGV; + + if (myeffect.type == FORM) { + if (bleep_playing) { + bleep_playing = FALSE; + pthread_join(playaiff_id, NULL); + } + err = pthread_create(&playaiff_id, &attr, (void *) &playaiff, &myeffect); + if (err != 0) { + printf("Can't create playaiff thread :[%s]", strerror(err)); + return; + } + sem_wait(&playaiff_okay); + } else if (myeffect.type == MOD || myeffect.type == OGGV) { + if (music_playing) { + music_playing = FALSE; + pthread_join(playmusic_id, NULL); + } + err = pthread_create(&playmusic_id, &attr, (void *) &playmusic, &myeffect); + if (err != 0) { + printf("Can't create playmusic thread :[%s]", strerror(err)); + return; + } + sem_wait(&playmusic_okay); + } else { + /* Something else was presented as an audio chunk. Ignore it. */ + } + }/* os_start_sample */ + + + /* + * os_stop_sample + * + * Turn off the current sample. + * + */ + void os_stop_sample (int number) + { + if (bleep_playing && (number == bleepnum || number == 0)) { + bleep_playing = FALSE; + sem_post(&bleep_buffer.empty); + pthread_join(playaiff_id, 0); + } + + if (get_music_playing() && (number == get_musicnum () || number == 0)) { + set_music_playing(false); + sem_post(&music_buffer.empty); + pthread_join(playmusic_id, 0); + } + + return; + }/* os_stop_sample */ + + + /* + * os_finish_with_sample + * + * Remove the current sample from memory (if any). + * + */ + void os_finish_with_sample (int number) + { + os_stop_sample(number); + + }/* os_finish_with_sample */ + + + /* + * os_wait_sample + * + * Stop repeating the current sample and wait until it finishes. + * + */ + void os_wait_sample (void) + { + + /* Not implemented */ + + }/* os_wait_sample */ + + + /* + ********************************************** + * These functions are internal to ux_audio.c + * + ********************************************** + */ + + /* + * mixer + * + * In a classic producer/consumer arrangement, this mixer watches for audio + * data to be placed in *bleepbuffer or *musicbuffer. When a semaphore for + * either is raised, the mixer processes the buffer. + * + * Data presented to the mixer must be floats at 44100hz + * + */ + static void *mixer(void * UNUSED(arg)) + { + short *shortbuffer; + int default_driver; + ao_device *device; + ao_sample_format format; + int samplecount; - int i; + + ao_initialize(); + default_driver = ao_default_driver_id(); + + shortbuffer = malloc(BUFFSIZE * sizeof(short) * 2); + if (shortbuffer == NULL) { + printf("Unable to malloc shortbuffer\n"); + exit(1); + } + + memset(&format, 0, sizeof(ao_sample_format)); + + format.byte_format = AO_FMT_NATIVE; + format.bits = 16; + format.channels = 2; + format.rate = SAMPLERATE; + + device = NULL; + + while (1) { + if(music_playing) { + sem_wait(&music_buffer.full); /* Wait until output buffer is full */ + } + if(bleep_playing ) { + sem_wait(&bleep_buffer.full); /* Wait until output buffer is full */ + } + + pthread_mutex_lock(&mutex); /* Acquire mutex */ + + if (device == NULL) { + device = ao_open_live(default_driver, &format, NULL); + if (device == NULL) { + printf(" Error opening sound device.\n"); + } + } + + if (bleep_playing && !music_playing) { + floattopcm16(shortbuffer, bleep_buffer.samples, bleep_buffer.nsamples); + ao_play(device, (char *) shortbuffer, bleep_buffer.nsamples * sizeof(short)); + bleep_buffer.nsamples = 0; + } + + if (music_playing && !bleep_playing) { + floattopcm16(shortbuffer, music_buffer.samples, music_buffer.nsamples); + ao_play(device, (char *) shortbuffer, music_buffer.nsamples * sizeof(short)); + music_buffer.nsamples = 0; + } + + if (music_playing && bleep_playing) { + int samples = 100000; + if(bleep_buffer.nsamples == -1) + bleep_buffer.nsamples = 0; + if(music_buffer.nsamples == -1) + music_buffer.nsamples = 0; + if(samples > bleep_buffer.nsamples && bleep_buffer.nsamples > 0) + samples = bleep_buffer.nsamples; + + if(samples > music_buffer.nsamples && music_buffer.nsamples > 0) + samples = music_buffer.nsamples; + + //both buffers have invalid sample data or are empty + if(samples == 100000) + samples = 0; + + float *outbuf = calloc(samples+1,sizeof(float)); + for(int i=0; i < samples; ++i) + outbuf[i] += music_buffer.samples[i]; + for(int i=0; i < samples; ++i) + outbuf[i] += bleep_buffer.samples[i]; + + //only partially consume data + if(bleep_buffer.nsamples > samples) { + memmove(bleep_buffer.samples, bleep_buffer.samples+samples, + sizeof(float)*(bleep_buffer.nsamples-samples)); + } + if(bleep_buffer.nsamples > 0) + bleep_buffer.nsamples -= samples; + + if(music_buffer.nsamples > samples) { + memmove(music_buffer.samples, music_buffer.samples+samples, + sizeof(float)*(music_buffer.nsamples-samples)); + } + if(music_buffer.nsamples > 0) + music_buffer.nsamples -= samples; + + + samplecount = samples; + floattopcm16(shortbuffer, outbuf, samples); + ao_play(device, (char *) shortbuffer, samplecount * sizeof(short)); + free(outbuf); + } + + if (!bleep_playing && !music_playing) { + ao_close(device); + device = NULL; + } + + pthread_mutex_unlock(&mutex); /* release the mutex lock */ + + if(bleep_buffer.nsamples) { + sem_post(&bleep_buffer.full); + } + if(music_buffer.nsamples) { + sem_post(&music_buffer.full); + } + + int tmp; + sem_getvalue(&bleep_buffer.empty, &tmp); + + if(bleep_buffer.nsamples <= 0 && tmp == 0) { + sem_post(&bleep_buffer.empty); /* signal empty */ + } + + sem_getvalue(&music_buffer.empty, &tmp); + if(music_buffer.nsamples <= 0 && tmp == 0) { + sem_post(&music_buffer.empty); /* signal empty */ + } + } + } /* mixer */ + + + /* Convert back to shorts */ + static void floattopcm16(short *outbuf, float *inbuf, int length) + { + int count; + + const float mul = (32768.0f); + for (count = 0; count <= length; count++) { + int32_t tmp = (int32_t)(mul * inbuf[count]); + tmp = MAX( tmp, -32768 ); // CLIP < 32768 + tmp = MIN( tmp, 32767 ); // CLIP > 32767 + outbuf[count] = tmp; + } + } + + + /* Convert the buffer to floats. (before resampling) */ + static void pcm16tofloat(float *outbuf, short *inbuf, int length) + { + int count; + + const float div = (1.0f/32768.0f); + for (count = 0; count <= length; count++) { + outbuf[count] = div * (float) inbuf[count]; + } + } + + + /* + * stereoize + * + * Copy the single channel of a monaural stream to both channels + * of a stereo stream. + * + */ + static void stereoize(float *outbuf, float *inbuf, size_t length) + { + size_t count; + int outcount; + + outcount = 0; + + for (count = 0; count < length; count++) { + outbuf[outcount] = outbuf[outcount+1] = inbuf[count]; + outcount += 2; + } + } + + + /* + * mypower + * + * Just a simple recursive integer-based power function because I don't + * want to use the floating-point version from libm. + * + */ + static int mypower(int base, int exp) { + if (exp == 0) + return 1; + else if (exp % 2) + return base * mypower(base, exp - 1); + else { + int temp = mypower(base, exp / 2); + return temp * temp; + } + } + + + /* + * playaiff + * + * This function takes a file pointer to a Blorb file and a bb_result_t + * struct describing what chunk to play. It's up to the caller to make + * sure that an AIFF chunk is to be played. Volume and repeats are also + * handled here. + * + * This function should be able to play OGG chunks, but because of a bug + * or oversight in Libsndfile, that library is incapable of playing OGG + * data which are embedded in a larger file. + * + */ + void *playaiff(EFFECT *raw_effect) + { + // long filestart; + + int volcount; + int volfactor; + + float *floatbuffer; + float *floatbuffer2; + + SNDFILE *sndfile; + SF_INFO sf_info; + + SRC_STATE *src_state; + SRC_DATA src_data; + int error; + sf_count_t output_count = 0; + + EFFECT myeffect = *raw_effect; + + sem_post(&playaiff_okay); + + sf_info.format = 0; + bleepnum = myeffect.number; + + // filestart = ftell(myeffect.fp); + lseek(fileno(myeffect.fp), myeffect.result.data.startpos, SEEK_SET); + sndfile = sf_open_fd(fileno(myeffect.fp), SFM_READ, &sf_info, 0); + + if (myeffect.vol < 1) myeffect.vol = 1; + if (myeffect.vol > 8) myeffect.vol = 8; + volfactor = mypower(2, -myeffect.vol + 8); + + floatbuffer = malloc(BUFFSIZE * sf_info.channels * sizeof(float)); + floatbuffer2 = malloc(BUFFSIZE * 2 * sizeof(float)); + memset(bleep_buffer.samples, 0, BUFFSIZE * sizeof(float) * 2); + + /* Set up for conversion */ + if ((src_state = src_new(SRC_SINC_FASTEST, sf_info.channels, &error)) == NULL) { + printf("Error: src_new() failed: %s.\n", src_strerror(error)); + exit(1); + } + src_data.end_of_input = 0; + src_data.input_frames = 0; + src_data.data_in = floatbuffer; + src_data.src_ratio = (1.0 * SAMPLERATE) / sf_info.samplerate; + src_data.data_out = floatbuffer2; + src_data.output_frames = BUFFSIZE / sf_info.channels; + + bleep_playing = TRUE; + + while (1) { + /* Check if we're being told to stop. */ + if (!bleep_playing) break; + sem_wait(&bleep_buffer.empty); + pthread_mutex_lock(&mutex); + + /* If floatbuffer is empty, refill it. */ + if (src_data.input_frames == 0) { + src_data.input_frames = sf_readf_float(sndfile, floatbuffer, BUFFSIZE / sf_info.channels); + src_data.data_in = floatbuffer; + /* Mark end of input. */ + if (src_data.input_frames < BUFFSIZE / sf_info.channels) + src_data.end_of_input = SF_TRUE; + } + + /* Do the sample rate conversion. */ + if ((error = src_process(src_state, &src_data))) { + printf("Error: %s\n", src_strerror(error)); + exit(1); + } + + bleep_buffer.nsamples = src_data.output_frames_gen * 2; + + /* Stereoize monaural sound-effects. */ + if (sf_info.channels == 1) { + /* Remember that each monaural frame contains just one sample. */ + stereoize(bleep_buffer.samples, floatbuffer2, src_data.output_frames_gen); + } else { + /* It's already stereo. Just copy the buffer. */ + memcpy(bleep_buffer.samples, floatbuffer2, sizeof(float) * src_data.output_frames_gen * 2); + } + + /* Adjust volume. */ + for (volcount = 0; volcount <= bleep_buffer.nsamples; volcount++) + bleep_buffer.samples[volcount] /= volfactor; + + /* If that's all, terminate and signal that we're done. */ + if (src_data.end_of_input && src_data.output_frames_gen == 0) { + sem_post(&bleep_buffer.full); + pthread_mutex_unlock(&mutex); + break; + } + + /* Get ready for the next chunk. */ + output_count += src_data.output_frames_gen; + src_data.data_in += src_data.input_frames_used * sf_info.channels; + src_data.input_frames -= src_data.input_frames_used; + + /* By this time, the buffer is full. Signal the mixer to play it. */ + pthread_mutex_unlock(&mutex); + sem_post(&bleep_buffer.full); + } + + /* The two ways to exit the above loop are to process all the + * samples in the AIFF file or else get told to stop early. + * Whichever, we need to clean up and terminate this thread. + */ + + bleep_playing = FALSE; + memset(bleep_buffer.samples, 0, BUFFSIZE * sizeof(float) * 2); + + // fseek(myeffect.fp, filestart, SEEK_SET); + + // pthread_mutex_unlock(&mutex); + // sem_post(&audio_empty); + + sf_close(sndfile); + free(floatbuffer); + free(floatbuffer2); + + pthread_exit(NULL); + } /* playaiff */ + + + /* + * playmusic + * + * To more easily make sure only one of MOD or OGGV plays at one time. + * + */ + static void *playmusic(EFFECT *raw_effect) + { + EFFECT myeffect = *raw_effect; + + sem_post(&playmusic_okay); + + if (myeffect.type == MOD) playmod(&myeffect); + else if (myeffect.type == OGGV) playogg(&myeffect); + else { } /* do nothing */ + + pthread_exit(NULL); + + } /* playmusic */ + + + /* + * playmod + * + * This function takes a file pointer to a Blorb file and a bb_result_t + * struct describing what chunk to play. It's up to the caller to make + * sure that a MOD chunk is to be played. Volume and repeats are also + * handled here. + * + */ + static void *playmod(EFFECT *raw_effect) + { + short *shortbuffer; + + // int modlen; + // int count; + + char *filedata; + long size; + ModPlugFile *mod; + ModPlug_Settings settings; + + long filestart; + + EFFECT myeffect = *raw_effect; + + set_musicnum(myeffect.number); + + filestart = ftell(myeffect.fp); + fseek(myeffect.fp, myeffect.result.data.startpos, SEEK_SET); + + ModPlug_GetSettings(&settings); + + /* Note: All "Basic Settings" must be set before ModPlug_Load. */ + settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; /* RESAMP */ + settings.mChannels = 2; + settings.mBits = 16; + settings.mFrequency = SAMPLERATE; + settings.mStereoSeparation = 128; + settings.mMaxMixChannels = 256; + + /* insert more setting changes here */ + ModPlug_SetSettings(&settings); + + /* remember to free() filedata later */ + filedata = getfiledata(myeffect.fp, &size); + + mod = ModPlug_Load(filedata, size); + fseek(myeffect.fp, filestart, SEEK_SET); + if (!mod) { + printf("Unable to load MOD chunk.\n\r"); + return 0; + } + + if (myeffect.vol < 1) myeffect.vol = 1; + if (myeffect.vol > 8) myeffect.vol = 8; + ModPlug_SetMasterVolume(mod, mypower(2, myeffect.vol)); + + shortbuffer = malloc(BUFFSIZE * sizeof(short) * 2); + + music_playing = TRUE; + + while (1) { + sem_wait(&music_buffer.empty); + pthread_mutex_lock(&mutex); + memset(music_buffer.samples, 0, BUFFSIZE * sizeof(float) * 2); + if (!music_playing) { + break; + } + music_buffer.nsamples = ModPlug_Read(mod, shortbuffer, BUFFSIZE) / 2; + pcm16tofloat(music_buffer.samples, shortbuffer, music_buffer.nsamples); + if (music_buffer.nsamples == 0) break; + pthread_mutex_unlock(&mutex); + sem_post(&music_buffer.full); + } + + music_playing = FALSE; + memset(music_buffer.samples, 0, BUFFSIZE * sizeof(float) * 2); + + pthread_mutex_unlock(&mutex); + sem_post(&music_buffer.empty); + + ModPlug_Unload(mod); + free(shortbuffer); + free(filedata); + + return 0; + } /* playmod */ + + + /* + * getfiledata + * + * libmodplug requires the whole file to be pulled into memory. + * This function does that and then closes the file. + */ + static char *getfiledata(FILE *fp, long *size) + { + char *data; + long offset; + + offset = ftell(fp); + fseek(fp, 0L, SEEK_END); + (*size) = ftell(fp); + fseek(fp, offset, SEEK_SET); + data = (char*)malloc(*size); + fread(data, *size, sizeof(char), fp); + fseek(fp, offset, SEEK_SET); + return(data); + } /* getfiledata */ + + + /* + * playogg + * + * This function takes a file pointer to a Blorb file and a bb_result_t + * struct describing what chunk to play. It's up to the caller to make + * sure that an OGG chunk is to be played. Volume and repeats are also + * handled here. + * + * Libsndfile is capable of reading OGG files, but not if the file is + * embedded in another file. That's why we're using libvorbisfile + * directly instead of going through libsndfile. Erikd, main developer + * of libsndfile is working on a fix. + * + */ + static void *playogg(EFFECT *raw_effect) + { + ogg_int64_t toread; + ogg_int64_t frames_read; + ogg_int64_t count; + + vorbis_info *info; + + OggVorbis_File vf; + + int current_section; + short *shortbuffer; + + // long filestart; + int volcount; + int volfactor; + + EFFECT myeffect = *raw_effect; + + // filestart = ftell(myeffect.fp); + fseek(myeffect.fp, myeffect.result.data.startpos, SEEK_SET); + + if (ov_open_callbacks(myeffect.fp, &vf, NULL, 0, OV_CALLBACKS_NOCLOSE) < 0) { + printf("Unable to load OGGV chunk.\n\r"); + return 0; + } + + info = ov_info(&vf, -1); + if (info == NULL) { + printf("Unable to get info on OGGV chunk.\n\r"); + return 0; + } + + if (myeffect.vol < 1) myeffect.vol = 1; + if (myeffect.vol > 8) myeffect.vol = 8; + volfactor = mypower(2, -myeffect.vol + 8); + + shortbuffer = malloc(BUFFSIZE * info->channels * sizeof(short)); + + frames_read = 0; + toread = ov_pcm_total(&vf, -1) * 2 * info->channels; + count = 0; + + music_playing = TRUE; + + while (count < toread) { + sem_wait(&music_buffer.empty); + pthread_mutex_lock(&mutex); + memset(music_buffer.samples, 0, BUFFSIZE * sizeof(float) * 2); + if (!music_playing) break; + + frames_read = ov_read(&vf, (char *)shortbuffer, BUFFSIZE, 0,2,1,¤t_section); + + pcm16tofloat(music_buffer.samples, shortbuffer, frames_read); + for (volcount = 0; volcount <= frames_read / 2; volcount++) { + ((float *) music_buffer.samples)[volcount] /= volfactor; + } + + music_buffer.nsamples = frames_read / 2; + if(music_buffer.nsamples == -1) + music_buffer.nsamples = 0; + //perform mix down + count += frames_read; + + pthread_mutex_unlock(&mutex); + sem_post(&music_buffer.full); + } + + // fseek(myeffect.fp, filestart, SEEK_SET); + music_playing = FALSE; + + pthread_mutex_unlock(&mutex); + sem_post(&music_buffer.empty); + + ov_clear(&vf); + + free(shortbuffer); + + return 0; + } /* playogg */ + + #endif /* NO_SOUND */ diff --cc src/curses/ux_blorb.c index 723ad8f,29adefc..59fa5fd --- a/src/curses/ux_blorb.c +++ b/src/curses/ux_blorb.c @@@ -1,72 -23,224 +23,160 @@@ #include #include #include - #include + + #include + #include #include - #include - #include "../common/frotz.h" - #include "../blorb/blorb.h" - #include "../blorb/blorblow.h" + #ifdef USE_NCURSES_H + #include + #else + #include + #endif + + #include "ux_frotz.h" #include "ux_blorb.h" + f_setup_t f_setup; + u_setup_t u_setup; + + FILE *blorb_fp; + bb_result_t blorb_res; + bb_map_t *blorb_map; + -// uint32 *findchunk(uint32 *data, char *chunkID, int length); -// static char *findchunk(char *pstart, char *fourcc, int n); -// static unsigned short ReadShort(const unsigned char *bytes); -// static unsigned long ReadLong(const unsigned char *bytes); -// static double ReadExtended(const unsigned char *bytes); + static int isblorb(FILE *); + + #define UnsignedToFloat(u) (((double)((long)(u - 2147483647L - 1))) + 2147483648.0) - char *findchunk(char *data, char *string, int length) + + /* + * ux_blorb_init + * + * Check if we're opening a Blorb file directly. If not, check + * to see if there's a seperate Blorb file that looks like it goes + * along with this Zcode file. If we have a Blorb file one way or the + * other, make a Blorb map. If we opened a Blorb file directly, that + * means that our executable is in that file and therefore we will look + * for a ZCOD chunk and record its location so os_load_story() can find it. + * Make sure the Blorb file is opened and with the file pointer blorb_fp. + */ + bb_err_t ux_blorb_init(char *filename) { - char *mydata = data+12; - while (TRUE) { - if (strncmp((char*)mydata, string, 4) == 0) - return mydata+8; + FILE *fp; + char *p; + char *mystring; + int len1; + int len2; + + bb_err_t blorb_err; + + blorb_map = NULL; + + if ((fp = fopen(filename, "rb")) == NULL) + return bb_err_Read; + + /* Is this really a Blorb file? If not, maybe we're loading a naked + * zcode file and our resources are in a seperate blorb file. + */ + if (isblorb(fp)) { /* Now we know to look */ + u_setup.exec_in_blorb = 1; /* for zcode in the blorb */ + blorb_fp = fopen(filename, "rb"); + } else { + len1 = strlen(filename) + strlen(EXT_BLORB); + len2 = strlen(filename) + strlen(EXT_BLORB3); + + mystring = malloc(len2 * sizeof(char) + 1); + strncat(mystring, filename, len1 * sizeof(char)); + p = rindex(mystring, '.'); + *p = '\0'; - mydata += ReadLong(mydata+4)+8; + strncat(mystring, EXT_BLORB, len1 * sizeof(char)); - if ((mydata - data) >= length) - break; + /* Done monkeying with the initial file. */ + fclose(fp); + fp = NULL; + + /* Check if foo.blb is there. */ + if ((blorb_fp = fopen(mystring, "rb")) == NULL) { + p = rindex(mystring, '.'); + *p = '\0'; + strncat(mystring, EXT_BLORB3, len2 * sizeof(char)); + blorb_fp = fopen(mystring, "rb"); } - return NULL; - } + if (blorb_fp == NULL || !isblorb(fp)) /* No matching blorbs found. */ + return bb_err_NoBlorb; - unsigned short ReadShort(const unsigned char *bytes) - { - return (unsigned short)( - ((unsigned short)(bytes[0] & 0xFF) << 8) | - ((unsigned short)(bytes[1] & 0xFF))); + /* At this point we know that we're using a naked zcode file */ + /* with resources in a seperate Blorb file. */ + u_setup.use_blorb = 1; + } + + /* Create a Blorb map from this file. + * This will fail if the file is not a valid Blorb file. + * From this map, we can now pick out any resource we need. + */ + blorb_err = bb_create_map(fp, &blorb_map); + if (blorb_err != bb_err_None) + return bb_err_Format; + + /* Locate the EXEC chunk within the blorb file and record its + * location so os_load_story() can find it. + */ + if (u_setup.exec_in_blorb) { + blorb_err = bb_load_chunk_by_type(blorb_map, bb_method_FilePos, + &blorb_res, bb_make_id('Z','C','O','D'), 0); + u_setup.exec_in_blorb = 1; + } + + fclose(fp); + return blorb_err; } - unsigned long ReadLong(const unsigned char *bytes) + + /* + * ux_blorb_stop + * + * Basically just close the Blorb file. + * + */ + void ux_blorb_stop(void) { - return (unsigned long)( - ((unsigned long)(bytes[0] & 0xFF) << 24) | - ((unsigned long)(bytes[1] & 0xFF) << 16) | - ((unsigned long)(bytes[2] & 0xFF) << 8) | - ((unsigned long)(bytes[3] & 0xFF))); + if (blorb_fp != NULL) + fclose(blorb_fp); + blorb_fp = NULL; } - double ReadExtended(const unsigned char *bytes) + /* + ********************************************** + * These functions are internal to ux_blorb.c + * + ********************************************** + */ + + /* + * isblorb + * + * Returns 1 if this file is a Blorb file, 0 if not. + * + * FIXME Is there a potential endian problem here? + */ + static int isblorb(FILE *fp) { - double f; - int expon; - unsigned long hiMant, loMant; - - expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF); - hiMant = ReadLong(bytes+2); - loMant = ReadLong(bytes+6); - - if (expon == 0 && hiMant == 0 && loMant == 0) - f = 0; - else { - if (expon == 0x7FFF) /* Infinity or NaN */ - f = -1; - else { - expon -= 16383; - /* must #include or these won't work */ - f = ldexp(UnsignedToFloat(hiMant),expon -= 31); - f += ldexp(UnsignedToFloat(loMant),expon -= 32); - } - } + char mybuf[4]; + + if (fp == NULL) + return 0; + + fread(mybuf, 1, 4, fp); + if (strncmp(mybuf, "FORM", 4)) + return 0; + + fseek(fp, 4, SEEK_CUR); + fread(mybuf, 1, 4, fp); + + if (strncmp(mybuf, "IFRS", 4)) + return 0; - if (bytes[0] & 0x80) - return -f; - return f; + return 1; } - -#ifdef CRAP -static char *findchunk(char *data, char *string, int length) -{ - char *mydata = data+12; - while (TRUE) { - if (strncmp((char*)mydata, string, 4) == 0) - return mydata+8; - mydata += ReadLong(mydata+4)+8; - if ((mydata - data) >= length) - break; - } - return NULL; -} - -static unsigned short ReadShort(const unsigned char *bytes) -{ - return (unsigned short)( - ((unsigned short)(bytes[0] & 0xFF) << 8) | - ((unsigned short)(bytes[1] & 0xFF))); -} - -static unsigned long ReadLong(const unsigned char *bytes) -{ - return (unsigned long)( - ((unsigned long)(bytes[0] & 0xFF) << 24) | - ((unsigned long)(bytes[1] & 0xFF) << 16) | - ((unsigned long)(bytes[2] & 0xFF) << 8) | - ((unsigned long)(bytes[3] & 0xFF))); -} - -static double ReadExtended(const unsigned char *bytes) -{ - double f; - int expon; - unsigned long hiMant, loMant; - - expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF); - hiMant = ReadLong(bytes+2); - loMant = ReadLong(bytes+6); - - if (expon == 0 && hiMant == 0 && loMant == 0) - f = 0; - else { - if (expon == 0x7FFF) /* Infinity or NaN */ - f = -1; - else { - expon -= 16383; - /* must #include or these won't work */ - f = ldexp(UnsignedToFloat(hiMant),expon -= 31); - f += ldexp(UnsignedToFloat(loMant),expon -= 32); - } - } - - if (bytes[0] & 0x80) - return -f; - return f; -} -#endif diff --cc src/curses/ux_frotz.h index 1637e86,4c15ebd..f40ddd4 --- a/src/curses/ux_frotz.h +++ b/src/curses/ux_frotz.h @@@ -5,10 -5,19 +5,18 @@@ * */ + #include "defines.h" #include "../common/frotz.h" #include "../blorb/blorb.h" + #include "../blorb/blorblow.h" #include "ux_setup.h" + #ifndef rindex + #define rindex strrchr + #endif + + #define MASTER_CONFIG "frotz.conf" + #define USER_CONFIG ".frotzrc" - #define ASCII_DEF 1 #define ATTRIB_ASSIG_DEF 0 #define ATTRIB_TEST_DEF 0 @@@ -90,11 -94,10 +93,9 @@@ void unix_init_scrollback(void); /* ux_ void unix_save_screen(int); /* ux_screen.c */ void unix_do_scrollback(void); /* ux_screen.c */ - FILE *pathopen(const char *, const char *, const char *, char *); - -void sigwinch_handler(int); -void sigint_handler(int); -- +#ifdef NO_STRRCHR +char *strrchr(const char *, int); +#endif #ifdef NO_MEMMOVE void *memmove(void *, void *); diff --cc src/curses/ux_init.c index af46d3e,8d564d1..b85b7ad --- a/src/curses/ux_init.c +++ b/src/curses/ux_init.c @@@ -52,16 -47,6 +47,16 @@@ f_setup_t f_setup; u_setup_t u_setup; +static int getconfig(char *); +static int geterrmode(char *); +static int getcolor(char *); +static int getbool(char *); + - static void sigwinch_handler(int); ++/* static void sigwinch_handler(int); */ +static void sigint_handler(int); - static void redraw(void); ++/* static void redraw(void); */ + + #define INFORMATION "\ An interpreter for all Infocom and other Z-Machine games.\n\ Complies with standard 1.0 of Graham Nelson's specification.\n\ @@@ -148,10 -167,8 +177,9 @@@ void os_fatal (const char *s, ... void os_process_arguments (int argc, char *argv[]) { int c; - char *p = NULL; -- char *blorb_ext = NULL; ++// FIXME: put this back before committing merge fixes ++// char *blorb_ext = NULL; char *home; char configfile[FILENAME_MAX + 1]; @@@ -194,32 -210,35 +221,35 @@@ if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, sigint_handler); - if (getenv("XDG_CONFIG_HOME")) { - snprintf(configfile, FILENAME_MAX, - "%s/frotz/frotz.conf", getenv("XDG_CONFIG_HOME")); - } else { - snprintf(configfile, FILENAME_MAX, - "%s/.config/frotz/frotz.conf", home); - } + - if (!getconfig(configfile)) { - snprintf(configfile, FILENAME_MAX, "%s/.frotzrc", home); - } - - if (!getconfig(configfile)) { - snprintf(configfile, FILENAME_MAX, "%s/frotz.conf", CONFIG_DIR); - getconfig(configfile); /* we're not concerned if this fails */ - } + if (signal(SIGTTIN, SIG_IGN) != SIG_IGN) + signal(SIGTTIN, SIG_IGN); - /* Parse the options */ + if (signal(SIGTTOU, SIG_IGN) != SIG_IGN) + signal(SIGTTOU, SIG_IGN); + + /* First check for a "$HOME/.frotzrc". */ + /* If not found, look for CONFIG_DIR/frotz.conf */ + /* $HOME/.frotzrc overrides CONFIG_DIR/frotz.conf */ + + strncpy(configfile, home, FILENAME_MAX); + strncat(configfile, "/", 1); + strncat(configfile, USER_CONFIG, strlen(USER_CONFIG)); + if (!getconfig(configfile)) { - strncpy(configfile, CONFIG_DIR, FILENAME_MAX); - strncat(configfile, "/", 1); /* added by DJP */ - strncat(configfile, MASTER_CONFIG, FILENAME_MAX-10); - getconfig(configfile); /* we're not concerned if this fails */ ++ strncpy(configfile, CONFIG_DIR, FILENAME_MAX); ++ strncat(configfile, "/", 1); /* added by DJP */ ++ strncat(configfile, MASTER_CONFIG, FILENAME_MAX-10); ++ getconfig(configfile); /* we're not concerned if this fails */ + } + + /* Parse the options */ do { - c = getopt(argc, argv, "aAb:c:def:Fh:il:oOpPqrR:s:S:tu:w:xZ:"); - c = zgetopt(argc, argv, "aAb:c:def:Fh:il:oOpPqr:s:S:tu:vw:xZ:"); ++ c = zgetopt(argc, argv, "aAb:c:def:Fh:il:oOpPqrR:s:S:tu:w:xZ:"); switch(c) { case 'a': f_setup.attribute_assignment = 1; break; case 'A': f_setup.attribute_testing = 1; break; -- - case 'b': u_setup.background_color = getcolor(optarg); + case 'b': u_setup.background_color = getcolor(zoptarg); u_setup.force_color = 1; u_setup.disable_color = 0; if ((u_setup.background_color < 2) || @@@ -247,18 -266,15 +277,19 @@@ case 'p': u_setup.plain_ascii = 1; break; case 'P': f_setup.piracy = 1; break; case 'q': f_setup.sound = 0; break; - case 'r': f_setup.right_margin = atoi(optarg); break; + case 'r': f_setup.right_margin = atoi(zoptarg); break; + case 'R': f_setup.restore_mode = 1; + f_setup.tmp_save_name = malloc(FILENAME_MAX * sizeof(char) + 1); - strncpy(f_setup.tmp_save_name, optarg, FILENAME_MAX); ++ strncpy(f_setup.tmp_save_name, zoptarg, FILENAME_MAX); + break; - case 's': u_setup.random_seed = atoi(optarg); break; - case 'S': f_setup.script_cols = atoi(optarg); break; + case 's': u_setup.random_seed = atoi(zoptarg); break; + case 'S': f_setup.script_cols = atoi(zoptarg); break; case 't': u_setup.tandy_bit = 1; break; - case 'u': f_setup.undo_slots = atoi(optarg); break; - case 'w': u_setup.screen_width = atoi(optarg); break; + case 'u': f_setup.undo_slots = atoi(zoptarg); break; + case 'v': print_version(); exit(2); break; + case 'w': u_setup.screen_width = atoi(zoptarg); break; case 'x': f_setup.expand_abbreviations = 1; break; - case 'Z': f_setup.err_report_mode = atoi(optarg); + case 'Z': f_setup.err_report_mode = atoi(zoptarg); if ((f_setup.err_report_mode < ERR_REPORT_NEVER) || (f_setup.err_report_mode > ERR_REPORT_FATAL)) f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE; @@@ -304,80 -317,44 +332,50 @@@ if ((p != NULL) && ((strcmp(p,EXT_BLORB2) == 0) || (strcmp(p,EXT_BLORB3) == 0) || - (strcmp(p,EXT_BLORB4) == 0) ) ) - { - blorb_ext = strdup(p); + (strcmp(p,EXT_BLORB4) == 0) ) ) { - blorb_ext = strdup(p); ++// blorb_ext = strdup(p); } else - { - blorb_ext = strdup(EXT_BLORB); - } - blorb_ext = strdup(EXT_BLORB); ++// blorb_ext = strdup(EXT_BLORB); + /* Get rid of extensions with 1 to 6 character extensions. */ /* This will take care of an extension like ".zblorb". */ /* More than that, there might be something weird going on */ /* which is not our concern. */ if (p != NULL) { - if (strlen(p) >= 2 && strlen(p) <= 7) { - *p = '\0'; /* extension removed */ - } + if (strlen(p) >= 2 && strlen(p) <= 7) { + *p = '\0'; /* extension removed */ + } } - f_setup.story_path = strdup(dirname(argv[optind])); - - /* Create nice default file names */ - - u_setup.blorb_name = malloc(FILENAME_MAX * sizeof(char)); - strncpy(u_setup.blorb_name, f_setup.story_name, - strlen(f_setup.story_name) +1); - strncat(u_setup.blorb_name, blorb_ext, strlen(blorb_ext)); + f_setup.story_path = strdup(dirname(argv[zoptind])); - u_setup.blorb_file = malloc(strlen(f_setup.story_path) * - sizeof(char) + strlen(u_setup.blorb_name) * sizeof(char) + 4); - strncpy(u_setup.blorb_file, f_setup.story_path, - strlen(f_setup.story_path)); - strncat(u_setup.blorb_file, "/", 1); - strncat(u_setup.blorb_file, u_setup.blorb_name, - strlen(u_setup.blorb_name) + 1); - - f_setup.script_name = malloc(strlen(f_setup.story_name) * sizeof(char) + 5); - strncpy(f_setup.script_name, f_setup.story_name, strlen(f_setup.story_name)); + f_setup.script_name = malloc((strlen(f_setup.story_name) + strlen(EXT_SCRIPT)) * sizeof(char) + 1); + strncpy(f_setup.script_name, f_setup.story_name, strlen(f_setup.story_name) + 1); strncat(f_setup.script_name, EXT_SCRIPT, strlen(EXT_SCRIPT)); - f_setup.command_name = malloc(strlen(f_setup.story_name) * sizeof(char) + 5); - strncpy(f_setup.command_name, f_setup.story_name, strlen(f_setup.story_name)); + f_setup.command_name = malloc((strlen(f_setup.story_name) + strlen(EXT_COMMAND)) * sizeof(char) + 1); + strncpy(f_setup.command_name, f_setup.story_name, strlen(f_setup.story_name) + 1); strncat(f_setup.command_name, EXT_COMMAND, strlen(EXT_COMMAND)); - f_setup.save_name = malloc((strlen(f_setup.story_name) + strlen(EXT_SAVE)) * sizeof(char) + 1); - strncpy(f_setup.save_name, f_setup.story_name, strlen(f_setup.story_name) + 1); - strncat(f_setup.save_name, EXT_SAVE, strlen(EXT_SAVE)); + if (!f_setup.restore_mode) { + f_setup.save_name = malloc(strlen(f_setup.story_name) * sizeof(char) + 5); + strncpy(f_setup.save_name, f_setup.story_name, strlen(f_setup.story_name)); + strncat(f_setup.save_name, EXT_SAVE, strlen(EXT_SAVE)); + } else { /*Set our auto load save as the name_save*/ + f_setup.save_name = malloc(strlen(f_setup.tmp_save_name) * sizeof(char) + 5); + strncpy(f_setup.save_name, f_setup.tmp_save_name, strlen(f_setup.tmp_save_name)); + free(f_setup.tmp_save_name); + } - f_setup.aux_name = malloc(strlen(f_setup.story_name) * sizeof(char) + 5); - strncpy(f_setup.aux_name, f_setup.story_name, strlen(f_setup.story_name)); + f_setup.aux_name = malloc((strlen(f_setup.story_name) + strlen(EXT_AUX)) * sizeof(char) + 1); + strncpy(f_setup.aux_name, f_setup.story_name, strlen(f_setup.story_name) + 1); strncat(f_setup.aux_name, EXT_AUX, strlen(EXT_AUX)); - if (strncmp(basename(f_setup.story_file), - basename(u_setup.blorb_file), 55)) { - return; - } else if (!(blorbfile = fopen(u_setup.blorb_file, "rb"))) { - fprintf(stderr, "Error: Cannot read blorb file %s.\n", u_setup.blorb_file); - } else if (bb_create_map(blorbfile, &blorb_map) != bb_err_None) { - fputs("Error: Blorb file loaded, but unable to build map.\n", stderr ); - } else if (bb_load_chunk_by_type(blorb_map, bb_method_FilePos, - &blorb_res, bb_make_id('Z','C','O','D'), 0) != bb_err_None) { - fputs("Error: Blorb file loaded, but lacks executable chunk.\n", stderr); - } else { - u_setup.exec_in_blorb = 1; - u_setup.use_blorb = 1; - } }/* os_process_arguments */ + /* * os_init_screen * @@@ -993,9 -949,8 +970,9 @@@ static int geterrmode(char *value * cleanly resize the window. * */ - - static void sigwinch_handler(int sig) -void sigwinch_handler(int UNUSED(sig)) --{ ++// FIXME: figure out what to do with this ++//static void sigwinch_handler(int UNUSED(sig)) ++//{ /* There are some significant problems involved in getting resizes to work properly with at least this implementation of the Z Machine and probably @@@ -1003,8 -958,8 +980,8 @@@ the Z-Machine standard itself. See th explaination for this. Because of this trouble, this function currently does nothing. */ -} ++//} - } /* * sigint_handler @@@ -1012,10 -967,12 +989,12 @@@ * is not done. * */ -void sigint_handler(int dummy) +static void sigint_handler(int dummy) { signal(SIGINT, sigint_handler); + dummy = dummy; + os_stop_sample(0); scrollok(stdscr, TRUE); scroll(stdscr); refresh(); endwin(); @@@ -1073,22 -1028,57 +1051,76 @@@ void os_init_setup(void } +#ifdef NO_STRRCHR +/* + * This is for operating systems that lack strrchr(3). + * + */ +char *strrchr(const char *s, int c) +{ + const char *save; + + if (c == 0) return (char *)s + strlen(s); + save = 0; + while (*s) { + if (*s == c) + save = s; + s++; + } + return (char *)save; - } ++#endif /* NO_STRRCHR */ ++ + + /* A unix-like getopt, but with the names changed to avoid any problems. */ + static int zgetopt (int argc, char *argv[], const char *options) + { + static int pos = 1; + const char *p; + if (zoptind >= argc || argv[zoptind][0] != '-' || argv[zoptind][1] == 0) + return EOF; + zoptopt = argv[zoptind][pos++]; + zoptarg = NULL; + if (argv[zoptind][pos] == 0) { + pos = 1; + zoptind++; + } + p = strchr (options, zoptopt); + if (zoptopt == ':' || p == NULL) { + fputs ("illegal option -- ", stderr); + goto error; + } else if (p[1] == ':') { + if (zoptind >= argc) { + fputs ("option requires an argument -- ", stderr); + goto error; + } else { + zoptarg = argv[zoptind]; + if (pos != 1) + zoptarg += pos; + pos = 1; zoptind++; + } + } + return zoptopt; + error: + fputc (zoptopt, stderr); + fputc ('\n', stderr); + return '?'; + }/* zgetopt */ + + + static void print_version(void) + { + printf("FROTZ V%s\t", VERSION); + #ifndef NO_SOUND + printf("Audio output enabled."); + #else + printf("Audio output disabled."); #endif + printf("\nGit commit:\t%s\n", GIT_HASH); + printf("Git tag:\t%s\n", GIT_TAG); + printf("Git branch:\t%s\n", GIT_BRANCH); + printf(" Frotz was originally written by Stefan Jokisch\n"); + printf(" It was ported to Unix by Galen Hazelwood.\n"); + printf(" The core and Unix port are currently maintained by David Griffith.\n"); + printf(" See https://github.com/DavidGriffith/frotz for Frotz's homepage\n\n"); + return; + } diff --cc src/curses/ux_input.c index ee91011,97e0d01..704abc0 --- a/src/curses/ux_input.c +++ b/src/curses/ux_input.c @@@ -36,6 -36,6 +36,9 @@@ #include "ux_frotz.h" ++static int start_of_prev_word(int, const zchar*); ++static int end_of_next_word(int, const zchar*, int); ++ static struct timeval global_timeout; /* Some special characters. */ @@@ -227,10 -230,8 +235,10 @@@ static int unix_read_char(int extkeys case MOD_META | 'x': return ZC_HKEY_QUIT; case MOD_META | 'd': return ZC_HKEY_DEBUG; case MOD_META | 'h': return ZC_HKEY_HELP; + case MOD_META | 'f': return ZC_WORD_RIGHT; + case MOD_META | 'b': return ZC_WORD_LEFT; - /* these are the emacs-editing characters */ + /* these are the emacs-editing characters */ case MOD_CTRL ^ 'B': return ZC_ARROW_LEFT; case MOD_CTRL ^ 'F': return ZC_ARROW_RIGHT; case MOD_CTRL ^ 'P': return ZC_ARROW_UP; @@@ -714,36 -691,3 +731,35 @@@ void *memmove(void *s, void *t, size_t } #endif /* NO_MEMMOVE */ + + +/* + * Search for start of preceding word + * param currpos marker position + * param buf input buffer + * returns new position + */ - int start_of_prev_word(int currpos, const zchar* buf) { ++static int start_of_prev_word(int currpos, const zchar* buf) { + int i, j; + for (i = currpos - 1; i > 0 && buf[i] == ' '; i--) {} + j = i; + for (; i > 0 && buf[i] != ' '; i--) {} + if (i < j && i != 0) { + i += 1; + } + return i; +} + +/* + * Search for end of next word + * param currpos marker position + * param buf input buffer + * param len length of buf + * returns new position + */ - int end_of_next_word(int currpos, const zchar* buf, int len) { - int i, j; ++static int end_of_next_word(int currpos, const zchar* buf, int len) { ++ int i; + for (i = currpos; i < len && buf[i] == ' '; i++) {} - j = i; + for (; i < len && buf[i] != ' '; i++) {} + return i; +} diff --cc src/dumb/dumb_init.c index 5ac463a,a4617b5..dec3ac8 --- a/src/dumb/dumb_init.c +++ b/src/dumb/dumb_init.c @@@ -232,23 -207,5 +231,22 @@@ void os_init_setup(void f_setup.script_cols = 80; f_setup.sound = 1; f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE; + f_setup.restore_mode = 0; + +} - +char *my_strdup(char *src) +{ + char *str; + char *p; + int len = 0; + + while (src[len]) + len++; + str = malloc(len + 1); + p = str; + while (*src) + *p++ = *src++; + *p = '\0'; + return str; }