shithub: choc

Download patch

ref: d06dcf916776e58b1f476cf9320b5274a28957b1
parent: d91e3e86736f710265629e56dc77c1dec0b425e7
parent: 24783792e5398a2d5af6b7220b68c46357d23e31
author: Simon Howard <fraggle@gmail.com>
date: Thu Jul 9 13:50:04 EDT 2009

Merge from trunk.

Subversion-branch: /branches/raven-branch
Subversion-revision: 1610

diff: cannot open b/pkg/wince//null: file does not exist: 'b/pkg/wince//null' diff: cannot open b/pkg//null: file does not exist: 'b/pkg//null'
--- a/NEWS
+++ b/NEWS
@@ -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
--- a/configure.in
+++ b/configure.in
@@ -113,6 +113,7 @@
 src/hexen/Makefile
 src/setup/Makefile
 pcsound/Makefile
+pkg/wince/Makefile
 src/resource.rc
 src/setup-res.rc
 src/setup/setup-manifest.xml
--- /dev/null
+++ b/pkg/wince/.gitignore
@@ -1,0 +1,3 @@
+Makefile
+Makefile.in
+*.cab
--- /dev/null
+++ b/pkg/wince/Makefile.am
@@ -1,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 $< $@
+
--- /dev/null
+++ b/pkg/wince/wince-cab.cfg
@@ -1,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"
+}
+
--- /dev/null
+++ b/pkg/wince/wince-cabgen
@@ -1,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])
+
--- 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 @@
 {
 }
 
-int  I_GetHeapSize (void)
+byte *I_ZoneBase (int *size)
 {
+    byte *zonemem;
+    int min_ram = MIN_RAM;
     int p;
 
     //!
@@ -97,28 +101,46 @@
     //
 
     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;
+    // 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.
 
-    *size = I_GetHeapSize();
+    zonemem = NULL;
 
-    zonemem = malloc(*size);
-
-    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);
 
--- a/src/i_video.c
+++ b/src/i_video.c
@@ -662,13 +662,20 @@
                 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;
 
                 /*
--- 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 @@
     char *tempdir;
 
 #ifdef _WIN32
-
     // Check the TEMP environment variable to find the location.
 
     tempdir = getenv("TEMP");
@@ -134,28 +135,131 @@
     fprintf(context->stream, "\n");
 }
 
-#if defined(_WIN32_WCE)
+#if defined(_WIN32)
 
-static int ExecuteCommand(const char **argv)
+// Wait for the specified process to exit.  Returns the exit code.
+
+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;
 
-static int ExecuteCommand(const char **argv)
+    // 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);
+    }
+
+    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)
 {
-    return _spawnv(_P_WAIT, argv[0], argv);
+    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)
+{
+    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 @@
 
 int ExecuteDoom(execute_context_t *context)
 {
-    const char *argv[3];
     char *response_file_arg;
     int result;
     
@@ -200,17 +303,13 @@
     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 @@
 
     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);