diff options
67 files changed, 2792 insertions, 640 deletions
@@ -10,9 +10,12 @@ config.log config.status config.h autom4te.cache +rpm.spec stamp-h stamp-h.in stamp-h1 +tags +TAGS # These are the default patterns globally ignored by Subversion: *.o @@ -23,7 +23,7 @@ Building Chocolate Doom On a Unix system, follow the standard instructions for installing an autotools-based package: - 1. Run './configure' to initialise the package. + 1. Run './configure' to initialize the package. 2. Run 'make' to compile the package. 3. Run 'make install' to install the package. diff --git a/Makefile.am b/Makefile.am index 15ad2252..e331d204 100644 --- a/Makefile.am +++ b/Makefile.am @@ -32,9 +32,12 @@ CODEBLOCKS_FILES= \ codeblocks/setup-res.rc DATA_FILES= \ + data/README \ data/doom.ico \ + data/doom8.ico \ data/doom.png \ data/setup.ico \ + data/setup8.ico \ data/setup.png \ data/convert-icon @@ -43,11 +46,13 @@ EXTRA_DIST= \ $(MSVC_FILES) \ $(CODEBLOCKS_FILES) \ $(DATA_FILES) \ + .lvimrc \ config.h \ CMDLINE \ HACKING \ TODO \ - BUGS + BUGS \ + rpm.spec MAINTAINERCLEANFILES = $(AUX_DIST_GEN) @@ -57,6 +62,8 @@ DIST_SUBDIRS=pkg $(SUBDIRS) if HAVE_PYTHON +noinst_DATA=CMDLINE + CMDLINE : src/ ./man/docgen -p man/CMDLINE.template src/ > $@ @@ -1,48 +1,96 @@ ... * Chocolate Doom now runs on Windows Mobile/Windows CE! - * It is possible to rebind most/all of the keys that control - the menu, shortcuts, automap and weapon switching. The - main reason for this is to support the Windows CE port - and other platforms where a full keyboard may not be present. - * Memory-mapped WAD I/O is disabled by default, as it caused - various issues, including a slowdown/crash with Plutonia 2 - MAP23. It can be explicitly re-enabled using the '-mmap' - command line parameter. + * It is possible to rebind most/all of the keys that control the + menu, shortcuts, automap and weapon switching. The main + reason for this is to support the Windows CE port and other + platforms where a full keyboard may not be present. + * Chocolate Doom now includes a proper Mac OS X package; it is + no longer necessary to compile binaries for this system by + hand. The package includes a simple graphical launcher + program and can be installed simply by dragging the "Chocolate + Doom" icon to the Applications folder. (thanks to Rikard Lang + for extensive testing and feedback) * The video mode auto-adjust code will automatically choose windowed mode if no fullscreen video modes are available. - * The zone memory size is automatically reduced on systems - with a small amount of memory. - * There is now a second, small textscreen font, so that the - ENDOOM screen and setup tool can be used on low resolution - devices (eg. PDAs/embedded devices) - * The textscreen library now has a scrollable pane widget. - * Doxygen documentation was added for the textscreen library. - * The "join game" window in the setup tool now has an option - to automatically join a game on the local network. + * The zone memory size is automatically reduced on systems with + a small amount of memory. + * The "join game" window in the setup tool now has an option to + automatically join a game on the local network. + * Chocolate Doom includes some initial hacks for compiling under + SDL 1.3. + * Recent versions of SDL_mixer include rewritten MIDI code on Mac + OS X. If you are using a version of SDL_mixer with the new + code, music will now be enabled by default. + * Windows Vista and Windows 7 no longer prompt for elevated + privileges when running the setup tool (thanks hobbs and + MikeRS). + * The Windows binaries now have better looking icons (thanks + MikeRS). + * Magic values specified using the -spechit command line + parameter can now be hexadecimal. + * DOOMWADDIR/DOOMWADPATH can now specify the complete path to + IWAD files, rather than the path to the directory that contains + them. + * When recording shorttics demos, errors caused by the reduced + turning resolution are carried forward, possibly making turning + smoother. Compatibility: * The A_BossDeath behavior in v1.9 emulation mode was fixed (thanks entryway) + * The "loading" disk icon is drawn more like how it is drawn in + Vanilla Doom, also fixing a bug with chook3.wad. + * Desync on 64-bit systems with ep1-0500.lmp has (at long last) + been fixed (thanks exp(x)). + * Donut overrun emulation code imported from Prboom+ (thanks + entryway). + * The correct level name should now be shown in the automap for + pl2.wad MAP33 (thanks Janizdreg). + * In Chex Quest, the green radiation suit colormap is now used + instead of the red colormaps normally used when taking damage + or using the berserk pack. This matches Vanilla chex.exe + behavior (thanks Fuzztooth). + * Impassible glass now displays and works the same as in Vanilla, + fixing wads such as OTTAWAU.WAD (thanks Never_Again). Bugs fixed: + * Memory-mapped WAD I/O is disabled by default, as it caused + various issues, including a slowdown/crash with Plutonia 2 + MAP23. It can be explicitly re-enabled using the '-mmap' + command line parameter. * Crash when saving games due to the ~/.chocolate-doom/savegames directory not being created (thanks to everyone who reported this). - * Chocolate Doom will now under Win95/98, as the + * Chocolate Doom will now run under Win95/98, as the SetProcessAffinityMask function is looked up dynamically. * Compilation under Linux with older versions of libc will now work (the semantics for sched_setaffinity were different in older versions) * Sound clipping when using libsamplerate was improved (thanks David Flater) - * The audio buffer size is now calculated based on the sample rate, - so there is not a noticeable delay when using a lower sample - rate. - * The manpage documentation for the DOOMWADPATH variable was fixed - (thanks MikeRS). - * Compilation with FEATURE_MULTIPLAYER and FEATURE_SOUND disabled - was fixed. + * The audio buffer size is now calculated based on the sample + rate, so there is not a noticeable delay when using a lower + sample rate. + * The manpage documentation for the DOOMWADPATH variable was + fixed (thanks MikeRS). + * Compilation with FEATURE_MULTIPLAYER and FEATURE_SOUND + disabled was fixed. + * Fixed crash when using the donut special type and the joining + linedef is one sided (thanks Alexander Waldmann). + * Key settings in a configuration file that are out of range + do not cause a crash (thanks entryway). + * Fix ear-piercing whistle when playing the MAP05 MIDI music + using timidity with EAWPATS (thanks entryway / HackNeyed). + + libtextscreen: + * There is now a second, small textscreen font, so that the + ENDOOM screen and setup tool can be used on low resolution + devices (eg. PDAs/embedded devices) + * The textscreen library now has a scrollable pane widget. Thanks + to LionsPhil for contributing code to scroll up and down using + the keyboard. + * Doxygen documentation was added for the textscreen library. 1.2.1 (2008-12-10): @@ -1,17 +1,22 @@ +Currently in progress: + +* OPL MIDI playback (see: opl-branch) +* Heretic/Hexen support (see: raven-branch) +* Strife support (see: strife-branch) + To do: -* Install packages: - - Debian/Ubuntu .deb packages, Fedora .rpm packages. - - Windows NSIS installer. - - MacOS X .dmg packages (should be universal binaries!) +* Demo hashes for regression testing of this and other ports. * File selector for chocolate-setup, so that WADs can be selected from a browser, instead of simply typing the filenames. * Multiplayer: - Master server for locating servers automatically - makes setting up public servers easier. + - Use UPnP to automatically configure port forwarding for NATted + networks. - Incorporate local LAN search into setup interface. - Multiplayer options and configuration file (server name, etc) -* Improve multiplayer startup: +* Improve multiplayer startup: - Select an IWAD automatically from the server's game type rather than all players having to specify -iwad. - Send list of WADs to load instead of all clients having to specify -file. @@ -20,16 +25,14 @@ To do: - Test on and fix for architectures where ((-2) >> 1) != -1 - Use size-specific types (eg. int32_t instead of int) - Don't make structure packing assumptions when loading levels. -* Port to every OS and architecture under the sun + - Port to every OS and architecture under the sun Crazy pie in the sky ideas: * Automatic WAD installer - download and run TCs from a list automatically (automating the current "instructions on wiki" system). -* Textscreen interface to the Compet-N database: menu driven system to +* Textscreen interface to the Compet-N database: menu driven system to automatically download and play speedruns. * DWANGO-like interface for finding players and setting up games. -* Demo hashes for regression testing of this and other ports. -* OPL emulation * Video capture mode? @@ -8,5 +8,5 @@ automake -a -c autoconf automake -./configure $@ +./configure "$@" diff --git a/configure.in b/configure.in index 0f4b48a0..e3287d00 100644 --- a/configure.in +++ b/configure.in @@ -1,5 +1,11 @@ AC_INIT(Chocolate Doom, 1.2.1, fraggle@gmail.com, chocolate-doom) +PACKAGE_SHORTDESC="Conservative Doom source port" +PACKAGE_COPYRIGHT="Copyright (C) 1993-2010" +PACKAGE_LICENSE="GNU General Public License, version 2" +PACKAGE_MAINTAINER="Simon Howard" +PACKAGE_URL="http://www.chocolate-doom.org/" + AC_CONFIG_AUX_DIR(autotools) orig_CFLAGS="$CFLAGS" @@ -120,27 +126,35 @@ AC_SUBST(SDLNET_LIBS) AC_SUBST(ac_aux_dir) +AC_SUBST(PACKAGE_SHORTDESC) +AC_SUBST(PACKAGE_COPYRIGHT) +AC_SUBST(PACKAGE_LICENSE) +AC_SUBST(PACKAGE_MAINTAINER) +AC_SUBST(PACKAGE_URL) + dnl Shut up the datarootdir warnings. AC_DEFUN([AC_DATAROOTDIR_CHECKED]) AC_OUTPUT([ Makefile -wince/Makefile -textscreen/Makefile -textscreen/examples/Makefile man/Makefile +pcsound/Makefile +pkg/Makefile +pkg/config.make +pkg/osx/Info-gnustep.plist +pkg/osx/Info.plist +rpm.spec src/Makefile +src/doom-screensaver.desktop src/doom/Makefile src/heretic/Makefile src/hexen/Makefile -src/setup/Makefile -pcsound/Makefile -pkg/Makefile -pkg/wince/GNUmakefile -pkg/win32/GNUmakefile src/resource.rc src/setup-res.rc +src/setup/Makefile src/setup/setup-manifest.xml -src/doom-screensaver.desktop +textscreen/Makefile +textscreen/examples/Makefile +wince/Makefile ]) diff --git a/data/README b/data/README index 8b927335..82fac17e 100644 --- a/data/README +++ b/data/README @@ -3,3 +3,10 @@ The Chocolate Doom icon is based on an image by Chris Metcalf http://www.flickr.com/photos/laffy4k/448920776/ +The "foo8.ico" files are 8-bit depth only, while the "foo.ico" files +contain full 32-bit versions, scaled to different sizes and with proper +alpha masks (as well as the 8-bit versions). The 8-bit versions are +used when setting the icon within SDL, as SDL under Windows displays +full color icons in a very poor quality. The full-color versions are +used in the resource files. + diff --git a/data/doom.ico b/data/doom.ico Binary files differindex f55ff28f..025cb698 100644 --- a/data/doom.ico +++ b/data/doom.ico diff --git a/data/doom8.ico b/data/doom8.ico Binary files differnew file mode 100644 index 00000000..f55ff28f --- /dev/null +++ b/data/doom8.ico diff --git a/data/setup.ico b/data/setup.ico Binary files differindex 6d734a11..dae4e5ec 100644 --- a/data/setup.ico +++ b/data/setup.ico diff --git a/data/setup8.ico b/data/setup8.ico Binary files differnew file mode 100644 index 00000000..6d734a11 --- /dev/null +++ b/data/setup8.ico diff --git a/man/Makefile.am b/man/Makefile.am index d3c90f7e..698c0862 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -5,9 +5,9 @@ if HAVE_PYTHON man_MANS=chocolate-doom.6 \ chocolate-server.6 \ - chocolate-setup.6 \ - default.cfg.5 \ - $(PACKAGE).cfg.5 + chocolate-setup.6 \ + default.cfg.5 \ + $(PACKAGE).cfg.5 chocolate-doom.6: ../src $(MANPAGE_GEN_FILES) ./docgen -m manpage.template ../src > $@ @@ -21,5 +21,6 @@ $(PACKAGE).cfg.5: ../src extra.cfg.template endif EXTRA_DIST = $(man_MANS) $(MANPAGE_GEN_FILES) \ - wikipages + wikipages \ + CMDLINE.template diff --git a/pcsound/.gitignore b/pcsound/.gitignore index 9889f6d5..022c663e 100644 --- a/pcsound/.gitignore +++ b/pcsound/.gitignore @@ -3,4 +3,6 @@ Makefile .deps libpcsound.a *.rc +tags +TAGS diff --git a/pkg/.gitignore b/pkg/.gitignore new file mode 100644 index 00000000..a1dd1cde --- /dev/null +++ b/pkg/.gitignore @@ -0,0 +1,3 @@ +Makefile +Makefile.in +config.make diff --git a/pkg/Makefile.am b/pkg/Makefile.am index 66cb9ba0..63da8f53 100644 --- a/pkg/Makefile.am +++ b/pkg/Makefile.am @@ -1,3 +1,35 @@ -DIST_SUBDIRS=wince win32 +OSX_FILES= \ +osx/Resources/128x128.png \ +osx/Resources/app.icns \ +osx/Resources/app.png \ +osx/Resources/wadfile.icns \ +osx/Resources/wadfile.png \ +osx/Resources/launcher.nib/classes.nib \ +osx/Resources/launcher.nib/info.nib \ +osx/Resources/launcher.nib/keyedobjects.nib \ +osx/GNUmakefile \ +osx/Info.plist.in osx/Info-gnustep.plist.in \ +osx/PkgInfo \ +osx/cp-with-libs \ +osx/main.m \ +osx/AppController.m osx/AppController.h \ +osx/Execute.m osx/Execute.h \ +osx/IWADController.m osx/IWADController.h \ +osx/IWADLocation.m osx/IWADLocation.h \ +osx/LauncherManager.m osx/LauncherManager.h + +WINCE_FILES= \ +wince/GNUmakefile \ +wince/common.py \ +wince/doom-cab.cfg \ +wince/heretic-cab.cfg \ +wince/hexen-cab.cfg \ +wince/wince-cabgen + +WIN32_FILES= \ +win32/GNUmakefile \ +win32/README + +EXTRA_DIST=$(OSX_FILES) $(WINCE_FILES) $(WIN32_FILES) diff --git a/pkg/config.make.in b/pkg/config.make.in new file mode 100644 index 00000000..b8a1dbe5 --- /dev/null +++ b/pkg/config.make.in @@ -0,0 +1,29 @@ +# Shared file included by the makefiles used to build packages. +# This contains various information needed by the makefiles, +# and is autogenerated by configure to include various +# necessary details. + +# Tools needed: + +CC = @CC@ +STRIP = @STRIP@ + +# Package name and version number: + +PROGRAM_PREFIX = @PROGRAM_PREFIX@ +PACKAGE = @PACKAGE@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ + +# Documentation files to distribute with packages. + +DOC_FILES = README \ + COPYING \ + ChangeLog \ + NEWS \ + BUGS \ + CMDLINE \ + TODO + diff --git a/pkg/osx/.gitignore b/pkg/osx/.gitignore new file mode 100644 index 00000000..ca1a7908 --- /dev/null +++ b/pkg/osx/.gitignore @@ -0,0 +1,7 @@ +Info.plist +Info-gnustep.plist +launcher +*.o +*.d +*.dmg +staging diff --git a/pkg/osx/AppController.h b/pkg/osx/AppController.h new file mode 100644 index 00000000..88b59043 --- /dev/null +++ b/pkg/osx/AppController.h @@ -0,0 +1,53 @@ +/* ... */ +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +//----------------------------------------------------------------------------- + +#ifndef LAUNCHER_APPCONTROLLER_H +#define LAUNCHER_APPCONTROLLER_H + +#include <AppKit/AppKit.h> + +#include "LauncherManager.h" + +@interface AppController : NSObject +{ + LauncherManager *launcherManager; + BOOL filesAdded; +} + ++ (void)initialize; + +- (id)init; +- (void)dealloc; + +- (void)awakeFromNib; + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotif; +- (BOOL)applicationShouldTerminate:(id)sender; +- (void)applicationWillTerminate:(NSNotification *)aNotif; +- (BOOL)application:(NSApplication *)application openFile:(NSString *)fileName; + +- (void)showPrefPanel:(id)sender; + +@end + +#endif + diff --git a/pkg/osx/AppController.m b/pkg/osx/AppController.m new file mode 100644 index 00000000..a26a7c9e --- /dev/null +++ b/pkg/osx/AppController.m @@ -0,0 +1,124 @@ +/* ... */ +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +//----------------------------------------------------------------------------- + +#include "AppController.h" + +#include "config.h" + +@implementation AppController + ++ (void)initialize +{ + NSMutableDictionary *defaults = [NSMutableDictionary dictionary]; + + /* + * Register your app's defaults here by adding objects to the + * dictionary, eg + * + * [defaults setObject:anObject forKey:keyForThatObject]; + * + */ + + [[NSUserDefaults standardUserDefaults] registerDefaults:defaults]; + [[NSUserDefaults standardUserDefaults] synchronize]; +} + +- (id)init +{ + if ((self = [super init])) + { + } + + self->filesAdded = NO; + + return self; +} + +- (void)dealloc +{ + [super dealloc]; +} + +- (void)awakeFromNib +{ + [[NSApp mainMenu] setTitle:@PACKAGE_NAME]; +} + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotif +{ +// Uncomment if your application is Renaissance-based +// [NSBundle loadGSMarkupNamed:@"Main" owner:self]; +} + +- (BOOL)applicationShouldTerminate:(id)sender +{ + return YES; +} + +- (void)applicationWillTerminate:(NSNotification *)aNotif +{ +} + +- (BOOL) application:(NSApplication *) application + openFile:(NSString *) fileName +{ + NSString *extension; + + // If this is the first file added, clear out the existing + // command line. This allows us to select multiple files + // in the finder and open them all together (for TCs, etc). + + if (!self->filesAdded) + { + [self->launcherManager clearCommandLine]; + } + + // Add file with appropriate command line option based on extension: + + extension = [fileName pathExtension]; + + if (![extension caseInsensitiveCompare: @"wad"]) + { + [self->launcherManager addFileToCommandLine: fileName + forArgument: @"-merge"]; + } + else if (![extension caseInsensitiveCompare: @"deh"]) + { + [self->launcherManager addFileToCommandLine: fileName + forArgument: @"-deh"]; + } + else + { + return NO; + } + + self->filesAdded = YES; + + return YES; +} + +- (void)showPrefPanel:(id)sender +{ +} + +@end + diff --git a/pkg/osx/Execute.h b/pkg/osx/Execute.h new file mode 100644 index 00000000..2098be8a --- /dev/null +++ b/pkg/osx/Execute.h @@ -0,0 +1,31 @@ +/* ... */ +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +//----------------------------------------------------------------------------- + +#ifndef LAUNCHER_EXECUTE_H +#define LAUNCHER_EXECUTE_H + +void SetProgramLocation(const char *path); +void ExecuteProgram(const char *executable, const char *iwad, const char *args); +void OpenTerminalWindow(const char *doomwadpath); + +#endif /* #ifndef LAUNCHER_EXECUTE_H */ + diff --git a/pkg/osx/Execute.m b/pkg/osx/Execute.m new file mode 100644 index 00000000..608443e2 --- /dev/null +++ b/pkg/osx/Execute.m @@ -0,0 +1,208 @@ +/* ... */ +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +//----------------------------------------------------------------------------- + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include <AppKit/AppKit.h> + +#include "config.h" + +#define RESPONSE_FILE "/tmp/launcher.rsp" +#define TEMP_SCRIPT "/tmp/tempscript.sh" + +static char *executable_path; + +// Called on startup to save the location of the launcher program +// (within a package, other executables should be in the same directory) + +void SetProgramLocation(const char *path) +{ + char *p; + + executable_path = strdup(path); + + p = strrchr(executable_path, '/'); + *p = '\0'; +} + +// Write out the response file containing command line arguments. + +static void WriteResponseFile(const char *iwad, const char *args) +{ + FILE *fstream; + + fstream = fopen(RESPONSE_FILE, "w"); + + if (iwad != NULL) + { + fprintf(fstream, "-iwad \"%s\"", iwad); + } + + if (args != NULL) + { + fprintf(fstream, "%s", args); + } + + fclose(fstream); +} + +static void DoExec(const char *executable, const char *iwad, const char *args) +{ + char *argv[3]; + + argv[0] = malloc(strlen(executable_path) + strlen(executable) + 3); + sprintf(argv[0], "%s/%s", executable_path, executable); + + if (iwad != NULL || args != NULL) + { + WriteResponseFile(iwad, args); + + argv[1] = "@" RESPONSE_FILE; + argv[2] = NULL; + } + else + { + argv[1] = NULL; + } + + execv(argv[0], argv); + exit(-1); +} + +// Execute the specified executable contained in the same directory +// as the launcher, with the specified arguments. + +void ExecuteProgram(const char *executable, const char *iwad, const char *args) +{ + pid_t childpid; + char *homedir; + + childpid = fork(); + + if (childpid == 0) + { + signal(SIGCHLD, SIG_DFL); + + // Change directory to home dir before launch, so that any demos + // are saved somewhere sensible. + + homedir = getenv("HOME"); + + if (homedir != NULL) + { + chdir(homedir); + } + + DoExec(executable, iwad, args); + } + else + { + signal(SIGCHLD, SIG_IGN); + } +} + +// Write a sequence of commands that will display the specified message +// via shell commands. + +static void WriteMessage(FILE *script, char *msg) +{ + char *p; + + fprintf(script, "echo \""); + + for (p=msg; *p != '\0'; ++p) + { + // Start new line? + + if (*p == '\n') + { + fprintf(script, "\"\necho \""); + continue; + } + + // Escaped character? + + if (*p == '\\' || *p == '\"') + { + fprintf(script, "\\"); + } + + fprintf(script, "%c", *p); + } + + fprintf(script, "\"\n"); +} + +// Open a terminal window with the PATH set appropriately, and DOOMWADPATH +// set to the specified value. + +void OpenTerminalWindow(const char *doomwadpath) +{ + FILE *stream; + + // Generate a shell script that sets the PATH to include the location + // where the Doom binaries are, and DOOMWADPATH to include the + // IWAD files that have been configured in the launcher interface. + // The script then deletes itself and starts a shell. + + stream = fopen(TEMP_SCRIPT, "w"); + + fprintf(stream, "#!/bin/sh\n"); + //fprintf(stream, "set -x\n"); + fprintf(stream, "PATH=\"%s:$PATH\"\n", executable_path); + fprintf(stream, "DOOMWADPATH=\"%s\"\n", doomwadpath); + fprintf(stream, "export DOOMWADPATH\n"); + fprintf(stream, "rm -f \"%s\"\n", TEMP_SCRIPT); + + // Display a useful message: + + fprintf(stream, "clear\n"); + WriteMessage(stream, + "\n" + "This command line has the PATH variable configured so that you may\n" + "launch the game with whatever parameters you desire.\n" + "\n" + "For example:\n" + "\n" + " " PACKAGE_TARNAME " -iwad doom2.wad -file sid.wad -warp 1\n" + "\n" + "Type 'exit' to exit.\n"); + + fprintf(stream, "exec $SHELL\n"); + fprintf(stream, "\n"); + + fclose(stream); + + chmod(TEMP_SCRIPT, 0755); + + // Tell the terminal to open a window to run the script. + + [[NSWorkspace sharedWorkspace] openFile: @TEMP_SCRIPT + withApplication: @"Terminal"]; +} + diff --git a/pkg/osx/GNUmakefile b/pkg/osx/GNUmakefile new file mode 100644 index 00000000..d119efa1 --- /dev/null +++ b/pkg/osx/GNUmakefile @@ -0,0 +1,101 @@ + +# Makefile for building the OS X launcher program and DMG package. +# It is also possible to build and run the launcher under Unix +# systems using GNUstep, although this is only here for development +# and debugging purposes. + +include ../config.make + +STAGING_DIR=staging +DMG=$(PACKAGE_TARNAME)-$(PACKAGE_VERSION).dmg + +TOPLEVEL=../.. +TOPLEVEL_DOCS=$(patsubst %,../../%,$(DOC_FILES)) + +ifndef GNUSTEP_MAKEFILES + +# DMG file containing package: + +$(DMG) : $(STAGING_DIR) + rm -f $@ + hdiutil create -volname "$(PACKAGE_STRING)" -srcdir $(STAGING_DIR) $@ + +endif + +# Staging dir build for package: + +APP_DIR=$(STAGING_DIR)/$(PACKAGE_NAME).app + +# OS X and GNUstep apps have a slightly different internal structure: +# OS X apps have their files within a containing "Contents" directory +# that does not exist in GNUstep apps. Similarly, the binaries are +# installed at the top level, rather than in a "MacOS" directory. +# Finally, we must install a different Info.plist file. + +ifdef GNUSTEP_MAKEFILES +APP_TOP_DIR=$(APP_DIR) +APP_BIN_DIR=$(APP_DIR) +SRC_INFO_PLIST=Info-gnustep.plist +else +APP_TOP_DIR=$(APP_DIR)/Contents +APP_BIN_DIR=$(APP_DIR)/Contents/MacOS +SRC_INFO_PLIST=Info.plist +endif + +$(STAGING_DIR): launcher $(TOPLEVEL_DOCS) + rm -rf $(STAGING_DIR) + mkdir $(STAGING_DIR) + + cp $(TOPLEVEL_DOCS) "$(STAGING_DIR)" + + mkdir -p "$(APP_TOP_DIR)" + cp -R Resources "$(APP_TOP_DIR)" + cp PkgInfo "$(APP_TOP_DIR)" + cp $(SRC_INFO_PLIST) "$(APP_TOP_DIR)" + + mkdir -p "$(APP_BIN_DIR)" + + cp launcher "$(APP_BIN_DIR)" + $(STRIP) "$(APP_BIN_DIR)/launcher" + + ./cp-with-libs $(TOPLEVEL)/src/$(PACKAGE_TARNAME) "$(APP_BIN_DIR)" + $(STRIP) "$(APP_BIN_DIR)/$(PACKAGE_TARNAME)" + ./cp-with-libs $(TOPLEVEL)/setup/chocolate-setup "$(APP_BIN_DIR)" + $(STRIP) "$(APP_BIN_DIR)/chocolate-setup" + + find $(STAGING_DIR) -name .svn -delete -exec rm -rf {} \; || true + +clean : launcher_clean + rm -f $(DMG) + rm -rf $(STAGING_DIR) + +# Launcher build: + +CFLAGS = -Wall -I$(TOPLEVEL) + +# Are we building using gs_make? + +ifdef GNUSTEP_MAKEFILES +CFLAGS += $(shell gnustep-config --objc-flags) +LDFLAGS = $(shell gnustep-config --gui-libs) +else +LDFLAGS = -framework Cocoa +endif + +LAUNCHER_OBJS= \ + AppController.o \ + Execute.o \ + IWADController.o \ + IWADLocation.o \ + LauncherManager.o \ + main.o + +launcher : $(LAUNCHER_OBJS) + $(CC) $(LDFLAGS) $(LAUNCHER_OBJS) -o $@ + +%.o : %.m + $(CC) -c $(CFLAGS) $^ -o $@ + +launcher_clean : + rm -f $(LAUNCHER_OBJS) launcher + diff --git a/pkg/osx/IWADController.h b/pkg/osx/IWADController.h new file mode 100644 index 00000000..90f44667 --- /dev/null +++ b/pkg/osx/IWADController.h @@ -0,0 +1,54 @@ +/* ... */ +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +//----------------------------------------------------------------------------- + +#ifndef LAUNCHER_IWADCONTROLLER_H +#define LAUNCHER_IWADCONTROLLER_H + +#include <AppKit/AppKit.h> +#include <AppKit/NSNibLoading.h> + +@interface IWADController : NSObject +{ + id iwadSelector; + id configWindow; + + id chex; + id doom1; + id doom2; + id plutonia; + id tnt; +} + +- (void) closeConfigWindow: (id)sender; +- (void) openConfigWindow: (id)sender; +- (NSString *) getIWADLocation; +- (void) awakeFromNib; +- (BOOL) setDropdownList; +- (void) setDropdownSelection; +- (void) saveConfig; +- (char *) doomWadPath; +- (void) setEnvironment; + +@end + +#endif /* #ifndef LAUNCHER_IWADCONTROLLER_H */ + diff --git a/pkg/osx/IWADController.m b/pkg/osx/IWADController.m new file mode 100644 index 00000000..3c596850 --- /dev/null +++ b/pkg/osx/IWADController.m @@ -0,0 +1,347 @@ +/* ... */ +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +//----------------------------------------------------------------------------- + +#include <stdlib.h> +#include <string.h> +#include <AppKit/AppKit.h> +#include "IWADController.h" +#include "IWADLocation.h" + +typedef enum +{ + IWAD_DOOM1, + IWAD_DOOM2, + IWAD_TNT, + IWAD_PLUTONIA, + IWAD_CHEX, + NUM_IWAD_TYPES +} IWAD; + +static NSString *IWADLabels[NUM_IWAD_TYPES] = +{ + @"Doom", + @"Doom II: Hell on Earth", + @"Final Doom: TNT: Evilution", + @"Final Doom: Plutonia Experiment", + @"Chex Quest" +}; + +static NSString *IWADFilenames[NUM_IWAD_TYPES + 1] = +{ + @"doom.wad", + @"doom2.wad", + @"tnt.wad", + @"plutonia.wad", + @"chex.wad", + @"undefined" +}; + +@implementation IWADController + +- (void) getIWADList: (IWADLocation **) iwadList +{ + iwadList[IWAD_DOOM1] = self->doom1; + iwadList[IWAD_DOOM2] = self->doom2; + iwadList[IWAD_TNT] = self->tnt; + iwadList[IWAD_PLUTONIA] = self->plutonia; + iwadList[IWAD_CHEX] = self->chex; +} + +- (IWAD) getSelectedIWAD +{ + unsigned int i; + + for (i=0; i<NUM_IWAD_TYPES; ++i) + { + if ([self->iwadSelector titleOfSelectedItem] == IWADLabels[i]) + { + return i; + } + } + + return NUM_IWAD_TYPES; +} + +// Get the location of the selected IWAD. + +- (NSString *) getIWADLocation +{ + IWAD selectedIWAD; + IWADLocation *iwadList[NUM_IWAD_TYPES]; + + selectedIWAD = [self getSelectedIWAD]; + + if (selectedIWAD == NUM_IWAD_TYPES) + { + return nil; + } + else + { + [self getIWADList: iwadList]; + + return [iwadList[selectedIWAD] getLocation]; + } +} + +- (void) setIWADConfig +{ + IWADLocation *iwadList[NUM_IWAD_TYPES]; + NSUserDefaults *defaults; + NSString *key; + NSString *value; + unsigned int i; + + [self getIWADList: iwadList]; + + // Load IWAD filename paths + + defaults = [NSUserDefaults standardUserDefaults]; + + for (i=0; i<NUM_IWAD_TYPES; ++i) + { + key = IWADFilenames[i]; + value = [defaults stringForKey:key]; + + if (value != nil) + { + [iwadList[i] setLocation:value]; + } + } +} + +// On startup, set the selected item in the IWAD dropdown + +- (void) setDropdownSelection +{ + NSUserDefaults *defaults; + NSString *selected; + unsigned int i; + + defaults = [NSUserDefaults standardUserDefaults]; + selected = [defaults stringForKey: @"selected_iwad"]; + + if (selected == nil) + { + return; + } + + // Find this IWAD in the filenames list, and select it. + + for (i=0; i<NUM_IWAD_TYPES; ++i) + { + if ([selected isEqualToString:IWADFilenames[i]]) + { + [self->iwadSelector selectItemWithTitle:IWADLabels[i]]; + break; + } + } +} + +// Set the dropdown list to include an entry for each IWAD that has +// been configured. Returns true if at least one IWAD is configured. + +- (BOOL) setDropdownList +{ + IWADLocation *iwadList[NUM_IWAD_TYPES]; + BOOL have_wads; + id location; + unsigned int i; + unsigned int enabled_wads; + + // Build the new list. + + [self getIWADList: iwadList]; + [self->iwadSelector removeAllItems]; + + enabled_wads = 0; + + for (i=0; i<NUM_IWAD_TYPES; ++i) + { + location = [iwadList[i] getLocation]; + + if (location != nil && [location length] > 0) + { + [self->iwadSelector addItemWithTitle: IWADLabels[i]]; + ++enabled_wads; + } + } + + // Enable/disable the dropdown depending on whether there + // were any configured IWADs. + + have_wads = enabled_wads > 0; + [self->iwadSelector setEnabled: have_wads]; + + // Restore the old selection. + + [self setDropdownSelection]; + + return have_wads; +} + +- (void) saveConfig +{ + IWADLocation *iwadList[NUM_IWAD_TYPES]; + IWAD selectedIWAD; + NSUserDefaults *defaults; + NSString *key; + NSString *value; + unsigned int i; + + [self getIWADList: iwadList]; + + // Store all IWAD locations to user defaults. + + defaults = [NSUserDefaults standardUserDefaults]; + + for (i=0; i<NUM_IWAD_TYPES; ++i) + { + key = IWADFilenames[i]; + value = [iwadList[i] getLocation]; + + [defaults setObject:value forKey:key]; + } + + // Save currently selected IWAD. + + selectedIWAD = [self getSelectedIWAD]; + [defaults setObject:IWADFilenames[selectedIWAD] + forKey:@"selected_iwad"]; +} + +// Callback method invoked when the configuration button in the main +// window is clicked. + +- (void) openConfigWindow: (id)sender +{ + if (![self->configWindow isVisible]) + { + [self->configWindow makeKeyAndOrderFront: sender]; + } +} + +// Callback method invoked when the close button is clicked. + +- (void) closeConfigWindow: (id)sender +{ + [self->configWindow orderOut: sender]; + [self saveConfig]; + [self setDropdownList]; +} + +- (void) awakeFromNib +{ + [self->configWindow center]; + + // Set configuration for all IWADs from configuration file. + + [self setIWADConfig]; + + // Populate the dropdown IWAD list. + + if ([self setDropdownList]) + { + [self setDropdownSelection]; + } +} + +// Generate a value to set for the DOOMWADPATH environment variable +// that contains each of the configured IWAD files. + +- (char *) doomWadPath +{ + IWADLocation *iwadList[NUM_IWAD_TYPES]; + NSString *location; + unsigned int i; + unsigned int len; + BOOL first; + char *env; + + [self getIWADList: iwadList]; + + // Calculate length of environment string. + + len = 0; + + for (i=0; i<NUM_IWAD_TYPES; ++i) + { + location = [iwadList[i] getLocation]; + + if (location != nil && [location length] > 0) + { + len += [location length] + 1; + } + } + + // Build string. + + env = malloc(len); + strcpy(env, ""); + + first = YES; + + for (i=0; i<NUM_IWAD_TYPES; ++i) + { + location = [iwadList[i] getLocation]; + + if (location != nil && [location length] > 0) + { + if (!first) + { + strcat(env, ":"); + } + + strcat(env, [location UTF8String]); + first = NO; + } + } + + return env; +} + +// Set the DOOMWADPATH environment variable to contain the path to each +// of the configured IWAD files. + +- (void) setEnvironment +{ + char *doomwadpath; + char *env; + + // Get the value for the path. + + doomwadpath = [self doomWadPath]; + + env = malloc(strlen(doomwadpath) + 15); + + sprintf(env, "DOOMWADPATH=%s", doomwadpath); + + free(doomwadpath); + + // Load into environment: + + putenv(env); + + //free(env); +} + +@end + diff --git a/pkg/osx/IWADLocation.h b/pkg/osx/IWADLocation.h new file mode 100644 index 00000000..4ddfebc2 --- /dev/null +++ b/pkg/osx/IWADLocation.h @@ -0,0 +1,44 @@ +/* ... */ +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +//----------------------------------------------------------------------------- + +#ifndef LAUNCHER_IWADLOCATION_H +#define LAUNCHER_IWADLOCATION_H + +#include <AppKit/AppKit.h> + +#include "IWADController.h" + +@interface IWADLocation : NSObject +{ + IWADController *iwadController; + + id locationConfigBox; +} + +- (void) setButtonClicked: (id)sender; +- (NSString *) getLocation; +- (void) setLocation: (NSString *) value; + +@end + +#endif /* #ifndef LAUNCHER_IWADLOCATION_H */ + diff --git a/pkg/osx/IWADLocation.m b/pkg/osx/IWADLocation.m new file mode 100644 index 00000000..3f2ac188 --- /dev/null +++ b/pkg/osx/IWADLocation.m @@ -0,0 +1,74 @@ +/* ... */ +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +//----------------------------------------------------------------------------- + +#include <AppKit/AppKit.h> +#include "IWADLocation.h" + +static id WAD_TYPES[] = +{ + @"wad", @"WAD" +}; + +@implementation IWADLocation + +- (void) setButtonClicked: (id)sender +{ + NSArray *wadTypes = [NSArray arrayWithObjects: WAD_TYPES count: 2]; + NSOpenPanel *openPanel; + NSArray *filenames; + int result; + + [wadTypes retain]; + + // Open a file selector for the new file. + + openPanel = [NSOpenPanel openPanel]; + [openPanel setTitle: @"Add IWAD file"]; + [openPanel setCanChooseFiles: YES]; + [openPanel setCanChooseDirectories: NO]; + + result = [openPanel runModalForTypes: wadTypes]; + + // If the "OK" button was clicked, add the new IWAD file to the list. + + if (result == NSOKButton) + { + filenames = [openPanel filenames]; + [self setLocation: [filenames lastObject]]; + + [self->iwadController saveConfig]; + [self->iwadController setDropdownList]; + } +} + +- (NSString *) getLocation +{ + return [self->locationConfigBox stringValue]; +} + +- (void) setLocation: (NSString *) filename +{ + [self->locationConfigBox setStringValue: filename]; +} + +@end + diff --git a/pkg/osx/Info-gnustep.plist.in b/pkg/osx/Info-gnustep.plist.in new file mode 100644 index 00000000..da6675ba --- /dev/null +++ b/pkg/osx/Info-gnustep.plist.in @@ -0,0 +1,36 @@ +{ + ApplicationName = "@PACKAGE_NAME@"; + ApplicationDescription = "@PACKAGE_SHORTDESC@"; + ApplicationIcon = app.png; + ApplicationRelease = @PACKAGE_VERSION@; + ApplicationURL = "@PACKAGE_URL@"; + Authors = ( + "@PACKAGE_MAINTAINER@ <@PACKAGE_BUGREPORT@>" + ); + Copyright = "@PACKAGE_COPYRIGHT@"; + CopyrightDescription = "@PACKAGE_LICENSE@"; + FullVersionID = @PACKAGE_VERSION@; + GSMainMarkupFile = ""; + NSExecutable = "launcher"; + NSIcon = app.png; + NSMainNibFile = launcher.nib; + NSPrincipalClass = NSApplication; + NSRole = Application; + NSTypes = ( + { + NSHumanReadableName = "Doom WAD file"; + NSUnixExtensions = ( wad ); + NSRole = Viewer; + NSMimeTypes = ( + "application/x-doom" + ); + NSIcon = "wadfile.png"; + }, + { + NSHumanReadableName = "Dehacked patch"; + NSUnixExtensions = ( deh ); + NSRole = Viewer; + NSIcon = "wadfile.png"; + } + ); +} diff --git a/pkg/osx/Info.plist.in b/pkg/osx/Info.plist.in new file mode 100644 index 00000000..8b8436f7 --- /dev/null +++ b/pkg/osx/Info.plist.in @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleDisplayName</key> + <string>@PACKAGE_NAME@</string> + <key>CFBundleExecutable</key> + <string>launcher</string> + <key>CFBundleGetInfoString</key> + <string>@PACKAGE_STRING@</string> + <key>CFBundleIconFile</key> + <string>app.icns</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>@PACKAGE_NAME@</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>@PACKAGE_VERSION@</string> + <key>CFBundleVersion</key> + <string>@PACKAGE_VERSION@</string> + <key>NSPrincipalClass</key> + <string>NSApplication</string> + <key>NSMainNibFile</key> + <string>launcher</string> + + <!-- file associations: --> + + <key>CFBundleDocumentTypes</key> + <array> + <dict> + <key>CFBundleTypeName</key> + <string>Doom WAD file</string> + <key>CFBundleTypeIconFile</key> + <string>wadfile.icns</string> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + <key>CFBundleTypeExtensions</key> + <array> + <string>wad</string> + </array> + </dict> + <dict> + <key>CFBundleTypeName</key> + <string>Dehacked patch</string> + <key>CFBundleTypeIconFile</key> + <string>wadfile.icns</string> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + <key>CFBundleTypeExtensions</key> + <array> + <string>deh</string> + </array> + </dict> + </array> +</dict> +</plist> diff --git a/pkg/osx/LauncherManager.h b/pkg/osx/LauncherManager.h new file mode 100644 index 00000000..e454ab4f --- /dev/null +++ b/pkg/osx/LauncherManager.h @@ -0,0 +1,52 @@ +/* ... */ +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +//----------------------------------------------------------------------------- + +#ifndef LAUNCHER_LAUNCHERMANAGER_H +#define LAUNCHER_LAUNCHERMANAGER_H + +#include <AppKit/AppKit.h> +#include <AppKit/NSNibLoading.h> +#include "IWADController.h" + +@interface LauncherManager : NSObject +{ + IWADController *iwadController; + + id launcherWindow; + id launchButton; + + id commandLineArguments; + id packageLabel; +} + +- (void) launch: (id)sender; +- (void) runSetup: (id)sender; +- (void) awakeFromNib; +- (void) clearCommandLine; +- (void) addFileToCommandLine: (NSString *) fileName + forArgument: (NSString *) args; +- (void) openTerminal: (id) sender; + +@end + +#endif /* #ifndef LAUNCHER_LAUNCHERMANAGER_H */ + diff --git a/pkg/osx/LauncherManager.m b/pkg/osx/LauncherManager.m new file mode 100644 index 00000000..ae91ef4d --- /dev/null +++ b/pkg/osx/LauncherManager.m @@ -0,0 +1,338 @@ +/* ... */ +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +//----------------------------------------------------------------------------- + +#include <AppKit/AppKit.h> +#include "Execute.h" +#include "LauncherManager.h" +#include "config.h" + +@implementation LauncherManager + +// Save configuration. Invoked when we launch the game or quit. + +- (void) saveConfig +{ + NSUserDefaults *defaults; + + // Save IWAD configuration and selected IWAD. + + [self->iwadController saveConfig]; + + // Save command line arguments. + + defaults = [NSUserDefaults standardUserDefaults]; + [defaults setObject:[self->commandLineArguments stringValue] + forKey:@"command_line_args"]; +} + +// Load configuration, invoked on startup. + +- (void) setConfig +{ + NSUserDefaults *defaults; + NSString *args; + + defaults = [NSUserDefaults standardUserDefaults]; + + args = [defaults stringForKey:@"command_line_args"]; + + if (args != nil) + { + [self->commandLineArguments setStringValue:args]; + } +} + +// Get the next command line argument from the command line. +// The position counter used to iterate over arguments is in 'pos'. +// The index of the argument that was found is saved in arg_pos. + +static NSString *GetNextArgument(NSString *commandLine, int *pos, int *arg_pos) +{ + NSRange arg_range; + + // Skip past any whitespace + + while (*pos < [commandLine length] + && isspace([commandLine characterAtIndex: *pos])) + { + ++*pos; + } + + if (*pos >= [commandLine length]) + { + *arg_pos = *pos; + return nil; + } + + // We are at the start of the argument. This may be a quoted + // string argument, or a "normal" one. + + if ([commandLine characterAtIndex: *pos] == '\"') + { + // Quoted string, skip past first quote + + ++*pos; + + // Save start position: + + *arg_pos = *pos; + + while (*pos < [commandLine length] + && [commandLine characterAtIndex: *pos] != '\"') + { + ++*pos; + } + + // Unexpected end of string? + + if (*pos >= [commandLine length]) + { + return nil; + } + + arg_range = NSMakeRange(*arg_pos, *pos - *arg_pos); + + // Skip past last quote + + ++*pos; + } + else + { + // Normal argument + + // Save position: + + *arg_pos = *pos; + + // Read until end: + + while (*pos < [commandLine length] + && !isspace([commandLine characterAtIndex: *pos])) + { + ++*pos; + } + + arg_range = NSMakeRange(*arg_pos, *pos - *arg_pos); + } + + return [commandLine substringWithRange: arg_range]; +} + +// Given the specified command line argument, find the index +// to insert the new file within the command line. Returns -1 if the +// argument is not already within the arguments string. + +static int GetFileInsertIndex(NSString *commandLine, NSString *needle) +{ + NSString *arg; + int arg_pos; + int pos; + + pos = 0; + + // Find the command line parameter we are searching + // for (-merge, -deh, etc) + + for (;;) + { + arg = GetNextArgument(commandLine, &pos, &arg_pos); + + // Searched to end of string and never found? + + if (arg == nil) + { + return -1; + } + + if (![arg caseInsensitiveCompare: needle]) + { + break; + } + } + + // Now skip over existing files. For example, if we + // have -file foo.wad bar.wad, the new file should be appended + // to the end of the list. + + for (;;) + { + arg = GetNextArgument(commandLine, &pos, &arg_pos); + + // If we search to the end of the string now, it is fine; + // the new string should be added to the end of the command + // line. Otherwise, if we find an argument that begins + // with '-', it is a new command line parameter and the end + // of the list. + + if (arg == nil || [arg characterAtIndex: 0] == '-') + { + break; + } + } + + // arg_pos should now contain the offset to insert the new filename. + + return arg_pos; +} + +// Given the specified string, append a filename, quoted if necessary. + +static NSString *AppendQuotedFilename(NSString *str, NSString *fileName) +{ + int i; + + // Search the filename for spaces, and quote if necessary. + + for (i=0; i<[fileName length]; ++i) + { + if (isspace([fileName characterAtIndex: i])) + { + str = [str stringByAppendingString: @" \""]; + str = [str stringByAppendingString: fileName]; + str = [str stringByAppendingString: @"\" "]; + + return str; + } + } + + str = [str stringByAppendingString: @" "]; + str = [str stringByAppendingString: fileName]; + + return str; +} + +// Clear out the existing command line options. +// Invoked before the first file is added. + +- (void) clearCommandLine +{ + [self->commandLineArguments setStringValue: @""]; +} + +// Add a file to the command line to load with the game. + +- (void) addFileToCommandLine: (NSString *) fileName + forArgument: (NSString *) arg +{ + NSString *commandLine; + int insert_pos; + + // Get the current command line + + commandLine = [self->commandLineArguments stringValue]; + + // Find the location to insert the new filename: + + insert_pos = GetFileInsertIndex(commandLine, arg); + + // If position < 0, we should add the new argument and filename + // to the end. Otherwise, append the new filename to the existing + // list of files. + + if (insert_pos < 0) + { + commandLine = [commandLine stringByAppendingString: @" "]; + commandLine = [commandLine stringByAppendingString: arg]; + commandLine = AppendQuotedFilename(commandLine, fileName); + } + else + { + NSString *start; + NSString *end; + + // Divide existing command line in half: + + start = [commandLine substringToIndex: insert_pos]; + end = [commandLine substringFromIndex: insert_pos]; + + // Construct new command line: + + commandLine = AppendQuotedFilename(start, fileName); + commandLine = [commandLine stringByAppendingString: @" "]; + commandLine = [commandLine stringByAppendingString: end]; + } + + [self->commandLineArguments setStringValue: commandLine]; +} + +- (void) launch: (id)sender +{ + NSString *iwad; + NSString *args; + + [self saveConfig]; + + iwad = [self->iwadController getIWADLocation]; + args = [self->commandLineArguments stringValue]; + + if (iwad == nil) + { + NSRunAlertPanel(@"No IWAD selected", + @"You have not selected an IWAD (game) file.\n\n" + "You must configure and select a valid IWAD file " + "in order to launch the game.", + @"OK", nil, nil); + return; + } + + ExecuteProgram(PACKAGE_TARNAME, [iwad UTF8String], + [args UTF8String]); + [NSApp terminate:sender]; +} + +// Invoked when the "Setup Tool" button is clicked, to run the setup tool: + +- (void) runSetup: (id)sender +{ + [self saveConfig]; + + [self->iwadController setEnvironment]; + ExecuteProgram("chocolate-setup", NULL, NULL); +} + +// Invoked when the "Terminal" option is selected from the menu, to open +// a terminal window. + +- (void) openTerminal: (id) sender +{ + char *doomwadpath; + + [self saveConfig]; + + doomwadpath = [self->iwadController doomWadPath]; + + OpenTerminalWindow(doomwadpath); + + free(doomwadpath); +} + +- (void) awakeFromNib +{ + [self->packageLabel setStringValue: @PACKAGE_STRING]; + [self->launcherWindow setTitle: @PACKAGE_NAME " Launcher"]; + [self->launcherWindow center]; + [self->launcherWindow setDefaultButtonCell: [self->launchButton cell]]; + [self setConfig]; +} + +@end + diff --git a/pkg/osx/PkgInfo b/pkg/osx/PkgInfo new file mode 100644 index 00000000..6f749b0f --- /dev/null +++ b/pkg/osx/PkgInfo @@ -0,0 +1 @@ +APPL???? diff --git a/pkg/osx/Resources/128x128.png b/pkg/osx/Resources/128x128.png Binary files differnew file mode 100644 index 00000000..0ef1fe9c --- /dev/null +++ b/pkg/osx/Resources/128x128.png diff --git a/pkg/osx/Resources/app.icns b/pkg/osx/Resources/app.icns Binary files differnew file mode 100644 index 00000000..9b535a25 --- /dev/null +++ b/pkg/osx/Resources/app.icns diff --git a/pkg/osx/Resources/app.png b/pkg/osx/Resources/app.png Binary files differnew file mode 100644 index 00000000..5b932e8c --- /dev/null +++ b/pkg/osx/Resources/app.png diff --git a/pkg/osx/Resources/launcher.nib/classes.nib b/pkg/osx/Resources/launcher.nib/classes.nib new file mode 100644 index 00000000..7efc837a --- /dev/null +++ b/pkg/osx/Resources/launcher.nib/classes.nib @@ -0,0 +1,47 @@ +{ + IBClasses = ( + { + CLASS = AppController; + LANGUAGE = ObjC; + OUTLETS = {launcherManager = id; }; + SUPERCLASS = NSObject; + }, + {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, + { + ACTIONS = {closeConfigWindow = id; openConfigWindow = id; }; + CLASS = IWADController; + LANGUAGE = ObjC; + OUTLETS = { + chex = id; + configWindow = id; + doom1 = id; + doom2 = id; + iwadSelector = id; + plutonia = id; + tnt = id; + }; + SUPERCLASS = NSObject; + }, + { + ACTIONS = {setButtonClicked = id; }; + CLASS = IWADLocation; + LANGUAGE = ObjC; + OUTLETS = {locationConfigBox = id; }; + SUPERCLASS = NSObject; + }, + { + ACTIONS = {launch = id; openTerminal = id; runSetup = id; }; + CLASS = LauncherManager; + LANGUAGE = ObjC; + OUTLETS = { + commandLineArguments = id; + iwadController = id; + launchButton = id; + launcherWindow = id; + packageLabel = id; + }; + SUPERCLASS = NSObject; + } + ); + IBVersion = 1; +}
\ No newline at end of file diff --git a/pkg/osx/Resources/launcher.nib/info.nib b/pkg/osx/Resources/launcher.nib/info.nib new file mode 100644 index 00000000..512ee6dd --- /dev/null +++ b/pkg/osx/Resources/launcher.nib/info.nib @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>IBDocumentLocation</key> + <string>325 73 612 260 0 0 1440 878 </string> + <key>IBEditorPositions</key> + <dict> + <key>29</key> + <string>221 322 205 44 0 0 1440 878 </string> + </dict> + <key>IBFramework Version</key> + <string>446.1</string> + <key>IBOpenObjects</key> + <array> + <integer>29</integer> + <integer>21</integer> + <integer>227</integer> + </array> + <key>IBSystem Version</key> + <string>8S2167</string> +</dict> +</plist> diff --git a/pkg/osx/Resources/launcher.nib/keyedobjects.nib b/pkg/osx/Resources/launcher.nib/keyedobjects.nib Binary files differnew file mode 100644 index 00000000..cc763056 --- /dev/null +++ b/pkg/osx/Resources/launcher.nib/keyedobjects.nib diff --git a/pkg/osx/Resources/wadfile.icns b/pkg/osx/Resources/wadfile.icns Binary files differnew file mode 100644 index 00000000..13502a55 --- /dev/null +++ b/pkg/osx/Resources/wadfile.icns diff --git a/pkg/osx/Resources/wadfile.png b/pkg/osx/Resources/wadfile.png Binary files differnew file mode 100644 index 00000000..394a3e33 --- /dev/null +++ b/pkg/osx/Resources/wadfile.png diff --git a/pkg/osx/cp-with-libs b/pkg/osx/cp-with-libs new file mode 100755 index 00000000..04fb5bc6 --- /dev/null +++ b/pkg/osx/cp-with-libs @@ -0,0 +1,100 @@ +#!/bin/bash +# +# Copy a program to the specified destination, along +# with libraries it depends upon. + +src_bin=$1 +dest_dir=$2 + +# Returns true if the specified file is a dylib. + +is_dylib() { + case "$1" in + *.dylib) + true + ;; + *) + false + ;; + esac +} + +# Returns true if the specified file is in a system location +# (/System or /usr): + +is_sys_lib() { + case "$1" in + /System/*) + true + ;; + /usr/*) + true + ;; + *) + false + ;; + esac +} + +# Install the specified file to the location in dest_dir, along with +# any libraries it depends upon (recursively): + +install_with_deps() { + local src_file + local bin_name + local dest_file + local lib_name + + src_file=$1 + bin_name=$(basename "$src_file") + dest_file="$dest_dir/$bin_name" + + # Already copied into the package? Don't copy again. + # Prevents endless recursion. + + if [ -e "$dest_file" ]; then + return + fi + + echo "Installing $bin_name..." + + # Copy file into package. + + cp "$src_file" "$dest_file" + + # Copy libraries that this file depends on: + + otool -L "$src_file" | tail -n +2 | sed 's/^.//; s/ (.*//' | while read; do + + # Don't copy system libraries + + if is_sys_lib "$REPLY"; then + continue + fi + + #echo " - $bin_name depends on $REPLY" + + # Copy this library into the package, and: + # recursively install any libraries that _this_ library depends on: + + install_with_deps "$REPLY" + + # Change destination binary to depend on the + # copy inside the package: + + lib_name=$(basename "$REPLY") + install_name_tool -change "$REPLY" "@executable_path/$lib_name" \ + "$dest_file" + done + + # If this is a library that we have installed, change its id: + + if is_dylib "$dest_file"; then + install_name_tool -id "@executable_path/$bin_name" "$dest_file" + fi +} + +# Install the file, and recursively install any libraries: + +install_with_deps "$src_bin" + diff --git a/pkg/osx/main.m b/pkg/osx/main.m new file mode 100644 index 00000000..e3c70dc1 --- /dev/null +++ b/pkg/osx/main.m @@ -0,0 +1,32 @@ +/* ... */ +//----------------------------------------------------------------------------- +// +// Copyright(C) 2009 Simon Howard +// +// 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., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +//----------------------------------------------------------------------------- + +#include <AppKit/AppKit.h> +#include "Execute.h" + +int main(int argc, const char *argv[]) +{ + SetProgramLocation(argv[0]); + + return NSApplicationMain (argc, argv); +} + diff --git a/pkg/win32/GNUmakefile b/pkg/win32/GNUmakefile new file mode 100644 index 00000000..147e2890 --- /dev/null +++ b/pkg/win32/GNUmakefile @@ -0,0 +1,34 @@ + +include ../config.make + +TOPLEVEL=../.. + +EXE_FILES=$(TOPLEVEL)/src/$(PROGRAM_PREFIX)doom.exe \ + $(TOPLEVEL)/src/$(PROGRAM_PREFIX)heretic.exe \ + $(TOPLEVEL)/src/$(PROGRAM_PREFIX)hexen.exe \ + $(TOPLEVEL)/src/$(PROGRAM_PREFIX)server.exe \ + $(TOPLEVEL)/src/$(PROGRAM_PREFIX)setup.exe + +DLL_FILES=$(TOPLEVEL)/src/SDL.dll \ + $(TOPLEVEL)/src/SDL_mixer.dll \ + $(TOPLEVEL)/src/SDL_net.dll + +ZIP=$(PACKAGE_TARNAME)-$(PACKAGE_VERSION)-win32.zip + +$(ZIP) : staging + zip -j -r $@ staging/ + +staging: $(EXE_FILES) $(DLL_FILES) $(patsubst %,../../%,$(DOC_FILES)) + rm -rf staging + mkdir staging + cp $(EXE_FILES) $(DLL_FILES) staging/ + $(STRIP) staging/*.exe + for f in $(DOC_FILES); do \ + cp $(TOPLEVEL)/$$f staging/$$f.txt; \ + unix2dos staging/$$f.txt; \ + done + +clean: + rm -f $(ZIP) + rm -rf staging + diff --git a/pkg/win32/GNUmakefile.am b/pkg/win32/GNUmakefile.am deleted file mode 100644 index 47d5ac63..00000000 --- a/pkg/win32/GNUmakefile.am +++ /dev/null @@ -1,37 +0,0 @@ - -TOPLEVEL=../.. - -EXE_FILES=$(TOPLEVEL)/src/@PROGRAM_PREFIX@doom.exe \ - $(TOPLEVEL)/src/@PROGRAM_PREFIX@heretic.exe \ - $(TOPLEVEL)/src/@PROGRAM_PREFIX@hexen.exe \ - $(TOPLEVEL)/src/@PROGRAM_PREFIX@server.exe \ - $(TOPLEVEL)/src/@PROGRAM_PREFIX@setup.exe - -DLL_FILES=$(TOPLEVEL)/src/SDL.dll \ - $(TOPLEVEL)/src/SDL_mixer.dll \ - $(TOPLEVEL)/src/SDL_net.dll - -DOC_FILES=README \ - COPYING \ - ChangeLog \ - NEWS \ - BUGS \ - CMDLINE \ - HH-TODO \ - TODO - -noinst_DATA=@PACKAGE_TARNAME@-@PACKAGE_VERSION@-win32.zip - -@PACKAGE_TARNAME@-@PACKAGE_VERSION@-win32.zip : staging - zip -j -r $@ staging/ - -staging: $(EXE_FILES) $(DLL_FILES) $(patsubst %,../../%,$(DOC_FILES)) - rm -rf staging - mkdir staging - cp $(EXE_FILES) $(DLL_FILES) staging/ - $(STRIP) staging/*.exe - for f in $(DOC_FILES); do \ - cp $(TOPLEVEL)/$$f staging/$$f.txt; \ - unix2dos staging/$$f.txt; \ - done - diff --git a/pkg/wince/GNUmakefile.am b/pkg/wince/GNUmakefile index a203e63c..82b1f1e3 100644 --- a/pkg/wince/GNUmakefile.am +++ b/pkg/wince/GNUmakefile @@ -1,7 +1,14 @@ +include ../config.make + +all: $(DOOM_CAB) $(HERETIC_CAB) $(HEXEN_CAB) + +clean: + rm -f $(DOOM_CAB) $(HERETIC_CAB) $(HEXEN_CAB) + # Doom: -DOOM_CAB=chocolate-doom-@PACKAGE_VERSION@.cab +DOOM_CAB=$(PROGRAM_PREFIX)doom-$(PACKAGE_VERSION).cab DOOM_CFG=doom-cab.cfg DOOM_DEPS=$(shell ./wince-cabgen -d $(DOOM_CFG)) @@ -10,7 +17,7 @@ $(DOOM_CAB) : $(DOOM_CFG) $(DOOM_DEPS) # Heretic: -HERETIC_CAB=chocolate-heretic-@PACKAGE_VERSION@.cab +HERETIC_CAB=$(PROGRAM_PREFIX)heretic-$(PACKAGE_VERSION).cab HERETIC_CFG=heretic-cab.cfg HERETIC_DEPS=$(shell ./wince-cabgen -d $(HERETIC_CFG)) @@ -19,12 +26,10 @@ $(HERETIC_CAB) : $(HERETIC_CFG) $(HERETIC_DEPS) # Hexen: -HEXEN_CAB=chocolate-hexen-@PACKAGE_VERSION@.cab +HEXEN_CAB=$(PROGRAM_PREFIX)hexen-$(PACKAGE_VERSION).cab HEXEN_CFG=hexen-cab.cfg HEXEN_DEPS=$(shell ./wince-cabgen -d $(HEXEN_CFG)) $(HEXEN_CAB) : $(HEXEN_CFG) $(HEXEN_DEPS) ./wince-cabgen $< $@ -noinst_DATA = $(DOOM_CAB) $(HERETIC_CAB) $(HEXEN_CAB) - diff --git a/rpm.spec.in b/rpm.spec.in new file mode 100644 index 00000000..1b7e90c7 --- /dev/null +++ b/rpm.spec.in @@ -0,0 +1,59 @@ + +Name: @PACKAGE@ +Summary: @PACKAGE_SHORTDESC@ +Version: @VERSION@ +Release: 1 +Source: http://mesh.dl.sourceforge.net/project/chocolate-doom/@PACKAGE@/@VERSION@/@PACKAGE@-@VERSION@.tar.gz +URL: @PACKAGE_URL@ +Group: Amusements/Games +BuildRoot: /var/tmp/@PACKAGE@-buildroot +License: @PACKAGE_LICENSE@ +Packager: @PACKAGE_MAINTAINER@ <@PACKAGE_BUGREPORT@> +Prefix: %{_prefix} +Autoreq: 0 +Requires: libSDL-1.2.so.0, libSDL_mixer-1.2.so.0, libSDL_net-1.2.so.0 + +%description +%(sed -n "/==/ q; p " < README) + +See @PACKAGE_URL@ for more information. + +%prep +rm -rf $RPM_BUILD_ROOT + +%setup -q + +%build +./configure \ + --prefix=/usr \ + --exec-prefix=/usr \ + --bindir=/usr/bin \ + --sbindir=/usr/sbin \ + --sysconfdir=/etc \ + --datadir=/usr/share \ + --includedir=/usr/include \ + --libdir=/usr/lib \ + --libexecdir=/usr/lib \ + --localstatedir=/var/lib \ + --sharedstatedir=/usr/com \ + --mandir=/usr/share/man \ + --infodir=/usr/share/info +make + +%install +%makeinstall + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%doc %{_mandir}/man5/* +%doc %{_mandir}/man6/* +%doc NEWS +%doc AUTHORS +%doc README +%doc COPYING +%doc CMDLINE +%doc BUGS +/usr/games/* + diff --git a/src/.gitignore b/src/.gitignore index d7e732ad..973a0073 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -5,3 +5,5 @@ Makefile.in chocolate-doom chocolate-server *.exe +tags +TAGS diff --git a/src/Makefile.am b/src/Makefile.am index fc7e8852..9e550e15 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -190,7 +190,7 @@ EXTRA_DIST = \ if HAVE_PYTHON -icon.c : $(top_builddir)/data/doom.ico +icon.c : $(top_builddir)/data/doom8.ico $(top_builddir)/data/convert-icon $^ $@ endif diff --git a/src/d_iwad.c b/src/d_iwad.c index ba9f9ae1..ab1dd823 100644 --- a/src/d_iwad.c +++ b/src/d_iwad.c @@ -330,44 +330,85 @@ static void CheckDOSDefaults(void) #endif +// Returns true if the specified path is a path to a file +// of the specified name. + +static boolean DirIsFile(char *path, char *filename) +{ + size_t path_len; + size_t filename_len; + + path_len = strlen(path); + filename_len = strlen(filename); + + return path_len >= filename_len + 1 + && path[path_len - filename_len - 1] == DIR_SEPARATOR + && !strcasecmp(&path[path_len - filename_len], filename); +} + +// Check if the specified directory contains the specified IWAD +// file, returning the full path to the IWAD if found, or NULL +// if not found. + +static char *CheckDirectoryHasIWAD(char *dir, char *iwadname) +{ + char *filename; + + // As a special case, the "directory" may refer directly to an + // IWAD file if the path comes from DOOMWADDIR or DOOMWADPATH. + + if (DirIsFile(dir, iwadname) && M_FileExists(dir)) + { + return strdup(dir); + } + + // Construct the full path to the IWAD if it is located in + // this directory, and check if it exists. + + filename = malloc(strlen(dir) + strlen(iwadname) + 3); + + if (!strcmp(dir, ".")) + { + strcpy(filename, iwadname); + } + else + { + sprintf(filename, "%s%c%s", dir, DIR_SEPARATOR, iwadname); + } + + if (M_FileExists(filename)) + { + return filename; + } + + free(filename); + + return NULL; +} + // Search a directory to try to find an IWAD // Returns the location of the IWAD if found, otherwise NULL. static char *SearchDirectoryForIWAD(char *dir, int mask, GameMission_t *mission) { + char *filename; size_t i; for (i=0; i<arrlen(iwads); ++i) { - char *filename; - char *iwadname; - if (((1 << iwads[i].mission) & mask) == 0) { continue; } - iwadname = DEH_String(iwads[i].name); - - filename = malloc(strlen(dir) + strlen(iwadname) + 3); - - if (!strcmp(dir, ".")) - { - strcpy(filename, iwadname); - } - else - { - sprintf(filename, "%s%c%s", dir, DIR_SEPARATOR, iwadname); - } + filename = CheckDirectoryHasIWAD(dir, DEH_String(iwads[i].name)); - if (M_FileExists(filename)) + if (filename != NULL) { *mission = iwads[i].mission; return filename; } - - free(filename); } return NULL; @@ -528,7 +569,6 @@ char *D_FindWADByName(char *name) { char *buf; int i; - boolean exists; // Absolute path? @@ -543,14 +583,21 @@ char *D_FindWADByName(char *name) for (i=0; i<num_iwad_dirs; ++i) { + // As a special case, if this is in DOOMWADDIR or DOOMWADPATH, + // the "directory" may actually refer directly to an IWAD + // file. + + if (DirIsFile(iwad_dirs[i], name) && M_FileExists(iwad_dirs[i])) + { + return strdup(iwad_dirs[i]); + } + // Construct a string for the full path buf = malloc(strlen(iwad_dirs[i]) + strlen(name) + 5); sprintf(buf, "%s%c%s", iwad_dirs[i], DIR_SEPARATOR, name); - exists = M_FileExists(buf); - - if (exists) + if (M_FileExists(buf)) { return buf; } diff --git a/src/doom/.gitignore b/src/doom/.gitignore index d7e732ad..973a0073 100644 --- a/src/doom/.gitignore +++ b/src/doom/.gitignore @@ -5,3 +5,5 @@ Makefile.in chocolate-doom chocolate-server *.exe +tags +TAGS diff --git a/src/doom/g_game.c b/src/doom/g_game.c index 35bf4d74..8d0e4503 100644 --- a/src/doom/g_game.c +++ b/src/doom/g_game.c @@ -588,10 +588,20 @@ void G_BuildTiccmd (ticcmd_t* cmd) if (lowres_turn) { - // round angleturn to the nearest 256 boundary + static signed short carry = 0; + signed short desired_angleturn; + + desired_angleturn = cmd->angleturn + carry; + + // round angleturn to the nearest 256 unit boundary // for recording demos with single byte values for turn - cmd->angleturn = (cmd->angleturn + 128) & 0xff00; + cmd->angleturn = (desired_angleturn + 128) & 0xff00; + + // Carry forward the error from the reduced resolution to the + // next tic, so that successive small movements can accumulate. + + carry = desired_angleturn - cmd->angleturn; } } diff --git a/src/doom/hu_stuff.c b/src/doom/hu_stuff.c index a106978e..ca74ce92 100644 --- a/src/doom/hu_stuff.c +++ b/src/doom/hu_stuff.c @@ -52,9 +52,9 @@ // Locally used constants, shortcuts. // #define HU_TITLE (mapnames[(gameepisode-1)*9+gamemap-1]) -#define HU_TITLE2 (mapnames2[gamemap-1]) -#define HU_TITLEP (mapnamesp[gamemap-1]) -#define HU_TITLET (mapnamest[gamemap-1]) +#define HU_TITLE2 (mapnames_commercial[gamemap-1]) +#define HU_TITLEP (mapnames_commercial[gamemap-1 + 32]) +#define HU_TITLET (mapnames_commercial[gamemap-1 + 64]) #define HU_TITLE_CHEX (mapnames[gamemap - 1]) #define HU_TITLEHEIGHT 1 #define HU_TITLEX 0 @@ -171,8 +171,16 @@ char* mapnames[] = // DOOM shareware/registered/retail (Ultimate) names. "NEWLEVEL" }; -char* mapnames2[] = // DOOM 2 map names. +// List of names for levels in commercial IWADs +// (doom2.wad, plutonia.wad, tnt.wad). These are stored in a +// single large array; WADs like pl2.wad have a MAP33, and rely on +// the layout in the Vanilla executable, where it is possible to +// overflow the end of one array into the next. + +char *mapnames_commercial[] = { + // DOOM 2 map names. + HUSTR_1, HUSTR_2, HUSTR_3, @@ -206,12 +214,10 @@ char* mapnames2[] = // DOOM 2 map names. HUSTR_29, HUSTR_30, HUSTR_31, - HUSTR_32 -}; + HUSTR_32, + // Plutonia WAD map names. -char* mapnamesp[] = // Plutonia WAD map names. -{ PHUSTR_1, PHUSTR_2, PHUSTR_3, @@ -245,12 +251,10 @@ char* mapnamesp[] = // Plutonia WAD map names. PHUSTR_29, PHUSTR_30, PHUSTR_31, - PHUSTR_32 -}; - + PHUSTR_32, + + // TNT WAD map names. -char *mapnamest[] = // TNT WAD map names. -{ THUSTR_1, THUSTR_2, THUSTR_3, diff --git a/src/doom/p_map.c b/src/doom/p_map.c index 3f88aabc..925e4398 100644 --- a/src/doom/p_map.c +++ b/src/doom/p_map.c @@ -885,7 +885,17 @@ PTR_AimTraverse (intercept_t* in) dist = FixedMul (attackrange, in->frac); - if (li->frontsector->floorheight != li->backsector->floorheight) + // Return false if there is no back sector. This should never + // be the case if the line is two-sided; however, some WADs + // (eg. ottawau.wad) use this as an "impassible glass" trick + // and rely on Vanilla Doom's (unintentional) support for this. + + if (li->backsector == NULL) + { + return false; + } + + if (li->frontsector->floorheight != li->backsector->floorheight) { slope = FixedDiv (openbottom - shootz , dist); if (slope > bottomslope) @@ -973,7 +983,14 @@ boolean PTR_ShootTraverse (intercept_t* in) dist = FixedMul (attackrange, in->frac); - if (li->frontsector->floorheight != li->backsector->floorheight) + // Check if backsector is NULL. See comment in PTR_AimTraverse. + + if (li->backsector == NULL) + { + goto hitline; + } + + if (li->frontsector->floorheight != li->backsector->floorheight) { slope = FixedDiv (openbottom - shootz , dist); if (slope > aimslope) diff --git a/src/doom/p_setup.c b/src/doom/p_setup.c index 5cf7a628..2a3a8f85 100644 --- a/src/doom/p_setup.c +++ b/src/doom/p_setup.c @@ -167,6 +167,7 @@ void P_LoadSegs (int lump) line_t* ldef; int linedef; int side; + int sidenum; numsegs = W_LumpLength (lump) / sizeof(mapseg_t); segs = Z_Malloc (numsegs*sizeof(seg_t),PU_LEVEL,0); @@ -179,7 +180,7 @@ void P_LoadSegs (int lump) { li->v1 = &vertexes[SHORT(ml->v1)]; li->v2 = &vertexes[SHORT(ml->v2)]; - + li->angle = (SHORT(ml->angle))<<16; li->offset = (SHORT(ml->offset))<<16; linedef = SHORT(ml->linedef); @@ -188,10 +189,28 @@ void P_LoadSegs (int lump) side = SHORT(ml->side); li->sidedef = &sides[ldef->sidenum[side]]; li->frontsector = sides[ldef->sidenum[side]].sector; - if (ldef-> flags & ML_TWOSIDED) - li->backsector = sides[ldef->sidenum[side^1]].sector; - else + + if (ldef-> flags & ML_TWOSIDED) + { + sidenum = ldef->sidenum[side ^ 1]; + + // If the sidenum is out of range, this may be a "glass hack" + // impassible window. Point at side #0 (this may not be + // the correct Vanilla behavior; however, it seems to work for + // OTTAWAU.WAD, which is the one place I've seen this trick + // used). + + if (sidenum < 0 || sidenum >= numsides) + { + sidenum = 0; + } + + li->backsector = sides[sidenum].sector; + } + else + { li->backsector = 0; + } } W_ReleaseLumpNum(lump); diff --git a/src/doom/p_sight.c b/src/doom/p_sight.c index e192567b..79c1bb1d 100644 --- a/src/doom/p_sight.c +++ b/src/doom/p_sight.c @@ -173,7 +173,7 @@ boolean P_CrossSubsector (int num) continue; line->validcount = validcount; - + v1 = line->v1; v2 = line->v2; s1 = P_DivlineSide (v1->x,v1->y, &strace); @@ -194,6 +194,14 @@ boolean P_CrossSubsector (int num) if (s1 == s2) continue; + // Backsector may be NULL if this is an "impassible + // glass" hack line. + + if (line->backsector == NULL) + { + return false; + } + // stop because it is not two sided anyway // might do this after updating validcount? if ( !(line->flags & ML_TWOSIDED) ) diff --git a/src/doom/st_stuff.c b/src/doom/st_stuff.c index e6ef0828..160244ce 100644 --- a/src/doom/st_stuff.c +++ b/src/doom/st_stuff.c @@ -260,9 +260,6 @@ // Height, in lines. #define ST_OUTHEIGHT 1 -#define ST_MAPWIDTH \ - (strlen(mapnames[(gameepisode-1)*9+(gamemap-1)])) - #define ST_MAPTITLEX \ (SCREENWIDTH - ST_MAPWIDTH * ST_CHATFONTWIDTH) @@ -418,10 +415,6 @@ cheatseq_t cheat_clev = CHEAT("idclev", 2); cheatseq_t cheat_mypos = CHEAT("idmypos", 0); -// -extern char* mapnames[]; - - // // STATUS BAR CODE // @@ -985,6 +978,17 @@ void ST_doPaletteStuff(void) else palette = 0; + // In Chex Quest, the player never sees red. Instead, the + // radiation suit palette is used to tint the screen green, + // as though the player is being covered in goo by an + // attacking flemoid. + + if (gameversion == exe_chex + && palette >= STARTREDPALS && palette < STARTREDPALS + NUMREDPALS) + { + palette = RADIATIONPAL; + } + if (palette != st_palette) { st_palette = palette; diff --git a/src/i_video.c b/src/i_video.c index 64a2e8eb..a370fc08 100644 --- a/src/i_video.c +++ b/src/i_video.c @@ -503,6 +503,7 @@ void I_ShutdownGraphics(void) { if (initialized) { + SDL_SetCursor(cursors[1]); SDL_ShowCursor(1); SDL_WM_GrabInput(SDL_GRAB_OFF); diff --git a/src/mus2mid.c b/src/mus2mid.c index 8ba1a5ff..88d24c97 100644 --- a/src/mus2mid.c +++ b/src/mus2mid.c @@ -1,4 +1,4 @@ -// Emacs style mode select -*- C++ -*- +// Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // Copyright(C) 1993-1996 Id Software, Inc. @@ -31,594 +31,663 @@ #include "memio.h" #include "mus2mid.h" +#define NUM_CHANNELS 16 + +#define MIDI_PERCUSSION_CHAN 9 +#define MUS_PERCUSSION_CHAN 15 + // MUS event codes -typedef enum +typedef enum { - mus_releasekey = 0x00, - mus_presskey = 0x10, - mus_pitchwheel = 0x20, - mus_systemevent = 0x30, - mus_changecontroller = 0x40, - mus_scoreend = 0x60 + mus_releasekey = 0x00, + mus_presskey = 0x10, + mus_pitchwheel = 0x20, + mus_systemevent = 0x30, + mus_changecontroller = 0x40, + mus_scoreend = 0x60 } musevent; // MIDI event codes -typedef enum +typedef enum { - midi_releasekey = 0x80, - midi_presskey = 0x90, - midi_aftertouchkey = 0xA0, - midi_changecontroller = 0xB0, - midi_changepatch = 0xC0, - midi_aftertouchchannel = 0xD0, - midi_pitchwheel = 0xE0 + midi_releasekey = 0x80, + midi_presskey = 0x90, + midi_aftertouchkey = 0xA0, + midi_changecontroller = 0xB0, + midi_changepatch = 0xC0, + midi_aftertouchchannel = 0xD0, + midi_pitchwheel = 0xE0 } midievent; - // Structure to hold MUS file header -typedef struct +typedef struct { - byte id[4]; - unsigned short scorelength; - unsigned short scorestart; - unsigned short primarychannels; - unsigned short secondarychannels; - unsigned short instrumentcount; + byte id[4]; + unsigned short scorelength; + unsigned short scorestart; + unsigned short primarychannels; + unsigned short secondarychannels; + unsigned short instrumentcount; } PACKEDATTR musheader; // Standard MIDI type 0 header + track header -static byte midiheader[] = +static const byte midiheader[] = { - 'M', 'T', 'h', 'd', // Main header - 0x00, 0x00, 0x00, 0x06, // Header size - 0x00, 0x00, // MIDI type (0) - 0x00, 0x01, // Number of tracks - 0x00, 0x46, // Resolution - 'M', 'T', 'r', 'k', // Start of track - 0x00, 0x00, 0x00, 0x00 // Placeholder for track length + 'M', 'T', 'h', 'd', // Main header + 0x00, 0x00, 0x00, 0x06, // Header size + 0x00, 0x00, // MIDI type (0) + 0x00, 0x01, // Number of tracks + 0x00, 0x46, // Resolution + 'M', 'T', 'r', 'k', // Start of track + 0x00, 0x00, 0x00, 0x00 // Placeholder for track length }; // Cached channel velocities -static byte channelvelocities[] = -{ - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127 +static byte channelvelocities[] = +{ + 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127 }; // Timestamps between sequences of MUS events -static unsigned int queuedtime = 0; +static unsigned int queuedtime = 0; // Counter for the length of the track static unsigned int tracksize; -static byte mus2midi_translation[] = -{ - 0x00, 0x20, 0x01, 0x07, 0x0A, 0x0B, 0x5B, 0x5D, - 0x40, 0x43, 0x78, 0x7B, 0x7E, 0x7F, 0x79 +static const byte controller_map[] = +{ + 0x00, 0x20, 0x01, 0x07, 0x0A, 0x0B, 0x5B, 0x5D, + 0x40, 0x43, 0x78, 0x7B, 0x7E, 0x7F, 0x79 }; +static int channel_map[NUM_CHANNELS]; + // Write timestamp to a MIDI file. -static boolean midi_writetime(unsigned int time, MEMFILE *midioutput) +static boolean WriteTime(unsigned int time, MEMFILE *midioutput) { - unsigned int buffer = time & 0x7F; - byte writeval; - - while ((time >>= 7) != 0) - { - buffer <<= 8; - buffer |= ((time & 0x7F) | 0x80); - } - - for (;;) - { - writeval = (byte)(buffer & 0xFF); - - if (mem_fwrite(&writeval, 1, 1, midioutput) != 1) - { - return true; - } - - ++tracksize; - - if ((buffer & 0x80) != 0) - { - buffer >>= 8; - } - else - { - queuedtime = 0; - return false; - } - } + unsigned int buffer = time & 0x7F; + byte writeval; + + while ((time >>= 7) != 0) + { + buffer <<= 8; + buffer |= ((time & 0x7F) | 0x80); + } + + for (;;) + { + writeval = (byte)(buffer & 0xFF); + + if (mem_fwrite(&writeval, 1, 1, midioutput) != 1) + { + return true; + } + + ++tracksize; + + if ((buffer & 0x80) != 0) + { + buffer >>= 8; + } + else + { + queuedtime = 0; + return false; + } + } } // Write the end of track marker -static boolean midi_writeendtrack(MEMFILE *midioutput) +static boolean WriteEndTrack(MEMFILE *midioutput) { - byte endtrack[] = {0xFF, 0x2F, 0x00}; + byte endtrack[] = {0xFF, 0x2F, 0x00}; - if (midi_writetime(queuedtime, midioutput)) - { - return true; - } + if (WriteTime(queuedtime, midioutput)) + { + return true; + } - if (mem_fwrite(endtrack, 1, 3, midioutput) != 3) - { - return true; - } + if (mem_fwrite(endtrack, 1, 3, midioutput) != 3) + { + return true; + } - tracksize += 3; - return false; + tracksize += 3; + return false; } // Write a key press event -static boolean midi_writepresskey(byte channel, byte key, - byte velocity, MEMFILE *midioutput) +static boolean WritePressKey(byte channel, byte key, + byte velocity, MEMFILE *midioutput) { - byte working = midi_presskey | channel; - - if (midi_writetime(queuedtime, midioutput)) - { - return true; - } - - if (mem_fwrite(&working, 1, 1, midioutput) != 1) - { - return true; - } - - working = key & 0x7F; - - if (mem_fwrite(&working, 1, 1, midioutput) != 1) - { - return true; - } - - working = velocity & 0x7F; - - if (mem_fwrite(&working, 1, 1, midioutput) != 1) - { - return true; - } - - tracksize += 3; - - return false; + byte working = midi_presskey | channel; + + if (WriteTime(queuedtime, midioutput)) + { + return true; + } + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + working = key & 0x7F; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + working = velocity & 0x7F; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + tracksize += 3; + + return false; } // Write a key release event -static boolean midi_writereleasekey(byte channel, byte key, - MEMFILE *midioutput) +static boolean WriteReleaseKey(byte channel, byte key, + MEMFILE *midioutput) { - byte working = midi_releasekey | channel; + byte working = midi_releasekey | channel; - if (midi_writetime(queuedtime, midioutput)) - { - return true; - } + if (WriteTime(queuedtime, midioutput)) + { + return true; + } - if (mem_fwrite(&working, 1, 1, midioutput) != 1) - { - return true; - } + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } - working = key & 0x7F; + working = key & 0x7F; - if (mem_fwrite(&working, 1, 1, midioutput) != 1) - { - return true; - } + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } - working = 0; + working = 0; - if (mem_fwrite(&working, 1, 1, midioutput) != 1) - { - return true; - } + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } - tracksize += 3; + tracksize += 3; - return false; + return false; } // Write a pitch wheel/bend event -static boolean midi_writepitchwheel(byte channel, short wheel, - MEMFILE *midioutput) +static boolean WritePitchWheel(byte channel, short wheel, + MEMFILE *midioutput) { - byte working = midi_pitchwheel | channel; + byte working = midi_pitchwheel | channel; - if (midi_writetime(queuedtime, midioutput)) - { - return true; - } + if (WriteTime(queuedtime, midioutput)) + { + return true; + } - if (mem_fwrite(&working, 1, 1, midioutput) != 1) - { - return true; - } + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } - working = wheel & 0x7F; + working = wheel & 0x7F; - if (mem_fwrite(&working, 1, 1, midioutput) != 1) - { - return true; - } + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } - working = (wheel >> 7) & 0x7F; + working = (wheel >> 7) & 0x7F; - if (mem_fwrite(&working, 1, 1, midioutput) != 1) - { - return true; - } + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } - tracksize += 3; - return false; + tracksize += 3; + return false; } // Write a patch change event -static boolean midi_writechangepatch(byte channel, byte patch, - MEMFILE *midioutput) +static boolean WriteChangePatch(byte channel, byte patch, + MEMFILE *midioutput) { - byte working = midi_changepatch | channel; - - if (midi_writetime(queuedtime, midioutput)) - { - return true; - } + byte working = midi_changepatch | channel; - if (mem_fwrite(&working, 1, 1, midioutput) != 1) - { - return true; - } + if (WriteTime(queuedtime, midioutput)) + { + return true; + } - working = patch & 0x7F; + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } - if (mem_fwrite(&working, 1, 1, midioutput) != 1) - { - return true; - } + working = patch & 0x7F; - tracksize += 2; - - return false; -} + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + tracksize += 2; + return false; +} // Write a valued controller change event -static boolean midi_writechangecontroller_valued(byte channel, - byte control, - byte value, - MEMFILE *midioutput) + +static boolean WriteChangeController_Valued(byte channel, + byte control, + byte value, + MEMFILE *midioutput) { - byte working = midi_changecontroller | channel; + byte working = midi_changecontroller | channel; + + if (WriteTime(queuedtime, midioutput)) + { + return true; + } - if (midi_writetime(queuedtime, midioutput)) - { - return true; - } + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } - if (mem_fwrite(&working, 1, 1, midioutput) != 1) - { - return true; - } + working = control & 0x7F; - working = control & 0x7F; + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } - if (mem_fwrite(&working, 1, 1, midioutput) != 1) - { - return true; - } - // Quirk in vanilla DOOM? MUS controller values should be - // 7-bit, not 8-bit. + // Quirk in vanilla DOOM? MUS controller values should be + // 7-bit, not 8-bit. - working = value;// & 0x7F; + working = value;// & 0x7F; - // Fix on said quirk to stop MIDI players from complaining that - // the value is out of range: + // Fix on said quirk to stop MIDI players from complaining that + // the value is out of range: - if (working & 0x80) - { - working = 0x7F; - } + if (working & 0x80) + { + working = 0x7F; + } - if (mem_fwrite(&working, 1, 1, midioutput) != 1) - { - return true; - } + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } - tracksize += 3; + tracksize += 3; - return false; + return false; } // Write a valueless controller change event -static boolean midi_writechangecontroller_valueless(byte channel, - byte control, - MEMFILE *midioutput) +static boolean WriteChangeController_Valueless(byte channel, + byte control, + MEMFILE *midioutput) { - return midi_writechangecontroller_valued(channel, control, 0, - midioutput); + return WriteChangeController_Valued(channel, control, 0, + midioutput); } -static boolean read_musheader(MEMFILE *file, musheader *header) +// Allocate a free MIDI channel. + +static int AllocateMIDIChannel(void) { - boolean result; - - result = (mem_fread(&header->id, sizeof(byte), 4, file) == 4) - && (mem_fread(&header->scorelength, sizeof(short), 1, file) == 1) - && (mem_fread(&header->scorestart, sizeof(short), 1, file) == 1) - && (mem_fread(&header->primarychannels, sizeof(short), 1, file) == 1) - && (mem_fread(&header->secondarychannels, sizeof(short), 1, file) == 1) - && (mem_fread(&header->instrumentcount, sizeof(short), 1, file) == 1); - - if (result) - { - header->scorelength = SHORT(header->scorelength); - header->scorestart = SHORT(header->scorestart); - header->primarychannels = SHORT(header->primarychannels); - header->secondarychannels = SHORT(header->secondarychannels); - header->instrumentcount = SHORT(header->instrumentcount); - } - - return result; + int result; + int max; + int i; + + // Find the current highest-allocated channel. + + max = -1; + + for (i=0; i<NUM_CHANNELS; ++i) + { + if (channel_map[i] > max) + { + max = channel_map[i]; + } + } + + // max is now equal to the highest-allocated MIDI channel. We can + // now allocate the next available channel. This also works if + // no channels are currently allocated (max=-1) + + result = max + 1; + + // Don't allocate the MIDI percussion channel! + + if (result == MIDI_PERCUSSION_CHAN) + { + ++result; + } + + return result; } +// Given a MUS channel number, get the MIDI channel number to use +// in the outputted file. -// Read a MUS file from a stream (musinput) and output a MIDI file to +static int GetMIDIChannel(int mus_channel) +{ + // Find the MIDI channel to use for this MUS channel. + // MUS channel 15 is the percusssion channel. + + if (mus_channel == MUS_PERCUSSION_CHAN) + { + return MIDI_PERCUSSION_CHAN; + } + else + { + // If a MIDI channel hasn't been allocated for this MUS channel + // yet, allocate the next free MIDI channel. + + if (channel_map[mus_channel] == -1) + { + channel_map[mus_channel] = AllocateMIDIChannel(); + } + + return channel_map[mus_channel]; + } +} + +static boolean ReadMusHeader(MEMFILE *file, musheader *header) +{ + boolean result; + + result = mem_fread(&header->id, sizeof(byte), 4, file) == 4 + && mem_fread(&header->scorelength, sizeof(short), 1, file) == 1 + && mem_fread(&header->scorestart, sizeof(short), 1, file) == 1 + && mem_fread(&header->primarychannels, sizeof(short), 1, file) == 1 + && mem_fread(&header->secondarychannels, sizeof(short), 1, file) == 1 + && mem_fread(&header->instrumentcount, sizeof(short), 1, file) == 1; + + if (result) + { + header->scorelength = SHORT(header->scorelength); + header->scorestart = SHORT(header->scorestart); + header->primarychannels = SHORT(header->primarychannels); + header->secondarychannels = SHORT(header->secondarychannels); + header->instrumentcount = SHORT(header->instrumentcount); + } + + return result; +} + + +// Read a MUS file from a stream (musinput) and output a MIDI file to // a stream (midioutput). // // Returns 0 on success or 1 on failure. -boolean mus2mid(MEMFILE *musinput, MEMFILE *midioutput) +boolean mus2mid(MEMFILE *musinput, MEMFILE *midioutput) { - // Header for the MUS file - musheader musfileheader; + // Header for the MUS file + musheader musfileheader; + + // Descriptor for the current MUS event + byte eventdescriptor; + int channel; // Channel number + musevent event; + + + // Bunch of vars read from MUS lump + byte key; + byte controllernumber; + byte controllervalue; - // Descriptor for the current MUS event - byte eventdescriptor; - int channel; // Channel number - musevent event; - + // Buffer used for MIDI track size record + byte tracksizebuffer[4]; - // Bunch of vars read from MUS lump - byte key; - byte controllernumber; - byte controllervalue; + // Flag for when the score end marker is hit. + int hitscoreend = 0; - // Buffer used for MIDI track size record - byte tracksizebuffer[4]; + // Temp working byte + byte working; + // Used in building up time delays + unsigned int timedelay; - // Flag for when the score end marker is hit. - int hitscoreend = 0; + // Initialise channel map to mark all channels as unused. - // Temp working byte - byte working; - // Used in building up time delays - unsigned int timedelay; + for (channel=0; channel<NUM_CHANNELS; ++channel) + { + channel_map[channel] = -1; + } - // Grab the header + // Grab the header - if (!read_musheader(musinput, &musfileheader)) - { - return true; - } + if (!ReadMusHeader(musinput, &musfileheader)) + { + return true; + } #ifdef CHECK_MUS_HEADER - // Check MUS header - if (musfileheader.id[0] != 'M' - || musfileheader.id[1] != 'U' - || musfileheader.id[2] != 'S' - || musfileheader.id[3] != 0x1A) - { - return true; - } + // Check MUS header + if (musfileheader.id[0] != 'M' + || musfileheader.id[1] != 'U' + || musfileheader.id[2] != 'S' + || musfileheader.id[3] != 0x1A) + { + return true; + } #endif - // Seek to where the data is held - if (mem_fseek(musinput, (long)musfileheader.scorestart, - MEM_SEEK_SET) != 0) - { - return true; - } - - // So, we can assume the MUS file is faintly legit. Let's start - // writing MIDI data... - - mem_fwrite(midiheader, 1, sizeof(midiheader), midioutput); - tracksize = 0; - - // Now, process the MUS file: - while (!hitscoreend) - { - // Handle a block of events: - - while (!hitscoreend) - { - // Fetch channel number and event code: - - if (mem_fread(&eventdescriptor, 1, 1, musinput) != 1) - { - return true; - } - - channel = eventdescriptor & 0x0F; - event = eventdescriptor & 0x70; - - // Swap channels 15 and 9. - // MIDI channel 9 = percussion. - // MUS channel 15 = percussion. - - if (channel == 15) - { - channel = 9; - } - else if (channel == 9) - { - channel = 15; - } - - switch (event) - { - case mus_releasekey: - if (mem_fread(&key, 1, 1, musinput) != 1) - { - return true; - } - - if (midi_writereleasekey(channel, key, midioutput)) - { - return true; - } - - break; - - case mus_presskey: - if (mem_fread(&key, 1, 1, musinput) != 1) - { - return true; - } - - if (key & 0x80) - { - if (mem_fread(&channelvelocities[channel], 1, 1, musinput) != 1) - { - return true; - } - - channelvelocities[channel] &= 0x7F; - } - - if (midi_writepresskey(channel, key, channelvelocities[channel], midioutput)) - { - return true; - } - - break; - - case mus_pitchwheel: - if (mem_fread(&key, 1, 1, musinput) != 1) - { - break; - } - if (midi_writepitchwheel(channel, (short)(key * 64), midioutput)) - { - return true; - } - - break; - - case mus_systemevent: - if (mem_fread(&controllernumber, 1, 1, musinput) != 1) - { - return true; - } - if (controllernumber < 10 || controllernumber > 14) - { - return true; - } - - if (midi_writechangecontroller_valueless(channel, mus2midi_translation[controllernumber], midioutput)) - { - return true; - } - - break; - - case mus_changecontroller: - if (mem_fread(&controllernumber, 1, 1, musinput) != 1) - { - return true; - } - - if (mem_fread(&controllervalue, 1, 1, musinput) != 1) - { - return true; - } - - if (controllernumber == 0) - { - if (midi_writechangepatch(channel, controllervalue, midioutput)) - { - return true; - } - } - else - { - if (controllernumber < 1 || controllernumber > 9) - { - return true; - } - - if (midi_writechangecontroller_valued(channel, mus2midi_translation[controllernumber], controllervalue, midioutput)) - { - return true; - } - } - - break; - - case mus_scoreend: - hitscoreend = 1; - break; - - default: - return true; - break; - } - - if (eventdescriptor & 0x80) - { - break; - } - } - // Now we need to read the time code: - if (!hitscoreend) - { - timedelay = 0; - for (;;) - { - if (mem_fread(&working, 1, 1, musinput) != 1) - { - return true; - } - - timedelay = timedelay * 128 + (working & 0x7F); - if ((working & 0x80) == 0) - { - break; - } - } - queuedtime += timedelay; - } - } - - // End of track - if (midi_writeendtrack(midioutput)) - { - return true; - } - - // Write the track size into the stream - if (mem_fseek(midioutput, 18, MEM_SEEK_SET)) - { - return true; - } - - tracksizebuffer[0] = (tracksize >> 24) & 0xff; - tracksizebuffer[1] = (tracksize >> 16) & 0xff; - tracksizebuffer[2] = (tracksize >> 8) & 0xff; - tracksizebuffer[3] = tracksize & 0xff; - - if (mem_fwrite(tracksizebuffer, 1, 4, midioutput) != 4) - { - return true; - } - - return false; + // Seek to where the data is held + if (mem_fseek(musinput, (long)musfileheader.scorestart, + MEM_SEEK_SET) != 0) + { + return true; + } + + // So, we can assume the MUS file is faintly legit. Let's start + // writing MIDI data... + + mem_fwrite(midiheader, 1, sizeof(midiheader), midioutput); + tracksize = 0; + + // Now, process the MUS file: + while (!hitscoreend) + { + // Handle a block of events: + + while (!hitscoreend) + { + // Fetch channel number and event code: + + if (mem_fread(&eventdescriptor, 1, 1, musinput) != 1) + { + return true; + } + + channel = GetMIDIChannel(eventdescriptor & 0x0F); + event = eventdescriptor & 0x70; + + switch (event) + { + case mus_releasekey: + if (mem_fread(&key, 1, 1, musinput) != 1) + { + return true; + } + + if (WriteReleaseKey(channel, key, midioutput)) + { + return true; + } + + break; + + case mus_presskey: + if (mem_fread(&key, 1, 1, musinput) != 1) + { + return true; + } + + if (key & 0x80) + { + if (mem_fread(&channelvelocities[channel], 1, 1, musinput) != 1) + { + return true; + } + + channelvelocities[channel] &= 0x7F; + } + + if (WritePressKey(channel, key, + channelvelocities[channel], midioutput)) + { + return true; + } + + break; + + case mus_pitchwheel: + if (mem_fread(&key, 1, 1, musinput) != 1) + { + break; + } + if (WritePitchWheel(channel, (short)(key * 64), midioutput)) + { + return true; + } + + break; + + case mus_systemevent: + if (mem_fread(&controllernumber, 1, 1, musinput) != 1) + { + return true; + } + if (controllernumber < 10 || controllernumber > 14) + { + return true; + } + + if (WriteChangeController_Valueless(channel, + controller_map[controllernumber], + midioutput)) + { + return true; + } + + break; + + case mus_changecontroller: + if (mem_fread(&controllernumber, 1, 1, musinput) != 1) + { + return true; + } + + if (mem_fread(&controllervalue, 1, 1, musinput) != 1) + { + return true; + } + + if (controllernumber == 0) + { + if (WriteChangePatch(channel, controllervalue, + midioutput)) + { + return true; + } + } + else + { + if (controllernumber < 1 || controllernumber > 9) + { + return true; + } + + if (WriteChangeController_Valued(channel, + controller_map[controllernumber], + controllervalue, + midioutput)) + { + return true; + } + } + + break; + + case mus_scoreend: + hitscoreend = 1; + break; + + default: + return true; + break; + } + + if (eventdescriptor & 0x80) + { + break; + } + } + // Now we need to read the time code: + if (!hitscoreend) + { + timedelay = 0; + for (;;) + { + if (mem_fread(&working, 1, 1, musinput) != 1) + { + return true; + } + + timedelay = timedelay * 128 + (working & 0x7F); + if ((working & 0x80) == 0) + { + break; + } + } + queuedtime += timedelay; + } + } + + // End of track + if (WriteEndTrack(midioutput)) + { + return true; + } + + // Write the track size into the stream + if (mem_fseek(midioutput, 18, MEM_SEEK_SET)) + { + return true; + } + + tracksizebuffer[0] = (tracksize >> 24) & 0xff; + tracksizebuffer[1] = (tracksize >> 16) & 0xff; + tracksizebuffer[2] = (tracksize >> 8) & 0xff; + tracksizebuffer[3] = tracksize & 0xff; + + if (mem_fwrite(tracksizebuffer, 1, 4, midioutput) != 4) + { + return true; + } + + return false; } diff --git a/src/resource.rc.in b/src/resource.rc.in index 7eb5d00f..f8b64331 100644 --- a/src/resource.rc.in +++ b/src/resource.rc.in @@ -13,7 +13,7 @@ FILETYPE 1 VALUE "FileDescription", "@PACKAGE_STRING@" VALUE "InternalName", "@PACKAGE_TARNAME@" VALUE "CompanyName", "@PACKAGE_BUGREPORT@" - VALUE "LegalCopyright", "GNU General Public License" + VALUE "LegalCopyright", "@PACKAGE_COPYRIGHT@. Licensed under @PACKAGE_LICENSE@" VALUE "ProductName", "@PACKAGE_NAME@" VALUE "ProductVersion", "@PACKAGE_VERSION@" } diff --git a/src/setup/.gitignore b/src/setup/.gitignore index d7e732ad..37c8e4c1 100644 --- a/src/setup/.gitignore +++ b/src/setup/.gitignore @@ -1,7 +1,8 @@ -Makefile Makefile.in +Makefile .deps +chocolate-setup *.rc -chocolate-doom -chocolate-server *.exe +tags +TAGS diff --git a/src/setup/Makefile.am b/src/setup/Makefile.am index 07d7c936..25ab38e3 100644 --- a/src/setup/Makefile.am +++ b/src/setup/Makefile.am @@ -27,3 +27,10 @@ libsetup_a_SOURCES = $(SOURCE_FILES) EXTRA_DIST= \ setup_icon.c +if HAVE_PYTHON + +setup_icon.c : $(top_builddir)/data/setup8.ico + $(top_builddir)/data/convert-icon $^ $@ + +endif + diff --git a/src/setup/execute.c b/src/setup/execute.c index be7214f5..4be44149 100644 --- a/src/setup/execute.c +++ b/src/setup/execute.c @@ -172,29 +172,38 @@ static void ConcatWCString(wchar_t *buf, const char *value) static wchar_t *BuildCommandLine(const char *program, const char *arg) { + wchar_t exe_path[MAX_PATH]; wchar_t *result; - char *sep; + wchar_t *sep; + + // Get the path to this .exe file. + + GetModuleFileNameW(NULL, exe_path, MAX_PATH); - result = calloc(strlen(myargv[0]) + strlen(program) + strlen(arg) + 6, + // Allocate buffer to contain result string. + + result = calloc(wcslen(exe_path) + strlen(program) + strlen(arg) + 6, sizeof(wchar_t)); wcscpy(result, L"\""); - sep = strrchr(myargv[0], DIR_SEPARATOR); + // Copy the path part of the filename (including ending \) + // into the result buffer: + + sep = wcsrchr(exe_path, DIR_SEPARATOR); if (sep != NULL) { - ConcatWCString(result, myargv[0]); - - // Cut off the string after the last directory separator, - // before appending the actual program. - - result[sep - myargv[0] + 2] = '\0'; - + wcsncpy(result + 1, exe_path, sep - exe_path + 1); + result[sep - exe_path + 2] = '\0'; } + // Concatenate the name of the program: + ConcatWCString(result, program); + // End of program name, start of argument: + wcscat(result, L"\" \""); ConcatWCString(result, arg); @@ -290,7 +299,7 @@ static int ExecuteCommand(const char *program, const char *arg) execvp(argv[0], (char **) argv); - exit(-1); + exit(0x80); } else { @@ -299,7 +308,7 @@ static int ExecuteCommand(const char *program, const char *arg) waitpid(childpid, &result, 0); - if (WIFEXITED(result)) + if (WIFEXITED(result) && WEXITSTATUS(result) != 0x80) { return WEXITSTATUS(result); } diff --git a/textscreen/.gitignore b/textscreen/.gitignore index 6f6e5b3b..3be8ec0c 100644 --- a/textscreen/.gitignore +++ b/textscreen/.gitignore @@ -2,3 +2,5 @@ Makefile Makefile.in .deps *.a +tags +TAGS diff --git a/textscreen/examples/.gitignore b/textscreen/examples/.gitignore index b75bfc6b..cb0fd52c 100644 --- a/textscreen/examples/.gitignore +++ b/textscreen/examples/.gitignore @@ -4,3 +4,5 @@ Makefile guitest calculator *.exe +tags +TAGS diff --git a/textscreen/txt_sdl.c b/textscreen/txt_sdl.c index 71736720..a0cbe3d6 100644 --- a/textscreen/txt_sdl.c +++ b/textscreen/txt_sdl.c @@ -289,10 +289,10 @@ void TXT_UpdateScreenArea(int x, int y, int w, int h) int x_end; int y_end; - x_end = LimitToRange(x + w, 0, TXT_SCREEN_W - 1); - y_end = LimitToRange(y + h, 0, TXT_SCREEN_H - 1); - x = LimitToRange(x, 0, TXT_SCREEN_W - 1); - y = LimitToRange(y, 0, TXT_SCREEN_H - 1); + x_end = LimitToRange(x + w, 0, TXT_SCREEN_W); + y_end = LimitToRange(y + h, 0, TXT_SCREEN_H); + x = LimitToRange(x, 0, TXT_SCREEN_W); + y = LimitToRange(y, 0, TXT_SCREEN_H); for (y1=y; y1<y_end; ++y1) { diff --git a/wince/.gitignore b/wince/.gitignore new file mode 100644 index 00000000..d4e88e5a --- /dev/null +++ b/wince/.gitignore @@ -0,0 +1,5 @@ +Makefile +Makefile.in +.deps +tags +TAGS diff --git a/wince/Makefile.am b/wince/Makefile.am index 476b9a67..c19471ca 100644 --- a/wince/Makefile.am +++ b/wince/Makefile.am @@ -4,6 +4,7 @@ noinst_LIBRARIES=libc_wince.a if WINDOWS_CE libc_wince_a_SOURCES = \ + libc_wince.h \ env.c env.h \ errno.c errno.h \ fileops.c fileops.h |