diff options
author | Simon Howard | 2009-07-09 17:50:04 +0000 |
---|---|---|
committer | Simon Howard | 2009-07-09 17:50:04 +0000 |
commit | d06dcf916776e58b1f476cf9320b5274a28957b1 (patch) | |
tree | 8105769fc715d0cdcbf4c9a6b20d1a92250d7ee6 | |
parent | d91e3e86736f710265629e56dc77c1dec0b425e7 (diff) | |
parent | 24783792e5398a2d5af6b7220b68c46357d23e31 (diff) | |
download | chocolate-doom-d06dcf916776e58b1f476cf9320b5274a28957b1.tar.gz chocolate-doom-d06dcf916776e58b1f476cf9320b5274a28957b1.tar.bz2 chocolate-doom-d06dcf916776e58b1f476cf9320b5274a28957b1.zip |
Merge from trunk.
Subversion-branch: /branches/raven-branch
Subversion-revision: 1610
-rw-r--r-- | NEWS | 44 | ||||
-rw-r--r-- | configure.in | 1 | ||||
-rw-r--r-- | pkg/wince/.gitignore | 3 | ||||
-rw-r--r-- | pkg/wince/Makefile.am | 10 | ||||
-rw-r--r-- | pkg/wince/wince-cab.cfg | 25 | ||||
-rwxr-xr-x | pkg/wince/wince-cabgen | 672 | ||||
-rw-r--r-- | src/i_system.c | 50 | ||||
-rw-r--r-- | src/i_video.c | 11 | ||||
-rw-r--r-- | src/setup/execute.c | 143 |
9 files changed, 921 insertions, 38 deletions
@@ -1,3 +1,47 @@ +... + + * 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. + * 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. + + Compatibility: + * The A_BossDeath behavior in v1.9 emulation mode was fixed + (thanks entryway) + + Bugs fixed: + * 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 + 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. + 1.2.1 (2008-12-10): This version just fixes a crash at the intermission screen when diff --git a/configure.in b/configure.in index 9fac0bc1..d4ebafab 100644 --- a/configure.in +++ b/configure.in @@ -113,6 +113,7 @@ src/heretic/Makefile src/hexen/Makefile src/setup/Makefile pcsound/Makefile +pkg/wince/Makefile src/resource.rc src/setup-res.rc src/setup/setup-manifest.xml diff --git a/pkg/wince/.gitignore b/pkg/wince/.gitignore new file mode 100644 index 00000000..285c4716 --- /dev/null +++ b/pkg/wince/.gitignore @@ -0,0 +1,3 @@ +Makefile +Makefile.in +*.cab diff --git a/pkg/wince/Makefile.am b/pkg/wince/Makefile.am new file mode 100644 index 00000000..13b48d2e --- /dev/null +++ b/pkg/wince/Makefile.am @@ -0,0 +1,10 @@ + +DEPS=$(shell ./wince-cabgen -d $(CONFIG_FILE)) +CONFIG_FILE=wince-cab.cfg +OUTPUT_FILE=@PACKAGE_TARNAME@-@PACKAGE_VERSION@.cab + +noinst_DATA = $(OUTPUT_FILE) + +$(OUTPUT_FILE) : $(CONFIG_FILE) $(DEPS) + ./wince-cabgen $< $@ + diff --git a/pkg/wince/wince-cab.cfg b/pkg/wince/wince-cab.cfg new file mode 100644 index 00000000..5f37c5ab --- /dev/null +++ b/pkg/wince/wince-cab.cfg @@ -0,0 +1,25 @@ + +app_name = "Chocolate Doom" +provider = "Simon Howard" +arch = "strongarm" + +# Install files: + +d = "$(PROGRAMS_GAMES)/Chocolate Doom/" +s = "$(START_GAMES)/" + +files = { + d+"chocolate-doom.exe": "../../src/chocolate-doom.exe", + d+"chocolate-setup.exe": "../../setup/chocolate-setup.exe", + d+"SDL.dll": "SDL.dll", + d+"SDL_mixer.dll": "SDL_mixer.dll", + d+"libSDL_net-1-2-0.dll": "libSDL_net-1-2-0.dll", +} + +# Start menu links: + +links = { + s+"Chocolate Doom.lnk": d+"chocolate-doom.exe", + s+"Chocolate Doom Setup.lnk": d+"chocolate-setup.exe" +} + diff --git a/pkg/wince/wince-cabgen b/pkg/wince/wince-cabgen new file mode 100755 index 00000000..97cba132 --- /dev/null +++ b/pkg/wince/wince-cabgen @@ -0,0 +1,672 @@ +#!/usr/bin/env python + +import os +import re +import shutil +import sys +import tempfile + +CAB_HEADER = "MSCE" + +ARCHITECTURES = { + "shx-sh3": 103, + "shx-sh4": 104, + "i386": 386, + "i486": 486, + "i586": 586, + "powerpc-601": 601, + "powerpc-603": 603, + "powerpc-604": 604, + "powerpc-620": 620, + "powerpc-mpc821": 821, + "arm720": 1824, + "arm820": 2080, + "arm920": 2336, + "strongarm": 2577, + "mips-r4000": 4000, + "sh3": 10003, + "sh3e": 10004, + "sh4": 10005, + "alpha-21064": 21064, + "arm7tdmi": 70001, +} + +DIR_VARIABLES = { + "PROGRAMS": "%CE1%", # \Program Files + "WINDOWS": "%CE2%", # \Windows + "DESKTOP": "%CE3%", # \Windows\Desktop + "STARTUP": "%CE4%", # \Windows\StartUp + "DOCUMENTS": "%CE5%", # \My Documents + "PROGRAMS_ACCESSORIES": "%CE6%", # \Program Files\Accessories + "PROGRAMS_COMMUNICATIONS": "%CE7%", # \Program Files\Communications + "PROGRAMS_GAMES": "%CE8%", # \Program Files\Games + "PROGRAMS_OUTLOOK": "%CE9%", # \Program Files\Pocket Outlook + "PROGRAMS_OFFICE": "%CE10%", # \Program Files\Office + "WINDOWS_PROGRAMS": "%CE11%", # \Windows\Programs + "WINDOWS_ACCESSORIES": "%CE12%", # \Windows\Programs\Accessories + "WINDOWS_COMMUNICATIONS": "%CE13%", # \Windows\Programs\Communications + "WINDOWS_GAMES": "%CE14%", # \Windows\Programs\Games + "FONTS": "%CE15%", # \Windows\Fonts + "RECENT": "%CE16%", # \Windows\Recent + "FAVORITES": "%CE17%", # \Windows\Favorites + + "START_PROGRAMS": "%CE11%", # \Windows\Start Menu\Programs + "START_ACCESSORIES": "%CE12%", # \Windows\Start Menu\Accessories + "START_COMMUNICATIONS": "%CE13%", # \Windows\Start Menu\Communications + "START_GAMES": "%CE14%", # \Windows\Start Menu\Games + "START": "%CE17%", # \Windows\Start Menu +} + +def write_int16(f, value): + b1 = value & 0xff + b2 = (value >> 8) & 0xff + f.write("%c%c" % (b1, b2)) + +def write_int32(f, value): + b1 = value & 0xff + b2 = (value >> 8) & 0xff + b3 = (value >> 16) & 0xff + b4 = (value >> 24) & 0xff + f.write("%c%c%c%c" % (b1, b2, b3, b4)) + +# Pad a string with NUL characters so that it has a length that is +# a multiple of 4. At least one NUL is always added. + +def pad_string(s): + pad_len = 4 - (len(s) % 4) + return s + (pad_len * "\x00") + +class HeaderSection: + + def __init__(self, cab_header): + self.cab_header = cab_header + self.arch = None + self.app_name = None + self.provider = None + self.unsupported = None + + def __len__(self): + return 100 # header has fixed size + + def set_meta(self, arch, app_name, provider, unsupported): + + if arch not in ARCHITECTURES: + raise Exception("Unknown architecture '%s'" % arch) + + self.arch = ARCHITECTURES[arch] + + dictionary = self.cab_header.dictionary + + self.app_name = app_name + dictionary.get(self.app_name) + + self.provider = provider + dictionary.get(self.provider) + + self.unsupported = unsupported + dictionary.get(self.unsupported) + + def write(self, stream): + + # Basic header + + stream.write(CAB_HEADER) + write_int32(stream, 0) + write_int32(stream, len(self.cab_header)) + write_int32(stream, 0) + write_int32(stream, 1) + write_int32(stream, self.arch) + + # minimum Windows CE version: + write_int32(stream, 0) + write_int32(stream, 0) + write_int32(stream, 0) + write_int32(stream, 0) + write_int32(stream, 0) + write_int32(stream, 0) + + dictionary = self.cab_header.dictionary + + # Write number of entries in other sections: + + for section in self.cab_header.sections: + if section is not self: + write_int16(stream, section.num_entries()) + + # Write offsets of other sections: + + for section in self.cab_header.sections: + if section is not self: + offset = self.cab_header.get_section_offset(section) + write_int32(stream, offset) + + # Special strings: + + special_strings = ( + self.app_name, + self.provider, + self.unsupported + ) + + dictionary_offset = self.cab_header.get_section_offset(dictionary) + + for s in special_strings: + s_offset = dictionary.get_offset(s) + write_int16(stream, dictionary_offset + s_offset) + write_int16(stream, len(s) + 1) + + # Two left-over fields of unknown use: + + write_int16(stream, 0) + write_int16(stream, 0) + +class StringDictionary: + def __init__(self, cab_header): + self.cab_header = cab_header + self.string_list = [] + self.strings = {} + self.length = 0 + self.index = 1 + + # Get the length of the dictionary, in bytes. + + def __len__(self): + return self.length + + # Get the number of entries in the dictionary. + + def num_entries(self): + return len(self.strings) + + # Get the ID for the given string, adding it if necessary. + + def get(self, s): + # Is this a new string? Add it to the dictionary. + + if s not in self.strings: + offset = self.length + padded = pad_string(s) + + self.strings[s] = (self.index, offset) + self.string_list.append((self.index, padded)) + self.length += len(padded) + 4 + self.index += 1 + + return self.strings[s][0] + + # Get the offset of a particular string within the dictionary. + + def get_offset(self, s): + return self.strings[s][1] + 4 + + # Write the dictionary to the output stream. + + def write(self, stream): + + # Write out all strings: + + for i, s in self.string_list: + write_int16(stream, i) + write_int16(stream, len(s)) + stream.write(s) + +class DirectoryList: + def __init__(self, cab_header): + self.cab_header = cab_header + self.directories_list = [] + self.directories = {} + self.length = 0 + self.index = 1 + + def __len__(self): + return self.length + + def num_entries(self): + return len(self.directories_list) + + # Find whether the specified directory exists in the list + + def find(self, dir): + key = dir.lower() + + if key in self.directories: + return self.directories[key] + else: + return None + + # Get the ID for the given directory, adding it if necessary. + + def get(self, dir): + + key = dir.lower() + dictionary = self.cab_header.dictionary + + # Add new directory? + + if key not in self.directories: + + # Separate into individual directories, and map to strings: + + #dir_path = dir.split("\\") + #if dir_path[0] == "": + # dir_path = dir_path[1:] + dir_path = [ dir ] + + dir_path = map(lambda x: dictionary.get(x), dir_path) + + self.directories[key] = self.index + self.directories_list.append((self.index, dir_path)) + self.length += 6 + 2 * len(dir_path) + self.index += 1 + + return self.directories[key] + + # Write the directory list to the specified stream. + + def write(self, stream): + for i, dir in self.directories_list: + write_int16(stream, i) + write_int16(stream, 2 * len(dir) + 2) + + for subdir in dir: + write_int16(stream, subdir) + + write_int16(stream, 0) + +class FileList: + def __init__(self, cab_header): + self.cab_header = cab_header + self.files = [] + self.length = 0 + self.index = 1 + + # Get the length of this section, in bytes. + + def __len__(self): + return self.length + + # Query whether the file list contains a particular file. + + def find(self, filename): + dirname, sep, target_basename = filename.rpartition("\\") + + target_basename = pad_string(target_basename) + + target_dir_id = self.cab_header.directory_list.find(dirname) + + if target_dir_id is None: + return None + else: + # Search the list of files: + + for i, dir_id, basename, file_no, flags in self.files: + if dir_id == target_dir_id and basename == target_basename: + return file_no + else: + return None + + # Get the number of entries in the file list + + def num_entries(self): + return len(self.files) + + # Add a file to the list. + + def add(self, filename, file_no, flags=0): + + dirname, sep, basename = filename.rpartition("\\") + + dir_id = self.cab_header.directory_list.get(dirname) + + padded = pad_string(basename) + + self.files.append((self.index, dir_id, padded, file_no, flags)) + self.length += 12 + len(padded) + self.index += 1 + + # Write this section to the output stream. + + def write(self, stream): + + for i, dir_id, filename, file_no, flags in self.files: + write_int16(stream, i) + write_int16(stream, dir_id) + write_int16(stream, file_no) + write_int32(stream, flags) + write_int16(stream, len(filename)) + stream.write(filename) + +# TODO? + +class RegHiveList: + def __len__(self): + return 0 + + def num_entries(self): + return 0 + + def write(self, stream): + pass + +class RegKeyList(): + def __len__(self): + return 0 + + def num_entries(self): + return 0 + + def write(self, stream): + pass + +class LinkList: + def __init__(self, cab_header): + self.cab_header = cab_header + self.links = [] + self.length = 0 + self.index = 1 + + def __len__(self): + return self.length + + def num_entries(self): + return len(self.links) + + # Determine the target type (dir/file) and ID: + + def __find_target(self, target): + file_id = self.cab_header.file_list.find(target) + + if file_id is not None: + return 1, file_id + + dir_list = self.cab_header.get_section(DirectoryList) + dir_id = dir_list.find(target) + + if dir_id is not None: + return 0, dir_id + + raise Exception("Link target '%s' not found" % target) + + def add(self, target, destination): + + target_type, target_id = self.__find_target(target) + + dest_path = destination.split("\\") + + # Leading \: + + if dest_path[0] == "": + dest_path = dest_path[1:] + + # %CEn% to specify the install root is handled differently for + # links than it is for files/dirs. + + match = re.match(r"\%CE(\d+)\%", dest_path[0]) + + if match: + base_dir = int(match.group(1)) + dest_path = dest_path[1:] + else: + base_dir = 0 + + # Map dirs that make up the path to strings. + + dictionary = self.cab_header.dictionary + dest_path = map(lambda x: dictionary.get(x), dest_path) + + self.links.append((self.index, target_type, target_id, + base_dir, dest_path)) + self.index += 1 + self.length += 14 + 2 * len(dest_path) + + def write(self, stream): + + for i, target_type, target_id, base_dir, dest_path in self.links: + + write_int16(stream, i) + write_int16(stream, 0) + write_int16(stream, base_dir) + write_int16(stream, target_id) + write_int16(stream, target_type) + write_int16(stream, 2 * len(dest_path) + 2) + + for subdir in dest_path: + write_int16(stream, subdir) + + write_int16(stream, 0) + +class CabHeaderFile: + def __init__(self): + self.dictionary = StringDictionary(self) + self.directory_list = DirectoryList(self) + self.file_list = FileList(self) + + self.sections = [ + HeaderSection(self), + self.dictionary, + self.directory_list, + self.file_list, + RegHiveList(), + RegKeyList(), + LinkList(self) + ] + + def set_meta(self, *args): + header_section = self.get_section(HeaderSection) + header_section.set_meta(*args) + + def add_file(self, filename, file_no, flags=0): + files_section = self.get_section(FileList) + files_section.add(filename, file_no, flags) + + def add_link(self, target, destination): + links_section = self.get_section(LinkList) + links_section.add(target, destination) + + def get_section(self, section_class): + for section in self.sections: + if isinstance(section, section_class): + return section + else: + raise Exception("Can't find section of class %s" % section_class) + + def get_section_offset(self, section): + offset = 0 + + for s in self.sections: + if section is s: + return offset + offset += len(s) + else: + raise Exception("Section %s not found in list") + + def __len__(self): + result = 0 + for s in self.sections: + result += len(s) + return result + + def write(self, stream): + old_pos = 0 + for section in self.sections: + section.write(stream) + pos = stream.tell() + if pos != old_pos + len(section): + raise Exception("Section is %i bytes long, but %i written" % \ + (len(section), pos - old_pos)) + old_pos = pos + +class CabFile: + def __init__(self, config): + self.cab_header = CabHeaderFile() + + self.__process_meta(config) + self.__process_files(config["files"]) + + if "links" in config: + self.__process_links(config["links"]) + + # Metadata: + + def __process_meta(self, config): + arch = config.get("arch") or "strongarm" + app_name = config.get("app_name") + provider = config.get("provider") + unsupported = config.get("unsupported") or "" + + if app_name is None or provider is None: + raise Exception("Application name and provider must be specified") + + self.cab_header.set_meta(arch, app_name, provider, unsupported) + self.app_name = app_name + + # Get the shortened 8.3 filename used for the specified file + # within the CAB. + + def __shorten_name(self, filename, file_no): + + # Strip down to base filename without extension: + + basename = os.path.basename(filename) + + if "." in basename: + basename = basename.rpartition(".")[0] + + # Remove non-alphanumeric characters: + + def only_alnum(x): + if x.isalnum(): + return x + else: + return "" + + cleaned_name = "".join(map(only_alnum, basename)) + short_name = cleaned_name[0:8] + + if len(short_name) < 8: + short_name = "0" * (8 - len(short_name)) + short_name + + return "%s.%03i" % (short_name, file_no) + + # Process the list of files to install: + + def __process_files(self, files): + self.files = [ self.app_name ] + + for filename, source_file in files.items(): + file_no = len(self.files) + filename = expand_path(filename) + self.cab_header.add_file(filename, file_no) + self.files.append(source_file) + + # Process the list of links: + + def __process_links(self, links): + for destination, target in links.items(): + target = expand_path(target) + destination = expand_path(destination) + self.cab_header.add_link(target, destination) + + # Write the header file: + + def __write_header(self, dir): + + basename = self.__shorten_name(self.files[0], 0) + filename = os.path.join(dir, basename) + + stream = file(filename, "w") + self.cab_header.write(stream) + stream.close() + + return [ filename ] + + # Write the files: + + def __write_files(self, dir): + + result = [] + + for file_no in range(1, len(self.files)): + source_file = self.files[file_no] + basename = self.__shorten_name(source_file, file_no) + filename = os.path.join(dir, basename) + + shutil.copy(source_file, filename) + result.append(filename) + + return result + + # Output to a file: + + def write(self, filename): + + temp_dir = tempfile.mkdtemp() + + header = self.__write_header(temp_dir) + files = self.__write_files(temp_dir) + files.reverse() + + args = [ "lcab", "-n" ] + header + files + [ filename ] + + os.spawnlp(os.P_WAIT, "lcab", *args) + + # Clean up: + + for tmpfile in header + files: + os.remove(tmpfile) + os.rmdir(temp_dir) + +def expand_path(filename): + + # Replace Unix-style / path separators with DOS-style \ + + filename = filename.replace("/", "\\") + + # Expand $(xyz) path variables to their Windows equivalents: + + def replace_var(match): + var_name = match.group(1) + + if not var_name in DIR_VARIABLES: + raise Exception("Unknown variable '%s'" % var_name) + else: + return DIR_VARIABLES[var_name] + + return re.sub(r"\$\((.*?)\)", replace_var, filename) + +def read_config_file(filename): + f = file(filename) + + data = f.readlines() + data = "".join(data) + + f.close() + + prog = compile(data, filename, "exec") + result = {} + eval(prog, result) + + return result + +# List the files that the output CAB depends on. + +def print_dependencies(filename): + config = read_config_file(filename) + + files_list = config["files"] + + for dest, source_file in files_list.items(): + print source_file + +if len(sys.argv) < 3: + print "Usage: %s <config file> <output file>" % sys.argv[0] + sys.exit(0) + +if sys.argv[1] == "-d": + print_dependencies(sys.argv[2]) +else: + config = read_config_file(sys.argv[1]) + + cab_file = CabFile(config) + cab_file.write(sys.argv[2]) + diff --git a/src/i_system.c b/src/i_system.c index 0ec8e185..c6deb905 100644 --- a/src/i_system.c +++ b/src/i_system.c @@ -55,6 +55,8 @@ #include "w_wad.h" #include "z_zone.h" +#define MIN_RAM 4 /* MiB */ + int mb_used = 16; typedef struct atexit_listentry_s atexit_listentry_t; @@ -86,8 +88,10 @@ void I_Tactile(int on, int off, int total) { } -int I_GetHeapSize (void) +byte *I_ZoneBase (int *size) { + byte *zonemem; + int min_ram = MIN_RAM; int p; //! @@ -97,28 +101,46 @@ int I_GetHeapSize (void) // p = M_CheckParm("-mb"); - + if (p > 0) { mb_used = atoi(myargv[p+1]); + min_ram = mb_used; } - - return mb_used*1024*1024; -} - -byte *I_ZoneBase (int *size) -{ - byte *zonemem; - *size = I_GetHeapSize(); + // Allocate the zone memory. This loop tries progressively smaller + // zone sizes until a size is found that can be allocated. + // If we used the -mb command line parameter, only the parameter + // provided is accepted. - zonemem = malloc(*size); + zonemem = NULL; - if (zonemem == NULL) + while (zonemem == NULL) { - I_Error("Failed to allocate %i bytes for zone memory", *size); + // We need a reasonable minimum amount of RAM to start. + + if (mb_used < min_ram) + { + I_Error("Unable to allocate %i MiB of RAM for zone", mb_used); + } + + // Try to allocate the zone memory. + + *size = mb_used * 1024 * 1024; + + zonemem = malloc(*size); + + // Failed to allocate? Reduce zone size until we reach a size + // that is acceptable. We decrease by 2 MiB at a time to ensure + // that there is 1-2 MiB still free on the system (my Windows + // Mobile PDA becomes unstable if very low on memory) + + if (zonemem == NULL) + { + mb_used -= 2; + } } - + printf("zone memory: %p, %x allocated for zone\n", zonemem, *size); diff --git a/src/i_video.c b/src/i_video.c index a938b1f8..6f82e60a 100644 --- a/src/i_video.c +++ b/src/i_video.c @@ -662,13 +662,20 @@ void I_GetEvent(void) event.data1 = TranslateKey(&sdlevent.key.keysym); event.data2 = GetTypedChar(&sdlevent); - D_PostEvent(&event); + if (event.data1 != 0) + { + D_PostEvent(&event); + } break; case SDL_KEYUP: event.type = ev_keyup; event.data1 = TranslateKey(&sdlevent.key.keysym); - D_PostEvent(&event); + + if (event.data1 != 0) + { + D_PostEvent(&event); + } break; /* diff --git a/src/setup/execute.c b/src/setup/execute.c index 1b510f2e..18a07156 100644 --- a/src/setup/execute.c +++ b/src/setup/execute.c @@ -29,11 +29,13 @@ #include <sys/types.h> #if defined(_WIN32_WCE) - #include "libc_wince.h" +#endif -#elif defined(_WIN32) +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include <windows.h> #include <process.h> #else @@ -66,7 +68,6 @@ static char *TempFile(char *s) char *tempdir; #ifdef _WIN32 - // Check the TEMP environment variable to find the location. tempdir = getenv("TEMP"); @@ -134,28 +135,131 @@ void AddCmdLineParameter(execute_context_t *context, char *s, ...) fprintf(context->stream, "\n"); } -#if defined(_WIN32_WCE) +#if defined(_WIN32) + +// Wait for the specified process to exit. Returns the exit code. -static int ExecuteCommand(const char **argv) +static unsigned int WaitForProcessExit(HANDLE subprocess) { - // Windows CE version. - // TODO - return 0; + DWORD exit_code; + + for (;;) + { + WaitForSingleObject(subprocess, INFINITE); + + if (!GetExitCodeProcess(subprocess, &exit_code)) + { + return -1; + } + + if (exit_code != STILL_ACTIVE) + { + return exit_code; + } + } } -#elif defined(_WIN32) +static wchar_t *GetFullExePath(const char *program) +{ + wchar_t *result; + unsigned int path_len; + char *sep; + + // Find the full path to the EXE to execute, by taking the path + // to this program and concatenating the EXE name: + + sep = strrchr(myargv[0], DIR_SEPARATOR); + + if (sep == NULL) + { + path_len = 0; + result = calloc(strlen(program) + 1, sizeof(wchar_t)); + } + else + { + path_len = sep - myargv[0] + 1; + + result = calloc(path_len + strlen(program) + 1, + sizeof(wchar_t)); + MultiByteToWideChar(CP_OEMCP, 0, + myargv[0], path_len, + result, path_len); + } -static int ExecuteCommand(const char **argv) + MultiByteToWideChar(CP_OEMCP, 0, + program, strlen(program) + 1, + result + path_len, strlen(program) + 1); + + return result; +} + +// Convert command line argument to wchar_t string and add surrounding +// "" quotes: + +static wchar_t *GetPaddedWideArg(const char *arg) +{ + wchar_t *result; + unsigned int len = strlen(arg); + + // Convert the command line arg to a wide char string: + + result = calloc(len + 3, sizeof(wchar_t)); + MultiByteToWideChar(CP_OEMCP, 0, + arg, len + 1, + result + 1, len + 1); + + // Surrounding quotes: + + result[0] = '"'; + result[len + 1] = '"'; + result[len + 2] = 0; + + return result; +} + +static int ExecuteCommand(const char *program, const char *arg) { - return _spawnv(_P_WAIT, argv[0], argv); + PROCESS_INFORMATION proc_info; + wchar_t *exe_path; + wchar_t *warg; + int result = 0; + + exe_path = GetFullExePath(program); + warg = GetPaddedWideArg(arg); + + // Invoke the program: + + memset(&proc_info, 0, sizeof(proc_info)); + + if (!CreateProcessW(exe_path, warg, + NULL, NULL, FALSE, 0, NULL, NULL, NULL, + &proc_info)) + { + result = -1; + } + else + { + // Wait for the process to finish, and save the exit code. + + result = WaitForProcessExit(proc_info.hProcess); + + CloseHandle(proc_info.hProcess); + CloseHandle(proc_info.hThread); + } + + free(exe_path); + free(warg); + + return result; } #else -static int ExecuteCommand(const char **argv) +static int ExecuteCommand(const char *program, const char *arg) { pid_t childpid; int result; + const char *argv[] = { program, arg, NULL }; childpid = fork(); @@ -189,7 +293,6 @@ static int ExecuteCommand(const char **argv) int ExecuteDoom(execute_context_t *context) { - const char *argv[3]; char *response_file_arg; int result; @@ -200,17 +303,13 @@ int ExecuteDoom(execute_context_t *context) response_file_arg = malloc(strlen(context->response_file) + 2); sprintf(response_file_arg, "@%s", context->response_file); - argv[0] = GetExecutableName(); - argv[1] = response_file_arg; - argv[2] = NULL; - // Run Doom - result = ExecuteCommand(argv); + result = ExecuteCommand(GetExecutableName(), response_file_arg); free(response_file_arg); - - // Destroy context + + // Destroy context remove(context->response_file); free(context->response_file); free(context); @@ -245,8 +344,8 @@ static void TestCallback(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(data)) exec = NewExecuteContext(); AddCmdLineParameter(exec, "-testcontrols"); - AddCmdLineParameter(exec, "-config %s", main_cfg); - AddCmdLineParameter(exec, "-extraconfig %s", extra_cfg); + AddCmdLineParameter(exec, "-config \"%s\"", main_cfg); + AddCmdLineParameter(exec, "-extraconfig \"%s\"", extra_cfg); ExecuteDoom(exec); TXT_CloseWindow(testwindow); |