Browse Source

Initial check-in.

Wei Mingzhi 11 years ago
commit
8d4be3267d
84 changed files with 38270 additions and 0 deletions
  1. 16 0
      CREDITS.txt
  2. 28 0
      Info.plist
  3. 30 0
      Makefile
  4. 28 0
      Makefile.cygwin
  5. 24 0
      Makefile.dingux
  6. 35 0
      Makefile.gph
  7. 26 0
      Makefile.psp
  8. 25 0
      Makefile.wii
  9. 88 0
      README.txt
  10. 383 0
      SDLMain.m
  11. 11 0
      SDLMain_Cocoa.h
  12. 136 0
      ascii.h
  13. 1462 0
      battle.c
  14. 238 0
      battle.h
  15. 183 0
      common.h
  16. 400 0
      ending.c
  17. 55 0
      ending.h
  18. 4972 0
      fight.c
  19. 107 0
      fight.h
  20. 290 0
      font.c
  21. 62 0
      font.h
  22. 132 0
      game.c
  23. 34 0
      game.h
  24. 127 0
      getopt.c
  25. 63 0
      getopt.h
  26. 1886 0
      global.c
  27. 766 0
      global.h
  28. 674 0
      gpl.txt
  29. 821 0
      input.c
  30. 86 0
      input.h
  31. 394 0
      input_PSP.c
  32. 429 0
      itemmenu.c
  33. 48 0
      itemmenu.h
  34. 432 0
      magicmenu.c
  35. 51 0
      magicmenu.h
  36. 594 0
      main.c
  37. 60 0
      main.h
  38. 107 0
      main_PSP.h
  39. 417 0
      map.c
  40. 138 0
      map.h
  41. 122 0
      midi.c
  42. 48 0
      midi.h
  43. 45 0
      pal_s60-10.cpp
  44. 923 0
      palcommon.c
  45. 145 0
      palcommon.h
  46. 652 0
      palette.c
  47. 85 0
      palette.h
  48. 623 0
      play.c
  49. 58 0
      play.h
  50. 3 0
      private.c
  51. 429 0
      res.c
  52. 79 0
      res.h
  53. 383 0
      rixplay.cpp
  54. 59 0
      rixplay.h
  55. 481 0
      rngplay.c
  56. 43 0
      rngplay.h
  57. 877 0
      scene.c
  58. 66 0
      scene.h
  59. 3425 0
      script.c
  60. 47 0
      script.h
  61. 154 0
      sdlpal.cproj
  62. 1203 0
      sdlpal.dev
  63. 576 0
      sdlpal.dsp
  64. BIN
      sdlpal.icns
  65. BIN
      sdlpal.ico
  66. 1 0
      sdlpal.rc
  67. 2808 0
      sdlpal.tgt
  68. 43 0
      sdlpal.wpj
  69. 64 0
      sdlpal.xpm
  70. 729 0
      sound.c
  71. 86 0
      sound.h
  72. 988 0
      text.c
  73. 101 0
      text.h
  74. 797 0
      ui.c
  75. 232 0
      ui.h
  76. 1777 0
      uibattle.c
  77. 152 0
      uibattle.h
  78. 1918 0
      uigame.c
  79. 94 0
      uigame.h
  80. 488 0
      util.c
  81. 133 0
      util.h
  82. 869 0
      video.c
  83. 111 0
      video.h
  84. 495 0
      yj1.c

+ 16 - 0
CREDITS.txt

@@ -0,0 +1,16 @@
+SPECIAL THANKS
+--------------
+
+Many of the ideas of this program are based on documents from PAL
+Research Project (http://code.google.com/p/palresearch).
+
+Portions of the code are based on the work done by Baldur and louyihua.
+
+getopt.c file is based on source code in OpenBSD.
+
+This program made extensive use of the following libraries:
+
+SDL (http://www.libsdl.org/)
+Adplug (http://adplug.sourceforge.net/)
+SDL_mixer (http://www.libsdl.org/projects/SDL_mixer/)
+libmad (http://www.underbit.com/products/mad/)

+ 28 - 0
Info.plist

@@ -0,0 +1,28 @@
+<?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>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleIconFile</key>
+	<string>sdlpal.icns</string>
+	<key>CFBundleIdentifier</key>
+	<string>com.weimingzhi.Pal</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1.0</string>
+	<key>NSMainNibFile</key>
+	<string>SDLMain</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+</dict>
+</plist>

+ 30 - 0
Makefile

@@ -0,0 +1,30 @@
+# Adapted from Makefile for Dingux by Rikku2000
+
+TARGET = sdlpal
+
+HOST =
+
+ADPLUG_FILES = adplug/rix.cpp adplug/player.cpp adplug/binio.cpp \
+	adplug/fprovide.cpp adplug/binfile.cpp adplug/dosbox_opl.cpp \
+	adplug/fmopl.c adplug/surroundopl.cpp adplug/emuopl.cpp
+
+LIBMAD_FILES = libmad/bit.c libmad/decoder.c libmad/fixed.c libmad/frame.c \
+	libmad/huffman.c libmad/layer12.c libmad/layer3.c libmad/music_mad.c \
+	libmad/stream.c libmad/synth.c libmad/timer.c
+
+FILES = rixplay.cpp text.c font.c itemmenu.c scene.c palcommon.c script.c \
+	util.c play.c getopt.c input.c uibattle.c game.c magicmenu.c map.c \
+	ending.c uigame.c rngplay.c ui.c global.c main.c fight.c \
+	video.c palette.c sound.c res.c battle.c yj1.c
+
+FILES += $(ADPLUG_FILES)
+FILES += $(LIBMAD_FILES)
+
+CFLAGS = `sdl-config --cflags` -g -Wall -O2 -fno-strict-aliasing
+LDFLAGS = `sdl-config --libs` -lstdc++ -lm
+
+$(TARGET):
+	$(HOST)gcc $(CFLAGS) -o $(TARGET) $(FILES) $(LDFLAGS)
+
+clean:
+	rm -f $(TARGET)

+ 28 - 0
Makefile.cygwin

@@ -0,0 +1,28 @@
+TARGET = sdlpal.exe
+
+HOST = i686-pc-cygwin-
+
+SOURCES = . ./adplug
+CFILES = $(foreach dir, $(SOURCES), $(wildcard $(dir)/*.c))
+CPPFILES = $(foreach dir, $(SOURCES), $(wildcard $(dir)/*.cpp))
+RCFILES = $(foreach dir, $(SOURCES), $(wildcard $(dir)/*.rc))
+OFILES = $(CFILES:.c=.o) $(CPPFILES:.cpp=.o) $(RCFILES:.rc=.o)
+
+CFLAGS = -g -Wall -O2 `sdl-config --cflags` -DCYGWIN -DPAL_CLASSIC
+CXXFLAGS = $(CFLAGS)
+LDFLAGS = `sdl-config --libs`
+
+$(TARGET): $(OFILES)
+	$(HOST)g++ $(OFILES) -o $@ $(LDFLAGS)
+
+%.o: %.c
+	$(HOST)gcc $(CFLAGS) -c $< -o $@
+
+%.o: %.cpp
+	$(HOST)g++ $(CXXFLAGS) -c $< -o $@
+
+%.o: %.rc
+	windres.exe -i $< --input-format=rc -o $@ -O coff
+
+clean:
+	rm -f $(TARGET) $(OFILES)

+ 24 - 0
Makefile.dingux

@@ -0,0 +1,24 @@
+TARGET = sdlpal.dge
+
+HOST = mipsel-linux-uclibc-
+
+SOURCES = . ./adplug
+CFILES = $(foreach dir, $(SOURCES), $(wildcard $(dir)/*.c))
+CPPFILES = $(foreach dir, $(SOURCES), $(wildcard $(dir)/*.cpp))
+OFILES = $(CFILES:.c=.o) $(CPPFILES:.cpp=.o)
+
+CFLAGS = -g -Wall -O2 `sdl-config --cflags` -DDINGOO -DPAL_CLASSIC
+CXXFLAGS = $(CFLAGS)
+LDFLAGS = `sdl-config --libs`
+
+$(TARGET): $(OFILES)
+	$(HOST)g++ $(OFILES) -o $@ $(LDFLAGS)
+
+%.o: %.c
+	$(HOST)gcc $(CFLAGS) -c $< -o $@
+
+%.o: %.cpp
+	$(HOST)g++ $(CXXFLAGS) -c $< -o $@
+
+clean:
+	rm -f $(TARGET) $(OFILES)

+ 35 - 0
Makefile.gph

@@ -0,0 +1,35 @@
+BUILD_GP2XWIZ = NO
+BUILD_CAANOO = YES
+
+TARGET = sdlpal.gpe
+
+ifeq ($(BUILD_GP2XWIZ), YES)
+HOST = arm-open2x-linux-
+DEVICE = -DGP2XWIZ
+else
+ifeq ($(BUILD_CAANOO), YES)
+HOST = arm-gph-linux-gnueabi-
+DEVICE = -DCAANOO
+endif
+endif
+
+SOURCES = . ./adplug
+CFILES = $(foreach dir, $(SOURCES), $(wildcard $(dir)/*.c))
+CPPFILES = $(foreach dir, $(SOURCES), $(wildcard $(dir)/*.cpp))
+OFILES = $(CFILES:.c=.o) $(CPPFILES:.cpp=.o)
+
+CFLAGS = -g -Wall -O2 `sdl-config --cflags` -DGPH $(DEVICE) -DPAL_CLASSIC
+CXXFLAGS = $(CFLAGS)
+LDFLAGS = `sdl-config --libs`
+
+$(TARGET): $(OFILES)
+	$(HOST)g++ $(OFILES) -o $@ $(LDFLAGS)
+
+%.o: %.c
+	$(HOST)gcc $(CFLAGS) -c $< -o $@
+
+%.o: %.cpp
+	$(HOST)g++ $(CXXFLAGS) -c $< -o $@
+
+clean:
+	rm -f $(TARGET) $(OFILES)

+ 26 - 0
Makefile.psp

@@ -0,0 +1,26 @@
+TARGET = sdlpal
+
+OBJS = adplug/binfile.o adplug/emuopl.o adplug/fprovide.o adplug/rix.o \
+	adplug/binio.o adplug/fmopl.o adplug/player.o \
+	battle.o getopt.o main.o play.o script.o uigame.o ending.o \
+	global.o map.o res.o sound.o util.o fight.o input_PSP.o palcommon.o \
+	rixplay.o text.o video.o font.o itemmenu.o palette.o rngplay.o \
+	uibattle.o yj1.o game.o magicmenu.o scene.o ui.o
+
+PSPSDK = $(shell psp-config --pspsdk-path)
+
+INCDIR = 
+CFLAGS = -O3 -G0 -Wall -g `$(PSPSDK)/../bin/sdl-config --cflags` -DPSP=1
+CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti `$(PSPSDK)/../bin/sdl-config --cflags`
+ASFLAGS = $(CFLAGS)
+
+LIBDIR =
+LIBS = `$(PSPSDK)/../bin/sdl-config --libs` -lstdc++
+
+EXTRA_TARGETS = EBOOT.PBP
+PSP_EBOOT_TITLE = sdlpal
+
+BUILD_PRX = 1
+PSP_FW_VERSION = 371
+
+include $(PSPSDK)/lib/build.mak

+ 25 - 0
Makefile.wii

@@ -0,0 +1,25 @@
+TARGET = boot
+
+HOST = powerpc-eabi-
+
+SOURCES = . ./adplug
+CFILES = $(foreach dir, $(SOURCES), $(wildcard $(dir)/*.c))
+CPPFILES = $(foreach dir, $(SOURCES), $(wildcard $(dir)/*.cpp))
+OFILES = $(CFILES:.c=.o) $(CPPFILES:.cpp=.o)
+
+CFLAGS = -g -Wall -O2 `sdl-config --cflags` -DGEKKO -DPAL_CLASSIC
+CXXFLAGS = $(CFLAGS)
+LDFLAGS = `sdl-config --libs` -DGEKKO -mrvl
+
+$(TARGET): $(OFILES)
+	$(HOST)g++ $(OFILES) -o $@.elf $(LDFLAGS)
+	elf2dol $(TARGET).elf $(TARGET).dol
+
+%.o: %.c
+	$(HOST)gcc $(CFLAGS) -c $< -o $@
+
+%.o: %.cpp
+	$(HOST)g++ $(CXXFLAGS) -c $< -o $@
+
+clean:
+	rm -f $(TARGET).elf $(TARGET).dol $(OFILES)

+ 88 - 0
README.txt

@@ -0,0 +1,88 @@
+SDLPAL
+======
+
+SDLPAL is an SDL-based reimplementation of the classic Chinese-language RPG
+"Xian Jian Qi Xia Zhuan" (also known as PAL or Legend of Sword and Fairy).
+
+
+LICENSE
+=======
+
+SDLPAL is Copyrighted (c) 2009-2011 Wei Mingzhi <whistler_wmz@users.sf.net>.
+All rights reserved.
+
+SDLPAL is distributed under the terms of GNU General Public License, version 3
+(or any later version) as published by the Free Software Foundation. See
+gpl.txt for details.
+
+Also see credits.txt for additional credits.
+
+This program does NOT include any code or data files of the original game,
+which is proprietary and copyrighted by SoftStar Inc.
+
+
+COMPILE UNDER WINDOWS
+=====================
+
+The following compilers/IDEs are supported under Windows:
+
+1) Microsoft Visual C++ 6.0 or higher
+2) Dev-C++ 4.9.9.2
+3) Open Watcom 1.7
+
+To compile, open the respective project file (sdlpal.dsp, sdlpal.dev, or
+sdlpal.wpj). You need to have SDL development files installed.
+
+
+COMPILE UNDER GNU/LINUX
+=======================
+
+To compile, type:
+
+make
+
+You need to have SDL development files installed. The compiled executable
+should be generated with the filename 'sdlpal' at the top directory of source
+files.
+
+SDLPAL should also be able to run under other UNIX-like systems, however
+it's not tested.
+
+
+COMPILE UNDER MAC OS X
+======================
+
+To compile, open the project Pal.xcodeproj with Xcode, and click Build. You
+need to have SDL framework installed at /Library/Frameworks.
+
+The compiled bundle should work as a "universal" binary which works on both
+Intel and PowerPC.
+
+
+CLASSIC BUILD
+=============
+
+By default, SDLPAL uses a revised battle system which is more exciting yet
+somewhat harder than the original game. If you prefer the traditional
+turn-based battle system, uncomment the following line:
+
+//#define PAL_CLASSIC           1
+
+in the file common.h and recompile. This will build a "classic" build which is
+100% the same as the original game.
+
+
+RUNNING THE GAME
+================
+
+The data files required for running the game are not included with the source
+package due to copyright issues. You must obtain them from the original CD.
+
+To run the game, copy all the files on the original CD to a directory, then
+copy the SDLPAL executable to the same directory, and run the executable.
+
+Note that the filenames of data files should be in lower-case under GNU/Linux
+(or other UNIX-like operating systems).
+
+
+-END OF FILE-

+ 383 - 0
SDLMain.m

@@ -0,0 +1,383 @@
+/*   SDLMain.m - main entry point for our Cocoa-ized SDL app
+       Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
+       Non-NIB-Code & other changes: Max Horn <max@quendi.de>
+
+    Feel free to customize this file to suit your needs
+*/
+
+#import "SDL.h"
+#import "SDLMain_Cocoa.h"
+#import <sys/param.h> /* for MAXPATHLEN */
+#import <unistd.h>
+
+/* For some reaon, Apple removed setAppleMenu from the headers in 10.4,
+ but the method still is there and works. To avoid warnings, we declare
+ it ourselves here. */
+@interface NSApplication(SDL_Missing_Methods)
+- (void)setAppleMenu:(NSMenu *)menu;
+@end
+
+/* Use this flag to determine whether we use SDLMain.nib or not */
+#define		SDL_USE_NIB_FILE	0
+
+/* Use this flag to determine whether we use CPS (docking) or not */
+#define		SDL_USE_CPS		1
+#ifdef SDL_USE_CPS
+/* Portions of CPS.h */
+typedef struct CPSProcessSerNum
+{
+	UInt32		lo;
+	UInt32		hi;
+} CPSProcessSerNum;
+
+extern OSErr	CPSGetCurrentProcess( CPSProcessSerNum *psn);
+extern OSErr 	CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
+extern OSErr	CPSSetFrontProcess( CPSProcessSerNum *psn);
+
+#endif /* SDL_USE_CPS */
+
+static int    gArgc;
+static char  **gArgv;
+static BOOL   gFinderLaunch;
+static BOOL   gCalledAppMainline = FALSE;
+
+static NSString *getApplicationName(void)
+{
+    NSDictionary *dict;
+    NSString *appName = 0;
+
+    /* Determine the application name */
+    dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
+    if (dict)
+        appName = [dict objectForKey: @"CFBundleName"];
+    
+    if (![appName length])
+        appName = [[NSProcessInfo processInfo] processName];
+
+    return appName;
+}
+
+#if SDL_USE_NIB_FILE
+/* A helper category for NSString */
+@interface NSString (ReplaceSubString)
+- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString;
+@end
+#endif
+
+@interface SDLApplication : NSApplication
+@end
+
+@implementation SDLApplication
+/* Invoked from the Quit menu item */
+- (void)terminate:(id)sender
+{
+    /* Post a SDL_QUIT event */
+    SDL_Event event;
+    event.type = SDL_QUIT;
+    SDL_PushEvent(&event);
+}
+@end
+
+/* The main class of the application, the application's delegate */
+@implementation SDLMain
+
+/* Set the working directory to the .app's parent directory */
+- (void) setupWorkingDirectory:(BOOL)shouldChdir
+{
+    if (shouldChdir)
+    {
+        char parentdir[MAXPATHLEN];
+		CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
+		CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
+		if (CFURLGetFileSystemRepresentation(url2, true, (UInt8 *)parentdir, MAXPATHLEN)) {
+	        assert ( chdir (parentdir) == 0 );   /* chdir to the binary app's parent */
+		}
+		CFRelease(url);
+		CFRelease(url2);
+	}
+
+}
+
+#if SDL_USE_NIB_FILE
+
+/* Fix menu to contain the real app name instead of "SDL App" */
+- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName
+{
+    NSRange aRange;
+    NSEnumerator *enumerator;
+    NSMenuItem *menuItem;
+
+    aRange = [[aMenu title] rangeOfString:@"SDL App"];
+    if (aRange.length != 0)
+        [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]];
+
+    enumerator = [[aMenu itemArray] objectEnumerator];
+    while ((menuItem = [enumerator nextObject]))
+    {
+        aRange = [[menuItem title] rangeOfString:@"SDL App"];
+        if (aRange.length != 0)
+            [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]];
+        if ([menuItem hasSubmenu])
+            [self fixMenu:[menuItem submenu] withAppName:appName];
+    }
+    [ aMenu sizeToFit ];
+}
+
+#else
+
+static void setApplicationMenu(void)
+{
+    /* warning: this code is very odd */
+    NSMenu *appleMenu;
+    NSMenuItem *menuItem;
+    NSString *title;
+    NSString *appName;
+    
+    appName = getApplicationName();
+    appleMenu = [[NSMenu alloc] initWithTitle:@""];
+    
+    /* Add menu items */
+    title = [@"About " stringByAppendingString:appName];
+    [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
+
+    [appleMenu addItem:[NSMenuItem separatorItem]];
+
+    title = [@"Hide " stringByAppendingString:appName];
+    [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
+
+    menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
+    [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
+
+    [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
+
+    [appleMenu addItem:[NSMenuItem separatorItem]];
+
+    title = [@"Quit " stringByAppendingString:appName];
+    [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
+
+    
+    /* Put menu into the menubar */
+    menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
+    [menuItem setSubmenu:appleMenu];
+    [[NSApp mainMenu] addItem:menuItem];
+
+    /* Tell the application object that this is now the application menu */
+    [NSApp setAppleMenu:appleMenu];
+
+    /* Finally give up our references to the objects */
+    [appleMenu release];
+    [menuItem release];
+}
+
+/* Create a window menu */
+static void setupWindowMenu(void)
+{
+    NSMenu      *windowMenu;
+    NSMenuItem  *windowMenuItem;
+    NSMenuItem  *menuItem;
+
+    windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
+    
+    /* "Minimize" item */
+    menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
+    [windowMenu addItem:menuItem];
+    [menuItem release];
+    
+    /* Put menu into the menubar */
+    windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
+    [windowMenuItem setSubmenu:windowMenu];
+    [[NSApp mainMenu] addItem:windowMenuItem];
+    
+    /* Tell the application object that this is now the window menu */
+    [NSApp setWindowsMenu:windowMenu];
+
+    /* Finally give up our references to the objects */
+    [windowMenu release];
+    [windowMenuItem release];
+}
+
+/* Replacement for NSApplicationMain */
+static void CustomApplicationMain (int argc, char **argv)
+{
+    NSAutoreleasePool	*pool = [[NSAutoreleasePool alloc] init];
+    SDLMain				*sdlMain;
+
+    /* Ensure the application object is initialised */
+    [SDLApplication sharedApplication];
+    
+#ifdef SDL_USE_CPS
+    {
+        CPSProcessSerNum PSN;
+        /* Tell the dock about us */
+        if (!CPSGetCurrentProcess(&PSN))
+            if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
+                if (!CPSSetFrontProcess(&PSN))
+                    [SDLApplication sharedApplication];
+    }
+#endif /* SDL_USE_CPS */
+
+    /* Set up the menubar */
+    [NSApp setMainMenu:[[NSMenu alloc] init]];
+    setApplicationMenu();
+    setupWindowMenu();
+
+    /* Create SDLMain and make it the app delegate */
+    sdlMain = [[SDLMain alloc] init];
+    [NSApp setDelegate:sdlMain];
+    
+    /* Start the main event loop */
+    [NSApp run];
+    
+    [sdlMain release];
+    [pool release];
+}
+
+#endif
+
+
+/*
+ * Catch document open requests...this lets us notice files when the app
+ *  was launched by double-clicking a document, or when a document was
+ *  dragged/dropped on the app's icon. You need to have a
+ *  CFBundleDocumentsType section in your Info.plist to get this message,
+ *  apparently.
+ *
+ * Files are added to gArgv, so to the app, they'll look like command line
+ *  arguments. Previously, apps launched from the finder had nothing but
+ *  an argv[0].
+ *
+ * This message may be received multiple times to open several docs on launch.
+ *
+ * This message is ignored once the app's mainline has been called.
+ */
+- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
+{
+    const char *temparg;
+    size_t arglen;
+    char *arg;
+    char **newargv;
+
+    if (!gFinderLaunch)  /* MacOS is passing command line args. */
+        return FALSE;
+
+    if (gCalledAppMainline)  /* app has started, ignore this document. */
+        return FALSE;
+
+    temparg = [filename UTF8String];
+    arglen = SDL_strlen(temparg) + 1;
+    arg = (char *) SDL_malloc(arglen);
+    if (arg == NULL)
+        return FALSE;
+
+    newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2));
+    if (newargv == NULL)
+    {
+        SDL_free(arg);
+        return FALSE;
+    }
+    gArgv = newargv;
+
+    SDL_strlcpy(arg, temparg, arglen);
+    gArgv[gArgc++] = arg;
+    gArgv[gArgc] = NULL;
+    return TRUE;
+}
+
+
+/* Called when the internal event loop has just started running */
+- (void) applicationDidFinishLaunching: (NSNotification *) note
+{
+    int status;
+
+    /* Set the working directory to the .app's parent directory */
+    [self setupWorkingDirectory:gFinderLaunch];
+
+#if SDL_USE_NIB_FILE
+    /* Set the main menu to contain the real app name instead of "SDL App" */
+    [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()];
+#endif
+
+    /* Hand off to main application code */
+    gCalledAppMainline = TRUE;
+    status = SDL_main (gArgc, gArgv);
+
+    /* We're done, thank you for playing */
+    exit(status);
+}
+@end
+
+
+@implementation NSString (ReplaceSubString)
+
+- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
+{
+    unsigned int bufferSize;
+    unsigned int selfLen = [self length];
+    unsigned int aStringLen = [aString length];
+    unichar *buffer;
+    NSRange localRange;
+    NSString *result;
+
+    bufferSize = selfLen + aStringLen - aRange.length;
+    buffer = NSAllocateMemoryPages(bufferSize*sizeof(unichar));
+    
+    /* Get first part into buffer */
+    localRange.location = 0;
+    localRange.length = aRange.location;
+    [self getCharacters:buffer range:localRange];
+    
+    /* Get middle part into buffer */
+    localRange.location = 0;
+    localRange.length = aStringLen;
+    [aString getCharacters:(buffer+aRange.location) range:localRange];
+     
+    /* Get last part into buffer */
+    localRange.location = aRange.location + aRange.length;
+    localRange.length = selfLen - localRange.location;
+    [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
+    
+    /* Build output string */
+    result = [NSString stringWithCharacters:buffer length:bufferSize];
+    
+    NSDeallocateMemoryPages(buffer, bufferSize);
+    
+    return result;
+}
+
+@end
+
+
+
+#ifdef main
+#  undef main
+#endif
+
+
+/* Main entry point to executable - should *not* be SDL_main! */
+int main (int argc, char **argv)
+{
+    /* Copy the arguments into a global variable */
+    /* This is passed if we are launched by double-clicking */
+    if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
+        gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
+        gArgv[0] = argv[0];
+        gArgv[1] = NULL;
+        gArgc = 1;
+        gFinderLaunch = YES;
+    } else {
+        int i;
+        gArgc = argc;
+        gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
+        for (i = 0; i <= argc; i++)
+            gArgv[i] = argv[i];
+        gFinderLaunch = NO;
+    }
+
+#if SDL_USE_NIB_FILE
+    [SDLApplication poseAsClass:[NSApplication class]];
+    NSApplicationMain (argc, argv);
+#else
+    CustomApplicationMain (argc, argv);
+#endif
+    return 0;
+}

+ 11 - 0
SDLMain_Cocoa.h

@@ -0,0 +1,11 @@
+/*   SDLMain.m - main entry point for our Cocoa-ized SDL app
+       Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
+       Non-NIB-Code & other changes: Max Horn <max@quendi.de>
+
+    Feel free to customize this file to suit your needs
+*/
+
+#import <Cocoa/Cocoa.h>
+
+@interface SDLMain : NSObject
+@end

+ 136 - 0
ascii.h

@@ -0,0 +1,136 @@
+unsigned char iso_font[] =
+{
+   /*
+    * Copyright (c) 2000
+    * Ka-Ping Yee <ping@lfw.org>
+    * This font may be freely used for any purpose.
+    */
+   /* 00 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 01 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 02 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 03 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 04 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 05 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 06 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 07 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 08 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 09 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 0A */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 0B */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 0C */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 0D */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 0E */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 0F */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 10 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 11 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 12 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 13 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 14 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 15 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 16 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 17 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 18 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 19 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 1A */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 1B */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 1C */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 1D */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 1E */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 1F */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 20 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 21 */ 0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,
+   /* 22 */ 0x00,0x00,0x6c,0x6c,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 23 */ 0x00,0x00,0x00,0x36,0x36,0x7f,0x36,0x36,0x7f,0x36,0x36,0x00,0x00,0x00,0x00,
+   /* 24 */ 0x00,0x08,0x08,0x3e,0x6b,0x0b,0x0b,0x3e,0x68,0x68,0x6b,0x3e,0x08,0x08,0x00,
+   /* 25 */ 0x00,0x00,0x00,0x33,0x13,0x18,0x08,0x0c,0x04,0x06,0x32,0x33,0x00,0x00,0x00,
+   /* 26 */ 0x00,0x00,0x1c,0x36,0x36,0x1c,0x6c,0x3e,0x33,0x33,0x7b,0xce,0x00,0x00,0x00,
+   /* 27 */ 0x00,0x00,0x18,0x18,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 28 */ 0x00,0x00,0x30,0x18,0x18,0x0c,0x0c,0x0c,0x0c,0x0c,0x18,0x18,0x30,0x00,0x00,
+   /* 29 */ 0x00,0x00,0x0c,0x18,0x18,0x30,0x30,0x30,0x30,0x30,0x18,0x18,0x0c,0x00,0x00,
+   /* 2A */ 0x00,0x00,0x00,0x00,0x00,0x36,0x1c,0x7f,0x1c,0x36,0x00,0x00,0x00,0x00,0x00,
+   /* 2B */ 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x7e,0x18,0x18,0x00,0x00,0x00,0x00,0x00,
+   /* 2C */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x0c,0x00,0x00,
+   /* 2D */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 2E */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
+   /* 2F */ 0x00,0x00,0x60,0x20,0x30,0x10,0x18,0x08,0x0c,0x04,0x06,0x02,0x03,0x00,0x00,
+   /* 30 */ 0x00,0x00,0x3e,0x63,0x63,0x63,0x6b,0x6b,0x63,0x63,0x63,0x3e,0x00,0x00,0x00,
+   /* 31 */ 0x00,0x00,0x18,0x1e,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,
+   /* 32 */ 0x00,0x00,0x3e,0x63,0x60,0x60,0x30,0x18,0x0c,0x06,0x03,0x7f,0x00,0x00,0x00,
+   /* 33 */ 0x00,0x00,0x3e,0x63,0x60,0x60,0x3c,0x60,0x60,0x60,0x63,0x3e,0x00,0x00,0x00,
+   /* 34 */ 0x00,0x00,0x30,0x38,0x3c,0x36,0x33,0x7f,0x30,0x30,0x30,0x30,0x00,0x00,0x00,
+   /* 35 */ 0x00,0x00,0x7f,0x03,0x03,0x3f,0x60,0x60,0x60,0x60,0x63,0x3e,0x00,0x00,0x00,
+   /* 36 */ 0x00,0x00,0x3c,0x06,0x03,0x03,0x3f,0x63,0x63,0x63,0x63,0x3e,0x00,0x00,0x00,
+   /* 37 */ 0x00,0x00,0x7f,0x60,0x30,0x30,0x18,0x18,0x18,0x0c,0x0c,0x0c,0x00,0x00,0x00,
+   /* 38 */ 0x00,0x00,0x3e,0x63,0x63,0x63,0x3e,0x63,0x63,0x63,0x63,0x3e,0x00,0x00,0x00,
+   /* 39 */ 0x00,0x00,0x3e,0x63,0x63,0x63,0x7e,0x60,0x60,0x60,0x30,0x1e,0x00,0x00,0x00,
+   /* 3A */ 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
+   /* 3B */ 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x0c,0x00,0x00,
+   /* 3C */ 0x00,0x00,0x60,0x30,0x18,0x0c,0x06,0x06,0x0c,0x18,0x30,0x60,0x00,0x00,0x00,
+   /* 3D */ 0x00,0x00,0x00,0x00,0x00,0x7e,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 3E */ 0x00,0x00,0x06,0x0c,0x18,0x30,0x60,0x60,0x30,0x18,0x0c,0x06,0x00,0x00,0x00,
+   /* 3F */ 0x00,0x00,0x3e,0x63,0x60,0x30,0x30,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,
+   /* 40 */ 0x00,0x00,0x3c,0x66,0x73,0x7b,0x6b,0x6b,0x7b,0x33,0x06,0x3c,0x00,0x00,0x00,
+   /* 41 */ 0x00,0x00,0x3e,0x63,0x63,0x63,0x7f,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,
+   /* 42 */ 0x00,0x00,0x3f,0x63,0x63,0x63,0x3f,0x63,0x63,0x63,0x63,0x3f,0x00,0x00,0x00,
+   /* 43 */ 0x00,0x00,0x3c,0x66,0x03,0x03,0x03,0x03,0x03,0x03,0x66,0x3c,0x00,0x00,0x00,
+   /* 44 */ 0x00,0x00,0x1f,0x33,0x63,0x63,0x63,0x63,0x63,0x63,0x33,0x1f,0x00,0x00,0x00,
+   /* 45 */ 0x00,0x00,0x7f,0x03,0x03,0x03,0x3f,0x03,0x03,0x03,0x03,0x7f,0x00,0x00,0x00,
+   /* 46 */ 0x00,0x00,0x7f,0x03,0x03,0x03,0x3f,0x03,0x03,0x03,0x03,0x03,0x00,0x00,0x00,
+   /* 47 */ 0x00,0x00,0x3c,0x66,0x03,0x03,0x03,0x73,0x63,0x63,0x66,0x7c,0x00,0x00,0x00,
+   /* 48 */ 0x00,0x00,0x63,0x63,0x63,0x63,0x7f,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,
+   /* 49 */ 0x00,0x00,0x3c,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,
+   /* 4A */ 0x00,0x00,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x33,0x1e,0x00,0x00,0x00,
+   /* 4B */ 0x00,0x00,0x63,0x33,0x1b,0x0f,0x07,0x07,0x0f,0x1b,0x33,0x63,0x00,0x00,0x00,
+   /* 4C */ 0x00,0x00,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x7f,0x00,0x00,0x00,
+   /* 4D */ 0x00,0x00,0x63,0x63,0x77,0x7f,0x7f,0x6b,0x6b,0x63,0x63,0x63,0x00,0x00,0x00,
+   /* 4E */ 0x00,0x00,0x63,0x63,0x67,0x6f,0x6f,0x7b,0x7b,0x73,0x63,0x63,0x00,0x00,0x00,
+   /* 4F */ 0x00,0x00,0x3e,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x3e,0x00,0x00,0x00,
+   /* 50 */ 0x00,0x00,0x3f,0x63,0x63,0x63,0x63,0x3f,0x03,0x03,0x03,0x03,0x00,0x00,0x00,
+   /* 51 */ 0x00,0x00,0x3e,0x63,0x63,0x63,0x63,0x63,0x63,0x6f,0x7b,0x3e,0x30,0x60,0x00,
+   /* 52 */ 0x00,0x00,0x3f,0x63,0x63,0x63,0x63,0x3f,0x1b,0x33,0x63,0x63,0x00,0x00,0x00,
+   /* 53 */ 0x00,0x00,0x3e,0x63,0x03,0x03,0x0e,0x38,0x60,0x60,0x63,0x3e,0x00,0x00,0x00,
+   /* 54 */ 0x00,0x00,0x7e,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,
+   /* 55 */ 0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x3e,0x00,0x00,0x00,
+   /* 56 */ 0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x36,0x36,0x1c,0x1c,0x08,0x00,0x00,0x00,
+   /* 57 */ 0x00,0x00,0x63,0x63,0x6b,0x6b,0x6b,0x6b,0x7f,0x36,0x36,0x36,0x00,0x00,0x00,
+   /* 58 */ 0x00,0x00,0x63,0x63,0x36,0x36,0x1c,0x1c,0x36,0x36,0x63,0x63,0x00,0x00,0x00,
+   /* 59 */ 0x00,0x00,0xc3,0xc3,0x66,0x66,0x3c,0x3c,0x18,0x18,0x18,0x18,0x00,0x00,0x00,
+   /* 5A */ 0x00,0x00,0x7f,0x30,0x30,0x18,0x18,0x0c,0x0c,0x06,0x06,0x7f,0x00,0x00,0x00,
+   /* 5B */ 0x00,0x00,0x3c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x3c,0x00,0x00,0x00,
+   /* 5C */ 0x00,0x00,0x03,0x02,0x06,0x04,0x0c,0x08,0x18,0x10,0x30,0x20,0x60,0x00,0x00,
+   /* 5D */ 0x00,0x00,0x3c,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3c,0x00,0x00,0x00,
+   /* 5E */ 0x00,0x08,0x1c,0x36,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 5F */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,
+   /* 60 */ 0x00,0x00,0x0c,0x0c,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 61 */ 0x00,0x00,0x00,0x00,0x00,0x3e,0x60,0x7e,0x63,0x63,0x73,0x6e,0x00,0x00,0x00,
+   /* 62 */ 0x00,0x00,0x03,0x03,0x03,0x3b,0x67,0x63,0x63,0x63,0x67,0x3b,0x00,0x00,0x00,
+   /* 63 */ 0x00,0x00,0x00,0x00,0x00,0x3e,0x63,0x03,0x03,0x03,0x63,0x3e,0x00,0x00,0x00,
+   /* 64 */ 0x00,0x00,0x60,0x60,0x60,0x6e,0x73,0x63,0x63,0x63,0x73,0x6e,0x00,0x00,0x00,
+   /* 65 */ 0x00,0x00,0x00,0x00,0x00,0x3e,0x63,0x63,0x7f,0x03,0x63,0x3e,0x00,0x00,0x00,
+   /* 66 */ 0x00,0x00,0x3c,0x66,0x06,0x1f,0x06,0x06,0x06,0x06,0x06,0x06,0x00,0x00,0x00,
+   /* 67 */ 0x00,0x00,0x00,0x00,0x00,0x6e,0x73,0x63,0x63,0x63,0x73,0x6e,0x60,0x63,0x3e,
+   /* 68 */ 0x00,0x00,0x03,0x03,0x03,0x3b,0x67,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,
+   /* 69 */ 0x00,0x00,0x0c,0x0c,0x00,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x38,0x00,0x00,0x00,
+   /* 6A */ 0x00,0x00,0x30,0x30,0x00,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x33,0x1e,
+   /* 6B */ 0x00,0x00,0x03,0x03,0x03,0x63,0x33,0x1b,0x0f,0x1f,0x33,0x63,0x00,0x00,0x00,
+   /* 6C */ 0x00,0x00,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x38,0x00,0x00,0x00,
+   /* 6D */ 0x00,0x00,0x00,0x00,0x00,0x35,0x6b,0x6b,0x6b,0x6b,0x6b,0x6b,0x00,0x00,0x00,
+   /* 6E */ 0x00,0x00,0x00,0x00,0x00,0x3b,0x67,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,
+   /* 6F */ 0x00,0x00,0x00,0x00,0x00,0x3e,0x63,0x63,0x63,0x63,0x63,0x3e,0x00,0x00,0x00,
+   /* 70 */ 0x00,0x00,0x00,0x00,0x00,0x3b,0x67,0x63,0x63,0x63,0x67,0x3b,0x03,0x03,0x03,
+   /* 71 */ 0x00,0x00,0x00,0x00,0x00,0x6e,0x73,0x63,0x63,0x63,0x73,0x6e,0x60,0xe0,0x60,
+   /* 72 */ 0x00,0x00,0x00,0x00,0x00,0x3b,0x67,0x03,0x03,0x03,0x03,0x03,0x00,0x00,0x00,
+   /* 73 */ 0x00,0x00,0x00,0x00,0x00,0x3e,0x63,0x0e,0x38,0x60,0x63,0x3e,0x00,0x00,0x00,
+   /* 74 */ 0x00,0x00,0x00,0x0c,0x0c,0x3e,0x0c,0x0c,0x0c,0x0c,0x0c,0x38,0x00,0x00,0x00,
+   /* 75 */ 0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x73,0x6e,0x00,0x00,0x00,
+   /* 76 */ 0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x36,0x36,0x1c,0x1c,0x08,0x00,0x00,0x00,
+   /* 77 */ 0x00,0x00,0x00,0x00,0x00,0x63,0x6b,0x6b,0x6b,0x3e,0x36,0x36,0x00,0x00,0x00,
+   /* 78 */ 0x00,0x00,0x00,0x00,0x00,0x63,0x36,0x1c,0x1c,0x1c,0x36,0x63,0x00,0x00,0x00,
+   /* 79 */ 0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x36,0x36,0x1c,0x1c,0x0c,0x0c,0x06,0x03,
+   /* 7A */ 0x00,0x00,0x00,0x00,0x00,0x7f,0x60,0x30,0x18,0x0c,0x06,0x7f,0x00,0x00,0x00,
+   /* 7B */ 0x00,0x00,0x70,0x18,0x18,0x18,0x18,0x0e,0x18,0x18,0x18,0x18,0x70,0x00,0x00,
+   /* 7C */ 0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,
+   /* 7D */ 0x00,0x00,0x0e,0x18,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x0e,0x00,0x00,
+   /* 7E */ 0x00,0x00,0x00,0x00,0x00,0x00,0x6e,0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+   /* 7F */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+};

File diff suppressed because it is too large
+ 1462 - 0
battle.c


+ 238 - 0
battle.h

@@ -0,0 +1,238 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef BATTLE_H
+#define BATTLE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "global.h"
+#include "uibattle.h"
+
+#define       BATTLE_FPS               25
+#define       BATTLE_FRAME_TIME        (1000 / BATTLE_FPS)
+
+typedef enum tagBATTLERESULT
+{
+   kBattleResultWon        = 3,      // player won the battle
+   kBattleResultLost       = 1,      // player lost the battle
+   kBattleResultFleed      = 0xFFFF, // player fleed from the battle
+   kBattleResultTerminated = 0,      // battle terminated with scripts
+   kBattleResultOnGoing    = 1000,   // the battle is ongoing
+   kBattleResultPreBattle  = 1001,   // running pre-battle scripts
+   kBattleResultPause      = 1002,   // battle pause
+} BATTLERESULT;
+
+typedef enum tagFIGHTERSTATE
+{
+   kFighterWait,  // waiting time
+   kFighterCom,   // accepting command
+   kFighterAct,   // doing the actual move
+} FIGHTERSTATE;
+
+typedef enum tagBATTLEACTIONTYPE
+{
+   kBattleActionPass,          // do nothing
+   kBattleActionDefend,        // defend
+   kBattleActionAttack,        // physical attack
+   kBattleActionMagic,         // use magic
+   kBattleActionCoopMagic,     // use cooperative magic
+   kBattleActionFlee,          // flee from the battle
+   kBattleActionThrowItem,     // throw item onto enemy
+   kBattleActionUseItem,       // use item
+   kBattleActionAttackMate,    // attack teammate (confused only)
+} BATTLEACTIONTYPE;
+
+typedef struct tagBATTLEACTION
+{
+   BATTLEACTIONTYPE   ActionType;
+   WORD               wActionID;   // item/magic to use
+   SHORT              sTarget;     // -1 for everyone
+   FLOAT              flRemainingTime;  // remaining waiting time before the action start
+} BATTLEACTION;
+
+typedef struct tagBATTLEENEMY
+{
+   WORD               wObjectID;              // Object ID of this enemy
+   ENEMY              e;                      // detailed data of this enemy
+   WORD               rgwStatus[kStatusAll];  // status effects
+   FLOAT              flTimeMeter;            // time-charging meter (0 = empty, 100 = full).
+   POISONSTATUS       rgPoisons[MAX_POISONS]; // poisons
+   LPSPRITE           lpSprite;
+   PAL_POS            pos;                    // current position on the screen
+   PAL_POS            posOriginal;            // original position on the screen
+   WORD               wCurrentFrame;          // current frame number
+   FIGHTERSTATE       state;                  // state of this enemy
+
+   BOOL               fTurnStart;
+   BOOL               fFirstMoveDone;
+   BOOL               fDualMove;
+
+   WORD               wScriptOnTurnStart;
+   WORD               wScriptOnBattleEnd;
+   WORD               wScriptOnReady;
+
+   WORD               wPrevHP;              // HP value prior to action
+
+   INT                iColorShift;
+} BATTLEENEMY;
+
+// We only put some data used in battle here; other data can be accessed in the global data.
+typedef struct tagBATTLEPLAYER
+{
+   INT                iColorShift;
+   FLOAT              flTimeMeter;          // time-charging meter (0 = empty, 100 = full).
+   FLOAT              flTimeSpeedModifier;
+   WORD               wHidingTime;          // remaining hiding time
+   LPSPRITE           lpSprite;
+   PAL_POS            pos;                  // current position on the screen
+   PAL_POS            posOriginal;          // original position on the screen
+   WORD               wCurrentFrame;        // current frame number
+   FIGHTERSTATE       state;                // state of this player
+   BATTLEACTION       action;               // action to perform
+   BOOL               fDefending;           // TRUE if player is defending
+   WORD               wPrevHP;              // HP value prior to action
+   WORD               wPrevMP;              // MP value prior to action
+#ifndef PAL_CLASSIC
+   SHORT              sTurnOrder;           // turn order
+#endif
+} BATTLEPLAYER;
+
+typedef struct tagSUMMON
+{
+   LPSPRITE           lpSprite;
+   WORD               wCurrentFrame;
+} SUMMON;
+
+#define MAX_BATTLE_ACTIONS    256
+#define MAX_KILLED_ENEMIES    256
+
+#ifdef PAL_CLASSIC
+
+typedef enum tabBATTLEPHASE
+{
+   kBattlePhaseSelectAction,
+   kBattlePhasePerformAction
+} BATTLEPHASE;
+
+typedef struct tagACTIONQUEUE
+{
+   BOOL       fIsEnemy;
+   WORD       wDexterity;
+   WORD       wIndex;
+} ACTIONQUEUE;
+
+#define MAX_ACTIONQUEUE_ITEMS (MAX_PLAYERS_IN_PARTY + MAX_ENEMIES_IN_TEAM * 2)
+
+#endif
+
+typedef struct tagBATTLE
+{
+   BATTLEPLAYER     rgPlayer[MAX_PLAYERS_IN_PARTY];
+   BATTLEENEMY      rgEnemy[MAX_ENEMIES_IN_TEAM];
+
+   WORD             wMaxEnemyIndex;
+
+   SDL_Surface     *lpSceneBuf;
+   SDL_Surface     *lpBackground;
+
+   SHORT            sBackgroundColorShift;
+
+   LPSPRITE         lpSummonSprite;       // sprite of summoned god
+   PAL_POS          posSummon;
+   INT              iSummonFrame;         // current frame of the summoned god
+
+   INT              iExpGained;           // total experience value gained
+   INT              iCashGained;          // total cash gained
+
+   BOOL             fIsBoss;              // TRUE if boss fight
+   BOOL             fEnemyCleared;        // TRUE if enemies are cleared
+   BATTLERESULT     BattleResult;
+
+   FLOAT            flTimeChargingUnit;   // the base waiting time unit
+
+   BATTLEUI         UI;
+
+   LPBYTE           lpEffectSprite;
+
+   BOOL             fEnemyMoving;         // TRUE if enemy is moving
+
+   INT              iHidingTime;          // Time of hiding
+
+   WORD             wMovingPlayerIndex;   // current moving player index
+
+   int              iBlow;
+
+#ifdef PAL_CLASSIC
+   BATTLEPHASE      Phase;
+   ACTIONQUEUE      ActionQueue[MAX_ACTIONQUEUE_ITEMS];
+   int              iCurAction;
+   BOOL             fRepeat;              // TRUE if player pressed Repeat
+   BOOL             fForce;               // TRUE if player pressed Force
+   BOOL             fFlee;                // TRUE if player pressed Flee
+#endif
+} BATTLE;
+
+extern BATTLE g_Battle;
+
+VOID
+PAL_LoadBattleSprites(
+   VOID
+);
+
+VOID
+PAL_BattleMakeScene(
+   VOID
+);
+
+VOID
+PAL_BattleBackupScene(
+   VOID
+);
+
+VOID
+PAL_BattleFadeScene(
+   VOID
+);
+
+VOID
+PAL_BattleEnemyEscape(
+   VOID
+);
+
+VOID
+PAL_BattlePlayerEscape(
+   VOID
+);
+
+BATTLERESULT
+PAL_StartBattle(
+   WORD        wEnemyTeam,
+   BOOL        fIsBoss
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 183 - 0
common.h

@@ -0,0 +1,183 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef _COMMON_H
+#define _COMMON_H
+
+//#define PAL_WIN95          1 // not valid for now
+//#define PAL_CLASSIC        1
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <assert.h>
+
+#include "SDL.h"
+#include "SDL_endian.h"
+
+#ifdef _SDL_stdinc_h
+#define malloc       SDL_malloc
+#define calloc       SDL_calloc
+#define free         SDL_free
+#define realloc      SDL_realloc
+#endif
+
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+#define SWAP16(X)    (X)
+#define SWAP32(X)    (X)
+#else
+#define SWAP16(X)    SDL_Swap16(X)
+#define SWAP32(X)    SDL_Swap32(X)
+#endif
+
+#ifndef max
+#define max(a, b)    (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a, b)    (((a) < (b)) ? (a) : (b))
+#endif
+
+#if defined (__SYMBIAN32__)
+
+#undef  _WIN32
+#undef  SDL_INIT_JOYSTICK
+#define SDL_INIT_JOYSTICK     0
+#define PAL_HAS_MOUSE         1
+#define PAL_PREFIX            "e:/data/pal/"
+#define PAL_SAVE_PREFIX       "e:/data/pal/"
+
+#elif defined (GEKKO)
+
+#define PAL_HAS_JOYSTICKS     1
+#define PAL_HAS_MOUSE         0
+#define PAL_PREFIX            "SD:/apps/sdlpal/"
+#define PAL_SAVE_PREFIX       "SD:/apps/sdlpal/"
+
+#elif defined (PSP)
+
+#define PAL_HAS_JOYSTICKS     0
+#define PAL_PREFIX            "ms0:/"
+#define PAL_SAVE_PREFIX       "ms0:/PSP/SAVEDATA/SDLPAL/"
+
+#else
+
+#define PAL_HAS_JOYSTICKS     1
+#ifndef _WIN32_WCE
+#define PAL_ALLOW_KEYREPEAT   1
+#if SDL_MAJOR_VERSION == 1 && SDL_MINOR_VERSION <= 2
+#define PAL_HAS_CD            1
+#endif
+#if !defined (CYGWIN) && !defined (DINGOO) && !defined (GPH) && !defined (GEKKO)
+#define PAL_HAS_MP3           1
+#endif
+#endif
+#ifndef PAL_PREFIX
+#define PAL_PREFIX            "./"
+#endif
+#ifndef PAL_SAVE_PREFIX
+#define PAL_SAVE_PREFIX       "./"
+#endif
+
+#endif
+
+#ifndef SDL_INIT_CDROM
+#define SDL_INIT_CDROM        0
+#endif
+
+#ifdef _WIN32
+
+#include <windows.h>
+
+#if !defined(__BORLANDC__) && !defined(_WIN32_WCE)
+#include <io.h>
+#endif
+
+#define vsnprintf _vsnprintf
+
+#ifdef _MSC_VER
+#pragma warning (disable:4018)
+#pragma warning (disable:4028)
+#pragma warning (disable:4244)
+#pragma warning (disable:4305)
+#pragma warning (disable:4761)
+#pragma warning (disable:4996)
+#endif
+
+#ifndef _LPCBYTE_DEFINED
+#define _LPCBYTE_DEFINED
+typedef const BYTE *LPCBYTE;
+#endif
+
+#define PAL_HAS_NATIVEMIDI  1
+
+#else
+
+#include <unistd.h>
+
+#define CONST               const
+#ifndef FALSE
+#define FALSE               0
+#endif
+#ifndef TRUE
+#define TRUE                1
+#endif
+#define VOID                void
+typedef char                CHAR;
+typedef short               SHORT;
+typedef long                LONG;
+
+typedef unsigned long       ULONG, *PULONG;
+typedef unsigned short      USHORT, *PUSHORT;
+typedef unsigned char       UCHAR, *PUCHAR;
+
+typedef unsigned short      WORD, *LPWORD;
+typedef unsigned int        DWORD, *LPDWORD;
+typedef int                 INT, *LPINT, BOOL, *LPBOOL;
+typedef unsigned int        UINT, *PUINT, UINT32, *PUINT32;
+typedef unsigned char       BYTE, *LPBYTE;
+typedef CONST BYTE         *LPCBYTE;
+typedef float               FLOAT, *LPFLOAT;
+typedef void               *LPVOID;
+typedef const void         *LPCVOID;
+typedef CHAR               *LPSTR;
+typedef const CHAR         *LPCSTR;
+
+#endif
+
+#if defined (__SYMBIAN32__)
+#define PAL_LARGE           static
+#else
+#define PAL_LARGE           /* */
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 400 - 0
ending.c

@@ -0,0 +1,400 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "main.h"
+
+static WORD g_wCurEffectSprite = 0;
+
+VOID
+PAL_EndingSetEffectSprite(
+   WORD         wSpriteNum
+)
+/*++
+  Purpose:
+
+    Set the effect sprite of the ending.
+
+  Parameters:
+
+    [IN]  wSpriteNum - the number of the sprite.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   g_wCurEffectSprite = wSpriteNum;
+}
+
+VOID
+PAL_ShowFBP(
+   WORD         wChunkNum,
+   WORD         wFade
+)
+/*++
+  Purpose:
+
+    Draw an FBP picture to the screen.
+
+  Parameters:
+
+    [IN]  wChunkNum - number of chunk in fbp.mkf file.
+
+    [IN]  wFade - fading speed of showing the picture.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   PAL_LARGE BYTE            buf[320 * 200];
+   PAL_LARGE BYTE            bufSprite[320 * 200];
+   const int                 rgIndex[6] = {0, 3, 1, 5, 2, 4};
+   SDL_Surface              *p;
+   int                       i, j, k;
+   BYTE                      a, b;
+
+   if (PAL_MKFDecompressChunk(buf, 320 * 200, wChunkNum, gpGlobals->f.fpFBP) <= 0)
+   {
+      memset(buf, 0, sizeof(buf));
+   }
+
+   if (g_wCurEffectSprite != 0)
+   {
+      PAL_MKFDecompressChunk(bufSprite, 320 * 200, g_wCurEffectSprite, gpGlobals->f.fpMGO);
+   }
+
+   if (wFade)
+   {
+      wFade++;
+      wFade *= 10;
+
+      p = SDL_CreateRGBSurface(gpScreen->flags & ~SDL_HWSURFACE, 320, 200, 8,
+         gpScreen->format->Rmask, gpScreen->format->Gmask,
+         gpScreen->format->Bmask, gpScreen->format->Amask);
+
+      PAL_FBPBlitToSurface(buf, p);
+      VIDEO_BackupScreen();
+
+      for (i = 0; i < 16; i++)
+      {
+         for (j = 0; j < 6; j++)
+         {
+            //
+            // Blend the pixels in the 2 buffers, and put the result into the
+            // backup buffer
+            //
+            for (k = rgIndex[j]; k < gpScreen->pitch * gpScreen->h; k += 6)
+            {
+               a = ((LPBYTE)(p->pixels))[k];
+               b = ((LPBYTE)(gpScreenBak->pixels))[k];
+
+               if (i > 0)
+               {
+                  if ((a & 0x0F) > (b & 0x0F))
+                  {
+                     b++;
+                  }
+                  else if ((a & 0x0F) < (b & 0x0F))
+                  {
+                     b--;
+                  }
+               }
+
+               ((LPBYTE)(gpScreenBak->pixels))[k] = ((a & 0xF0) | (b & 0x0F));
+            }
+
+            SDL_BlitSurface(gpScreenBak, NULL, gpScreen, NULL);
+
+            if (g_wCurEffectSprite != 0)
+            {
+               int f = SDL_GetTicks() / 150;
+               PAL_RLEBlitToSurface(PAL_SpriteGetFrame(bufSprite, f % PAL_SpriteGetNumFrames(bufSprite)),
+                  gpScreen, PAL_XY(0, 0));
+            }
+
+            VIDEO_UpdateScreen(NULL);
+            UTIL_Delay(wFade);
+         }
+      }
+
+      SDL_FreeSurface(p);
+   }
+
+   //
+   // HACKHACK: to make the ending show correctly
+   //
+   if (wChunkNum != 49)
+   {
+      PAL_FBPBlitToSurface(buf, gpScreen);
+   }
+
+   VIDEO_UpdateScreen(NULL);
+}
+
+VOID
+PAL_ScrollFBP(
+   WORD         wChunkNum,
+   WORD         wScrollSpeed,
+   BOOL         fScrollDown
+)
+/*++
+  Purpose:
+
+    Scroll up an FBP picture to the screen.
+
+  Parameters:
+
+    [IN]  wChunkNum - number of chunk in fbp.mkf file.
+
+    [IN]  wScrollSpeed - scrolling speed of showing the picture.
+
+    [IN]  fScrollDown - TRUE if scroll down, FALSE if scroll up.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   SDL_Surface          *p;
+   PAL_LARGE BYTE        buf[320 * 200];
+   PAL_LARGE BYTE        bufSprite[320 * 200];
+   int                   i, l;
+   SDL_Rect              rect, dstrect;
+
+   if (PAL_MKFDecompressChunk(buf, 320 * 200, wChunkNum, gpGlobals->f.fpFBP) <= 0)
+   {
+      return;
+   }
+
+   if (g_wCurEffectSprite != 0)
+   {
+      PAL_MKFDecompressChunk(bufSprite, 320 * 200, g_wCurEffectSprite, gpGlobals->f.fpMGO);
+   }
+
+   p = SDL_CreateRGBSurface(gpScreen->flags & ~SDL_HWSURFACE, 320, 200, 8,
+      gpScreen->format->Rmask, gpScreen->format->Gmask,
+      gpScreen->format->Bmask, gpScreen->format->Amask);
+
+   if (p == NULL)
+   {
+      return;
+   }
+
+   VIDEO_BackupScreen();
+   PAL_FBPBlitToSurface(buf, p);
+
+   if (wScrollSpeed == 0)
+   {
+      wScrollSpeed = 1;
+   }
+
+   rect.x = 0;
+   rect.w = 320;
+   dstrect.x = 0;
+   dstrect.w = 320;
+
+   for (l = 0; l < 220; l++)
+   {
+      i = l;
+      if (i > 200)
+      {
+         i = 200;
+      }
+
+      if (fScrollDown)
+      {
+         rect.y = 0;
+         dstrect.y = i;
+         rect.h = 200 - i;
+         dstrect.h = 200 - i;
+      }
+      else
+      {
+         rect.y = i;
+         dstrect.y = 0;
+         rect.h = 200 - i;
+         dstrect.h = 200 - i;
+      }
+
+      SDL_BlitSurface(gpScreenBak, &rect, gpScreen, &dstrect);
+
+      if (fScrollDown)
+      {
+         rect.y = 200 - i;
+         dstrect.y = 0;
+         rect.h = i;
+         dstrect.h = i;
+      }
+      else
+      {
+         rect.y = 0;
+         dstrect.y = 200 - i;
+         rect.h = i;
+         dstrect.h = i;
+      }
+
+      SDL_BlitSurface(p, &rect, gpScreen, &dstrect);
+
+      PAL_ApplyWave(gpScreen);
+
+      if (g_wCurEffectSprite != 0)
+      {
+         int f = SDL_GetTicks() / 150;
+         PAL_RLEBlitToSurface(PAL_SpriteGetFrame(bufSprite, f % PAL_SpriteGetNumFrames(bufSprite)),
+            gpScreen, PAL_XY(0, 0));
+      }
+
+      VIDEO_UpdateScreen(NULL);
+
+      if (gpGlobals->fNeedToFadeIn)
+      {
+         PAL_FadeIn(gpGlobals->wNumPalette, gpGlobals->fNightPalette, 1);
+         gpGlobals->fNeedToFadeIn = FALSE;
+      }
+
+      UTIL_Delay(800 / wScrollSpeed);
+   }
+
+   SDL_BlitSurface(p, NULL, gpScreen, NULL);
+   SDL_FreeSurface(p);
+   VIDEO_UpdateScreen(NULL);
+}
+
+VOID
+PAL_EndingAnimation(
+   VOID
+)
+/*++
+  Purpose:
+
+    Show the ending animation.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   LPBYTE            buf;
+   LPBYTE            bufGirl;
+   SDL_Surface      *pUpper;
+   SDL_Surface      *pLower;
+   SDL_Rect          srcrect, dstrect;
+
+   int               yPosGirl = 180;
+   int               i;
+
+   buf = (LPBYTE)UTIL_calloc(1, 64000);
+   bufGirl = (LPBYTE)UTIL_calloc(1, 6000);
+
+   pUpper = SDL_CreateRGBSurface(gpScreen->flags & ~SDL_HWSURFACE, 320, 200, 8,
+      gpScreen->format->Rmask, gpScreen->format->Gmask,
+      gpScreen->format->Bmask, gpScreen->format->Amask);
+
+   pLower = SDL_CreateRGBSurface(gpScreen->flags & ~SDL_HWSURFACE, 320, 200, 8,
+      gpScreen->format->Rmask, gpScreen->format->Gmask,
+      gpScreen->format->Bmask, gpScreen->format->Amask);
+
+   PAL_MKFDecompressChunk(buf, 64000, 61, gpGlobals->f.fpFBP);
+   PAL_FBPBlitToSurface(buf, pUpper);
+
+   PAL_MKFDecompressChunk(buf, 64000, 62, gpGlobals->f.fpFBP);
+   PAL_FBPBlitToSurface(buf, pLower);
+
+   PAL_MKFDecompressChunk(buf, 64000, 571, gpGlobals->f.fpMGO);
+   PAL_MKFDecompressChunk(bufGirl, 6000, 572, gpGlobals->f.fpMGO);
+
+   srcrect.x = 0;
+   dstrect.x = 0;
+   srcrect.w = 320;
+   dstrect.w = 320;
+
+   gpGlobals->wScreenWave = 2;
+
+   for (i = 0; i < 400; i++)
+   {
+      //
+      // Draw the background
+      //
+      srcrect.y = 0;
+      srcrect.h = 200 - i / 2;
+
+      dstrect.y = i / 2;
+      dstrect.h = 200 - i / 2;
+
+      SDL_BlitSurface(pLower, &srcrect, gpScreen, &dstrect);
+
+      srcrect.y = 200 - i / 2;
+      srcrect.h = i / 2;
+
+      dstrect.y = 0;
+      dstrect.h = i / 2;
+
+      SDL_BlitSurface(pUpper, &srcrect, gpScreen, &dstrect);
+
+      PAL_ApplyWave(gpScreen);
+
+      //
+      // Draw the beast
+      //
+      PAL_RLEBlitToSurface(PAL_SpriteGetFrame(buf, 0), gpScreen, PAL_XY(0, -400 + i));
+      PAL_RLEBlitToSurface(PAL_SpriteGetFrame(buf, 1), gpScreen, PAL_XY(0, -200 + i));
+
+      //
+      // Draw the girl
+      //
+      yPosGirl -= i & 1;
+      if (yPosGirl < 80)
+      {
+         yPosGirl = 80;
+      }
+
+      PAL_RLEBlitToSurface(PAL_SpriteGetFrame(bufGirl, (SDL_GetTicks() / 50) % 4),
+         gpScreen, PAL_XY(220, yPosGirl));
+
+      //
+      // Update the screen
+      //
+      VIDEO_UpdateScreen(NULL);
+      if (gpGlobals->fNeedToFadeIn)
+      {
+         PAL_FadeIn(gpGlobals->wNumPalette, gpGlobals->fNightPalette, 1);
+         gpGlobals->fNeedToFadeIn = FALSE;
+      }
+
+      UTIL_Delay(50);
+   }
+
+   gpGlobals->wScreenWave = 0;
+
+   SDL_FreeSurface(pUpper);
+   SDL_FreeSurface(pLower);
+
+   free(buf);
+   free(bufGirl);
+}

+ 55 - 0
ending.h

@@ -0,0 +1,55 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef ENDGAME_H
+#define ENDGAME_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+VOID
+PAL_EndingSetEffectSprite(
+   WORD         wSpriteNum
+);
+
+VOID
+PAL_ShowFBP(
+   WORD         wChunkNum,
+   WORD         wFade
+);
+
+VOID
+PAL_ScrollFBP(
+   WORD         wChunkNum,
+   WORD         wScrollSpeed,
+   BOOL         fScrollDown
+);
+
+VOID
+PAL_EndingAnimation(
+   VOID
+);
+
+#ifdef __cplusplus
+}
+#endif
+#endif

File diff suppressed because it is too large
+ 4972 - 0
fight.c


+ 107 - 0
fight.h

@@ -0,0 +1,107 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef FIGHT_H
+#define FIGHT_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+INT
+PAL_BattleSelectAutoTarget(
+   VOID
+);
+
+#ifndef PAL_CLASSIC
+
+VOID
+PAL_UpdateTimeChargingUnit(
+   VOID
+);
+
+FLOAT
+PAL_GetTimeChargingSpeed(
+   WORD           wDexterity
+);
+
+#endif
+
+VOID
+PAL_BattleUpdateFighters(
+   VOID
+);
+
+VOID
+PAL_BattlePlayerCheckReady(
+   VOID
+);
+
+VOID
+PAL_BattleStartFrame(
+   VOID
+);
+
+VOID
+PAL_BattleCommitAction(
+   BOOL         fRepeat
+);
+
+VOID
+PAL_BattlePlayerPerformAction(
+   WORD         wPlayerIndex
+);
+
+VOID
+PAL_BattleEnemyPerformAction(
+   WORD         wEnemyIndex
+);
+
+VOID
+PAL_BattleShowPlayerPreMagicAnim(
+   WORD         wPlayerIndex,
+   BOOL         fSummon
+);
+
+VOID
+PAL_BattleDelay(
+   WORD       wDuration,
+   WORD       wObjectID,
+   BOOL       fUpdateGesture
+);
+
+VOID
+PAL_BattleStealFromEnemy(
+   WORD           wTarget,
+   WORD           wStealRate
+);
+
+VOID
+PAL_BattleSimulateMagic(
+   SHORT      sTarget,
+   WORD       wMagicObjectID,
+   WORD       wBaseDamage
+);
+
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 290 - 0
font.c

@@ -0,0 +1,290 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "font.h"
+#include "ascii.h"
+#include "util.h"
+
+typedef struct tagFont
+{
+   LPWORD           lpBufChar;
+   LPBYTE           lpBufGlyph;
+   INT              nChar;
+} FONT, *LPFONT;
+
+static LPFONT gpFont = NULL;
+
+INT
+PAL_InitFont(
+   VOID
+)
+/*++
+  Purpose:
+
+    Load the font files.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    0 if succeed, -1 if cannot allocate memory, -2 if cannot load files.
+
+--*/
+{
+   FILE *fp;
+
+   if (gpFont != NULL)
+   {
+      //
+      // Already initialized
+      //
+      return 0;
+   }
+
+   gpFont = (LPFONT)calloc(1, sizeof(FONT));
+   if (gpFont == NULL)
+   {
+      return -1;
+   }
+
+   //
+   // Load the wor16.asc file.
+   //
+   fp = UTIL_OpenRequiredFile("wor16.asc");
+
+   //
+   // Get the size of wor16.asc file.
+   //
+   fseek(fp, 0, SEEK_END);
+   gpFont->nChar = ftell(fp);
+   gpFont->nChar /= 2;
+
+   //
+   // Read all the character codes.
+   //
+   gpFont->lpBufChar = (LPWORD)calloc(gpFont->nChar, sizeof(WORD));
+   if (gpFont->lpBufChar == NULL)
+   {
+      free(gpFont);
+      gpFont = NULL;
+      return -1;
+   }
+
+   fseek(fp, 0, SEEK_SET);
+   fread(gpFont->lpBufChar, sizeof(WORD), gpFont->nChar, fp);
+
+   //
+   // Close wor16.asc file.
+   //
+   fclose(fp);
+
+   //
+   // Read all bitmaps from wor16.fon file.
+   //
+   fp = UTIL_OpenRequiredFile("wor16.fon");
+
+   gpFont->lpBufGlyph = (LPBYTE)calloc(gpFont->nChar, 30);
+   if (gpFont->lpBufGlyph == NULL)
+   {
+      free(gpFont->lpBufChar);
+      free(gpFont);
+      gpFont = NULL;
+      return -1;
+   }
+
+   //
+   // The font glyph data begins at offset 0x682 in wor16.fon.
+   //
+   fseek(fp, 0x682, SEEK_SET);
+   fread(gpFont->lpBufGlyph, 30, gpFont->nChar, fp);
+   fclose(fp);
+
+   return 0;
+}
+
+VOID
+PAL_FreeFont(
+   VOID
+)
+/*++
+  Purpose:
+
+    Free the memory used for fonts.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   if (gpFont != NULL)
+   {
+      free(gpFont->lpBufChar);
+      free(gpFont->lpBufGlyph);
+      free(gpFont);
+   }
+
+   gpFont = NULL;
+}
+
+VOID
+PAL_DrawCharOnSurface(
+   WORD                     wChar,
+   SDL_Surface             *lpSurface,
+   PAL_POS                  pos,
+   BYTE                     bColor
+)
+/*++
+  Purpose:
+
+    Draw a BIG-5 Chinese character on a surface.
+
+  Parameters:
+
+    [IN]  wChar - the character to be drawn (in BIG-5).
+
+    [OUT] lpSurface - the destination surface.
+
+    [IN]  pos - the destination location of the surface.
+
+    [IN]  bColor - the color of the character.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   int       i, j, dx;
+   int       x = PAL_X(pos), y = PAL_Y(pos);
+   LPBYTE    pChar;
+
+   //
+   // Check for NULL pointer.
+   //
+   if (lpSurface == NULL || gpFont == NULL)
+   {
+      return;
+   }
+
+   //
+   // Locate for this character in the font lib.
+   //
+   for (i = 0; i < gpFont->nChar; i++)
+   {
+      if (gpFont->lpBufChar[i] == wChar)
+      {
+         break;
+      }
+   }
+
+   if (i >= gpFont->nChar)
+   {
+      //
+      // This character does not exist in the font lib.
+      //
+      return;
+   }
+
+   pChar = gpFont->lpBufGlyph + i * 30;
+
+   //
+   // Draw the character to the surface.
+   //
+   y *= lpSurface->pitch;
+   for (i = 0; i < 30; i++)
+   {
+      dx = x + ((i & 1) << 3);
+      for (j = 0; j < 8; j++)
+      {
+         if (pChar[i] & (1 << (7 - j)))
+         {
+            ((LPBYTE)(lpSurface->pixels))[y + dx] = bColor;
+         }
+         dx++;
+      }
+      y += (i & 1) * lpSurface->pitch;
+   }
+}
+
+VOID
+PAL_DrawASCIICharOnSurface(
+   BYTE                     bChar,
+   SDL_Surface             *lpSurface,
+   PAL_POS                  pos,
+   BYTE                     bColor
+)
+/*++
+  Purpose:
+
+    Draw a ASCII character on a surface.
+
+  Parameters:
+
+    [IN]  bChar - the character to be drawn.
+
+    [OUT] lpSurface - the destination surface.
+
+    [IN]  pos - the destination location of the surface.
+
+    [IN]  bColor - the color of the character.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   int i, j, dx;
+   int x = PAL_X(pos), y = PAL_Y(pos);
+   LPBYTE pChar = &iso_font[(int)(bChar & ~128) * 15];
+
+   //
+   // Check for NULL pointer.
+   //
+   if (lpSurface == NULL)
+   {
+      return;
+   }
+
+   //
+   // Draw the character to the surface.
+   //
+   y *= lpSurface->pitch;
+   for (i = 0; i < 15; i++)
+   {
+      dx = x;
+      for (j = 0; j < 8; j++)
+      {
+         if (pChar[i] & (1 << j))
+         {
+            ((LPBYTE)(lpSurface->pixels))[y + dx] = bColor;
+         }
+         dx++;
+      }
+      y += lpSurface->pitch;
+   }
+}

+ 62 - 0
font.h

@@ -0,0 +1,62 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef FONT_H
+#define FONT_H
+
+#include "common.h"
+#include "palcommon.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+INT
+PAL_InitFont(
+   VOID
+);
+
+VOID
+PAL_FreeFont(
+   VOID
+);
+
+VOID
+PAL_DrawCharOnSurface(
+   WORD                     wChar,
+   SDL_Surface             *lpSurface,
+   PAL_POS                  pos,
+   BYTE                     bColor
+);
+
+VOID
+PAL_DrawASCIICharOnSurface(
+   BYTE                     bChar,
+   SDL_Surface             *lpSurface,
+   PAL_POS                  pos,
+   BYTE                     bColor
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 132 - 0
game.c

@@ -0,0 +1,132 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "main.h"
+
+static VOID
+PAL_GameStart(
+   VOID
+)
+/*++
+  Purpose:
+
+    Do some initialization work when game starts (new game or load game).
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   PAL_SetLoadFlags(kLoadScene | kLoadPlayerSprite);
+
+   if (!gpGlobals->fEnteringScene)
+   {
+      //
+      // Fade in music if the player has loaded an old game.
+      //
+      PAL_PlayMUS(gpGlobals->wNumMusic, TRUE, 1);
+   }
+
+   gpGlobals->fNeedToFadeIn = TRUE;
+   gpGlobals->dwFrameNum = 0;
+}
+
+VOID
+PAL_GameMain(
+   VOID
+)
+/*++
+  Purpose:
+
+    The game entry routine.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   DWORD       dwTime;
+
+   //
+   // Show the opening menu.
+   //
+   gpGlobals->bCurrentSaveSlot = (BYTE)PAL_OpeningMenu();
+
+   //
+   // Initialize game data and set the flags to load the game resources.
+   //
+   PAL_InitGameData(gpGlobals->bCurrentSaveSlot);
+
+   //
+   // Run the main game loop.
+   //
+   dwTime = SDL_GetTicks();
+   while (TRUE)
+   {
+      //
+      // Do some initialization at game start.
+      //
+      if (gpGlobals->fGameStart)
+      {
+         PAL_GameStart();
+         gpGlobals->fGameStart = FALSE;
+      }
+
+      //
+      // Load the game resources if needed.
+      //
+      PAL_LoadResources();
+
+      //
+      // Clear the input state of previous frame.
+      //
+      PAL_ClearKeyState();
+
+      //
+      // Wait for the time of one frame. Accept input here.
+      //
+      PAL_ProcessEvent();
+      while (SDL_GetTicks() <= dwTime)
+      {
+         PAL_ProcessEvent();
+         SDL_Delay(1);
+      }
+
+      //
+      // Set the time of the next frame.
+      //
+      dwTime = SDL_GetTicks() + FRAME_TIME;
+
+      //
+      // Run the main frame routine.
+      //
+      PAL_StartFrame();
+   }
+}

+ 34 - 0
game.h

@@ -0,0 +1,34 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef GAME_H
+#define GAME_H
+
+#include "common.h"
+
+#define    FPS             10
+#define    FRAME_TIME      (1000 / FPS)
+
+VOID
+PAL_GameMain(
+   VOID
+);
+
+#endif

+ 127 - 0
getopt.c

@@ -0,0 +1,127 @@
+//
+// Copyright (c) 1987, 1993, 1994
+//   The Regents of the University of California. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.
+// 3. Neither the name of the University nor the names of its contributors
+//    may be used to endorse or promote products derived from this software
+//    without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+//
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "getopt.h"
+
+int     opterr = 1;      // if error message should be printed
+int     optind = 1;      // index into parent argv vector
+int     optopt;          // character checked for validity
+int     optreset;        // reset getopt
+char   *optarg;          // argument associated with option
+
+#define   BADCH       (int)'?'
+#define   BADARG      (int)':'
+#define   EMSG        ""
+
+//
+// getopt --
+//   Parse argc/argv argument vector.
+//
+int
+getopt(
+   int             nargc,
+   char * const   *nargv,
+   const char     *ostr
+)
+{
+   static char *place = EMSG; // option letter processing
+   char *oli;                 // option letter list index
+
+   if (optreset || !*place)
+   {
+      // update scanning pointer
+      optreset = 0;
+      if (optind >= nargc || *(place = nargv[optind]) != '-')
+      {
+         place = EMSG;
+         return -1;
+      }
+      if (place[1] && *++place == '-')
+      {
+         // found "--"
+         ++optind;
+         place = EMSG;
+         return -1;
+      }
+   }
+
+   // option letter okay?
+   if ((optopt = (int)*place++) == (int)':' ||
+      !(oli = strchr(ostr, optopt)))
+   {
+      //
+      // if the user didn't specify '-' as an option,
+      // assume it means -1.
+      //
+      if (optopt == (int)'-')
+         return -1;
+      if (!*place)
+         ++optind;
+      if (opterr && *ostr != ':')
+         fprintf(stderr, "%s: illegal option -- %c\n", nargv[0], optopt);
+      return BADCH;
+   }
+
+   if (*++oli != ':')
+   {
+      // don't need argument
+      optarg = NULL;
+      if (!*place)
+         ++optind;
+   }
+   else
+   {
+      // need an argument
+      if (*place) // no white space
+         optarg = place;
+      else if (nargc <= ++optind)
+      {
+         // no arg
+         place = EMSG;
+         if (*ostr == ':')
+            return BADARG;
+         if (opterr)
+         {
+            fprintf(stderr, "%s: option requires an argument -- %c\n",
+               nargv[0], optopt);
+         }
+         return BADCH;
+      }
+      else // white space
+         optarg = nargv[optind];
+      place = EMSG;
+      ++optind;
+   }
+
+   return optopt; // dump back option letter
+}

+ 63 - 0
getopt.h

@@ -0,0 +1,63 @@
+//
+// Copyright (c) 1987, 1993, 1994
+//   The Regents of the University of California. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.
+// 3. Neither the name of the University nor the names of its contributors
+//    may be used to endorse or promote products derived from this software
+//    without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+//
+
+#ifndef GETOPT_H
+#define GETOPT_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define opterr     PAL_opterr
+#define optind     PAL_optind
+#define optopt     PAL_optopt
+#define optreset   PAL_optreset
+#define optarg     PAL_optarg
+#define getopt     PAL_getopt
+
+extern int     opterr;
+extern int     optind;
+extern int     optopt;
+extern int     optreset;
+extern char   *optarg;
+
+int
+getopt(
+   int             nargc,
+   char * const   *nargv,
+   const char     *ostr
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

File diff suppressed because it is too large
+ 1886 - 0
global.c


+ 766 - 0
global.h

@@ -0,0 +1,766 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef GLOBAL_H
+#define GLOBAL_H
+
+#include "common.h"
+#include "map.h"
+#include "ui.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+//
+// SOME NOTES ON "AUTO SCRIPT" AND "TRIGGER SCRIPT":
+//
+// Auto scripts are executed automatically in each frame.
+//
+// Trigger scripts are only executed when the event is triggered (player touched
+// an event object, player triggered an event script by pressing Spacebar).
+//
+
+// maximum number of players in party
+#define     MAX_PLAYERS_IN_PARTY         3
+
+// total number of possible player roles
+#define     MAX_PLAYER_ROLES             6
+
+// totally number of playable player roles
+#define     MAX_PLAYABLE_PLAYER_ROLES    5
+
+// maximum entries of inventory
+#define     MAX_INVENTORY                256
+
+// maximum items in a store
+#define     MAX_STORE_ITEM               9
+
+// total number of magic attributes
+#define     NUM_MAGIC_ELEMENTAL          5
+
+// maximum number of enemies in a team
+#define     MAX_ENEMIES_IN_TEAM          5
+
+// maximum number of equipments for a player
+#define     MAX_PLAYER_EQUIPMENTS        6
+
+// maximum number of magics for a player
+#define     MAX_PLAYER_MAGICS            32
+
+// maximum number of scenes
+#define     MAX_SCENES                   300
+
+// maximum number of objects
+#define     MAX_OBJECTS                  600
+
+// maximum number of event objects (should be somewhat more than the original,
+// as there are some modified versions which has more)
+#define     MAX_EVENT_OBJECTS            5500
+
+// maximum number of effective poisons to players
+#define     MAX_POISONS                  16
+
+// maximum number of level
+#define     MAX_LEVELS                   99
+
+// status of characters
+typedef enum tagSTATUS
+{
+   kStatusConfused = 0,  // attack friends randomly
+#ifdef PAL_CLASSIC
+   kStatusParalyzed,     // paralyzed
+#else
+   kStatusSlow,          // slower
+#endif
+   kStatusSleep,         // not allowed to move
+   kStatusSilence,       // cannot use magic
+   kStatusPuppet,        // for dead players only, continue attacking
+   kStatusBravery,       // more power for physical attacks
+   kStatusProtect,       // more defense value
+   kStatusHaste,         // faster
+   kStatusDualAttack,    // dual attack
+   kStatusAll
+} STATUS;
+
+#ifndef PAL_CLASSIC
+#define kStatusParalyzed kStatusSleep
+#endif
+
+// body parts of equipments
+typedef enum tagBODYPART
+{
+   kBodyPartHead     = 0,
+   kBodyPartBody,
+   kBodyPartShoulder,
+   kBodyPartHand,
+   kBodyPartFeet,
+   kBodyPartWear,
+   kBodyPartExtra,
+} BODYPART;
+
+// state of event object, used by the sState field of the EVENTOBJECT struct
+typedef enum tagOBJECTSTATE
+{
+   kObjStateHidden               = 0,
+   kObjStateNormal               = 1,
+   kObjStateBlocker              = 2
+} OBJECTSTATE, *LPOBJECTSTATE;
+
+typedef enum tagTRIGGERMODE
+{
+   kTriggerNone                  = 0,
+   kTriggerSearchNear            = 1,
+   kTriggerSearchNormal          = 2,
+   kTriggerSearchFar             = 3,
+   kTriggerTouchNear             = 4,
+   kTriggerTouchNormal           = 5,
+   kTriggerTouchFar              = 6,
+   kTriggerTouchFarther          = 7,
+   kTriggerTouchFarthest         = 8
+} TRIGGERMODE;
+
+typedef struct tagEVENTOBJECT
+{
+   SHORT        sVanishTime;         // vanish time (?)
+   WORD         x;                   // X coordinate on the map
+   WORD         y;                   // Y coordinate on the map
+   SHORT        sLayer;              // layer value
+   WORD         wTriggerScript;      // Trigger script entry
+   WORD         wAutoScript;         // Auto script entry
+   SHORT        sState;              // state of this object
+   WORD         wTriggerMode;        // trigger mode
+   WORD         wSpriteNum;          // number of the sprite
+   USHORT       nSpriteFrames;       // total number of frames of the sprite
+   WORD         wDirection;          // direction
+   WORD         wCurrentFrameNum;    // current frame number
+   USHORT       nScriptIdleFrame;    // count of idle frames, used by trigger script
+   WORD         wSpritePtrOffset;    // FIXME: ???
+   USHORT       nSpriteFramesAuto;   // total number of frames of the sprite, used by auto script
+   WORD         wScriptIdleFrameCountAuto;     // count of idle frames, used by auto script
+} EVENTOBJECT, *LPEVENTOBJECT;
+
+typedef struct tagSCENE
+{
+   WORD         wMapNum;         // number of the map
+   WORD         wScriptOnEnter;  // when entering this scene, execute script from here
+   WORD         wScriptOnTeleport;  // when teleporting out of this scene, execute script from here
+   WORD         wEventObjectIndex;  // event objects in this scene begins from number wEventObjectIndex + 1
+} SCENE, *LPSCENE;
+
+// object including system strings, players, items, magics, enemies and poison scripts.
+
+// system strings and players
+typedef struct tagOBJECT_PLAYER
+{
+   WORD         wReserved[2];    // always zero
+   WORD         wScriptOnFriendDeath; // when friends in party dies, execute script from here
+   WORD         wScriptOnDying;  // when dying, execute script from here
+} OBJECT_PLAYER;
+
+typedef enum tagITEMFLAG
+{
+   kItemFlagUsable          = (1 << 0),
+   kItemFlagEquipable       = (1 << 1),
+   kItemFlagThrowable       = (1 << 2),
+   kItemFlagConsuming       = (1 << 3),
+   kItemFlagApplyToAll      = (1 << 4),
+   kItemFlagSellable        = (1 << 5),
+   kItemFlagEquipableByPlayerRole_First  = (1 << 6)
+} ITEMFLAG;
+
+// items
+typedef struct tagOBJECT_ITEM
+{
+   WORD         wBitmap;         // bitmap number in BALL.MKF
+   WORD         wPrice;          // price
+   WORD         wScriptOnUse;    // script executed when using this item
+   WORD         wScriptOnEquip;  // script executed when equipping this item
+   WORD         wScriptOnThrow;  // script executed when throwing this item to enemy
+   WORD         wFlags;          // flags
+} OBJECT_ITEM;
+
+typedef enum tagMAGICFLAG
+{
+   kMagicFlagUsableOutsideBattle        = (1 << 0),
+   kMagicFlagUsableInBattle             = (1 << 1),
+   kMagicFlagUsableToEnemy              = (1 << 3),
+   kMagicFlagApplyToAll                 = (1 << 4),
+} MAGICFLAG;
+
+// magics
+typedef struct tagOBJECT_MAGIC
+{
+   WORD         wMagicNumber;      // magic number, according to DATA.MKF #3
+   WORD         wReserved1;        // always zero
+   WORD         wScriptOnSuccess;  // when magic succeed, execute script from here
+   WORD         wScriptOnUse;      // when use this magic, execute script from here
+   WORD         wReserved2;        // always zero
+   WORD         wFlags;            // flags
+} OBJECT_MAGIC;
+
+// enemies
+typedef struct tagOBJECT_ENEMY
+{
+   WORD         wEnemyID;        // ID of the enemy, according to DATA.MKF #1.
+                                 // Also indicates the bitmap number in ABC.MKF.
+   WORD         wResistanceToSorcery;  // resistance to sorcery and poison (0 min, 10 max)
+   WORD         wScriptOnTurnStart;    // script executed when turn starts
+   WORD         wScriptOnBattleEnd;    // script executed when battle ends
+   WORD         wScriptOnReady;        // script executed when the enemy is ready
+} OBJECT_ENEMY;
+
+// poisons (scripts executed in each round)
+typedef struct tagOBJECT_POISON
+{
+   WORD         wPoisonLevel;    // level of the poison
+   WORD         wColor;          // color of avatars
+   WORD         wPlayerScript;   // script executed when player has this poison (per round)
+   WORD         wReserved;       // always zero
+   WORD         wEnemyScript;    // script executed when enemy has this poison (per round)
+} OBJECT_POISON;
+
+typedef union tagOBJECT
+{
+   WORD              rgwData[6];
+   OBJECT_PLAYER     player;
+   OBJECT_ITEM       item;
+   OBJECT_MAGIC      magic;
+   OBJECT_ENEMY      enemy;
+   OBJECT_POISON     poison;
+} OBJECT, *LPOBJECT;
+
+typedef struct tagSCRIPTENTRY
+{
+   WORD          wOperation;     // operation code
+   WORD          rgwOperand[3];  // operands
+} SCRIPTENTRY, *LPSCRIPTENTRY;
+
+typedef struct tagINVENTORY
+{
+   WORD          wItem;             // item object code
+   USHORT        nAmount;           // amount of this item
+   USHORT        nAmountInUse;      // in-use amount of this item
+} INVENTORY, *LPINVENTORY;
+
+typedef struct tagSTORE
+{
+   WORD          rgwItems[MAX_STORE_ITEM];
+} STORE, *LPSTORE;
+
+typedef struct tagENEMY
+{
+   WORD        wIdleFrames;         // total number of frames when idle
+   WORD        wMagicFrames;        // total number of frames when using magics
+   WORD        wAttackFrames;       // total number of frames when doing normal attack
+   WORD        wIdleAnimSpeed;      // speed of the animation when idle
+   WORD        wActWaitFrames;      // FIXME: ???
+   WORD        wYPosOffset;
+   WORD        wAttackSound;        // sound played when this enemy uses normal attack
+   WORD        wActionSound;        // FIXME: ???
+   WORD        wMagicSound;         // sound played when this enemy uses magic
+   WORD        wDeathSound;         // sound played when this enemy dies
+   WORD        wCallSound;          // sound played when entering the battle
+   WORD        wHealth;             // total HP of the enemy
+   WORD        wExp;                // How many EXPs we'll get for beating this enemy
+   WORD        wCash;               // how many cashes we'll get for beating this enemy
+   WORD        wLevel;              // this enemy's level
+   WORD        wMagic;              // this enemy's magic number
+   WORD        wMagicRate;          // chance for this enemy to use magic
+   WORD        wAttackEquivItem;    // equivalence item of this enemy's normal attack
+   WORD        wAttackEquivItemRate;// chance for equivalence item
+   WORD        wStealItem;          // which item we'll get when stealing from this enemy
+   USHORT      nStealItem;          // total amount of the items which can be stolen
+   WORD        wAttackStrength;     // normal attack strength
+   WORD        wMagicStrength;      // magical attack strength
+   WORD        wDefense;            // resistance to all kinds of attacking
+   WORD        wDexterity;          // dexterity
+   WORD        wFleeRate;           // chance for successful fleeing
+   WORD        wPoisonResistance;   // resistance to poison
+   WORD        wElemResistance[NUM_MAGIC_ELEMENTAL]; // resistance to elemental magics
+   WORD        wPhysicalResistance; // resistance to physical attack
+   WORD        wDualMove;           // whether this enemy can do dual move or not
+   WORD        wCollectValue;       // value for collecting this enemy for items
+} ENEMY, *LPENEMY;
+
+typedef struct tagENEMYTEAM
+{
+   WORD        rgwEnemy[MAX_ENEMIES_IN_TEAM];
+} ENEMYTEAM, *LPENEMYTEAM;
+
+typedef WORD PLAYERS[MAX_PLAYER_ROLES];
+
+typedef struct tagPLAYERROLES
+{
+   PLAYERS            rgwAvatar;             // avatar (shown in status view)
+   PLAYERS            rgwSpriteNumInBattle;  // sprite displayed in battle (in F.MKF)
+   PLAYERS            rgwSpriteNum;          // sprite displayed in normal scene (in MGO.MKF)
+   PLAYERS            rgwName;               // name of player class (in WORD.DAT)
+   PLAYERS            rgwAttackAll;          // whether player can attack everyone in a bulk or not
+   PLAYERS            rgwUnknown1;           // FIXME: ???
+   PLAYERS            rgwLevel;              // level
+   PLAYERS            rgwMaxHP;              // maximum HP
+   PLAYERS            rgwMaxMP;              // maximum MP
+   PLAYERS            rgwHP;                 // current HP
+   PLAYERS            rgwMP;                 // current MP
+   WORD               rgwEquipment[MAX_PLAYER_EQUIPMENTS][MAX_PLAYER_ROLES]; // equipments
+   PLAYERS            rgwAttackStrength;     // normal attack strength
+   PLAYERS            rgwMagicStrength;      // magical attack strength
+   PLAYERS            rgwDefense;            // resistance to all kinds of attacking
+   PLAYERS            rgwDexterity;          // dexterity
+   PLAYERS            rgwFleeRate;           // chance of successful fleeing
+   PLAYERS            rgwPoisonResistance;   // resistance to poison
+   WORD               rgwElementalResistance[NUM_MAGIC_ELEMENTAL][MAX_PLAYER_ROLES]; // resistance to elemental magics
+   PLAYERS            rgwUnknown2;           // FIXME: ???
+   PLAYERS            rgwUnknown3;           // FIXME: ???
+   PLAYERS            rgwUnknown4;           // FIXME: ???
+   PLAYERS            rgwCoveredBy;          // who will cover me when I am low of HP or not sane
+   WORD               rgwMagic[MAX_PLAYER_MAGICS][MAX_PLAYER_ROLES]; // magics
+   PLAYERS            rgwWalkFrames;         // walk frame (???)
+   PLAYERS            rgwCooperativeMagic;   // cooperative magic
+   PLAYERS            rgwUnknown5;           // FIXME: ???
+   PLAYERS            rgwUnknown6;           // FIXME: ???
+   PLAYERS            rgwDeathSound;         // sound played when player dies
+   PLAYERS            rgwAttackSound;        // sound played when player attacks
+   PLAYERS            rgwWeaponSound;        // weapon sound (???)
+   PLAYERS            rgwCriticalSound;      // sound played when player make critical hits
+   PLAYERS            rgwMagicSound;         // sound played when player is casting a magic
+   PLAYERS            rgwCoverSound;         // sound played when player cover others
+   PLAYERS            rgwDyingSound;         // sound played when player is dying
+} PLAYERROLES, *LPPLAYERROLES;
+
+typedef enum tagMAGIC_TYPE
+{
+   kMagicTypeNormal           = 0,
+   kMagicTypeAttackAll        = 1,  // draw the effect on each of the enemies
+   kMagicTypeAttackWhole      = 2,  // draw the effect on the whole enemy team
+   kMagicTypeAttackField      = 3,  // draw the effect on the battle field
+   kMagicTypeApplyToPlayer    = 4,  // the magic is used on one player
+   kMagicTypeApplyToParty     = 5,  // the magic is used on the whole party
+   kMagicTypeTrance           = 8,  // trance the player
+   kMagicTypeSummon           = 9,  // summon
+} MAGIC_TYPE;
+
+typedef struct tagMAGIC
+{
+   WORD               wEffect;               // effect sprite
+   WORD               wType;                 // type of this magic
+   WORD               wXOffset;
+   WORD               wYOffset;
+   WORD               wSummonEffect;         // summon effect sprite (in F.MKF)
+   WORD               wSpeed;                // speed of the effect
+   WORD               wKeepEffect;           // FIXME: ???
+   WORD               wSoundDelay;           // delay of the SFX
+   WORD               wEffectTimes;          // total times of effect
+   WORD               wShake;                // shake screen
+   WORD               wWave;                 // wave screen
+   WORD               wUnknown;              // FIXME: ???
+   WORD               wCostMP;               // MP cost
+   WORD               wBaseDamage;           // base damage
+   WORD               wElemental;            // elemental (0 = No Elemental, last = poison)
+   WORD               wSound;                // sound played when using this magic
+} MAGIC, *LPMAGIC;
+
+typedef struct tagBATTLEFIELD
+{
+   WORD               wScreenWave;                      // level of screen waving
+   SHORT              rgsMagicEffect[NUM_MAGIC_ELEMENTAL]; // effect of attributed magics
+} BATTLEFIELD, *LPBATTLEFIELD;
+
+// magics learned when level up
+typedef struct tagLEVELUPMAGIC
+{
+   WORD               wLevel;    // level reached
+   WORD               wMagic;    // magic learned
+} LEVELUPMAGIC, *LPLEVELUPMAGIC;
+
+typedef struct tagLEVELUPMAGIC_ALL
+{
+   LEVELUPMAGIC       m[MAX_PLAYABLE_PLAYER_ROLES];
+} LEVELUPMAGIC_ALL, *LPLEVELUPMAGIC_ALL;
+
+typedef struct tagENEMYPOS
+{
+   struct {
+      WORD      x;
+      WORD      y;
+   } pos[MAX_ENEMIES_IN_TEAM][MAX_ENEMIES_IN_TEAM];
+} ENEMYPOS, *LPENEMYPOS;
+
+// Exp. points needed for the next level
+typedef WORD LEVELUPEXP, *LPLEVELUPEXP;
+
+// game data which is available in data files.
+typedef struct tagGAMEDATA
+{
+   LPEVENTOBJECT           lprgEventObject;
+   int                     nEventObject;
+
+   SCENE                   rgScene[MAX_SCENES];
+   OBJECT                  rgObject[MAX_OBJECTS];
+
+   LPSCRIPTENTRY           lprgScriptEntry;
+   int                     nScriptEntry;
+
+   LPSTORE                 lprgStore;
+   int                     nStore;
+
+   LPENEMY                 lprgEnemy;
+   int                     nEnemy;
+
+   LPENEMYTEAM             lprgEnemyTeam;
+   int                     nEnemyTeam;
+
+   PLAYERROLES             PlayerRoles;
+
+   LPMAGIC                 lprgMagic;
+   int                     nMagic;
+
+   LPBATTLEFIELD           lprgBattleField;
+   int                     nBattleField;
+
+   LPLEVELUPMAGIC_ALL      lprgLevelUpMagic;
+   int                     nLevelUpMagic;
+
+   ENEMYPOS                EnemyPos;
+   LEVELUPEXP              rgLevelUpExp[MAX_LEVELS + 1];
+
+   WORD                    rgwBattleEffectIndex[10][2];
+} GAMEDATA, *LPGAMEDATA;
+
+typedef struct tagFILES
+{
+   FILE            *fpFBP;      // battlefield background images
+   FILE            *fpMGO;      // sprites in scenes
+   FILE            *fpBALL;     // item bitmaps
+   FILE            *fpDATA;     // misc data
+   FILE            *fpF;        // player sprites during battle
+   FILE            *fpFIRE;     // fire effect sprites
+   FILE            *fpRGM;      // character face bitmaps
+   FILE            *fpSSS;      // script data
+} FILES, *LPFILES;
+
+// player party
+typedef struct tagPARTY
+{
+   WORD             wPlayerRole;         // player role
+   SHORT            x, y;                // position
+   WORD             wFrame;              // current frame number
+   WORD             wImageOffset;        // FIXME: ???
+} PARTY, *LPPARTY;
+
+// player trail, used for other party members to follow the main party member
+typedef struct tagTRAIL
+{
+   WORD             x, y;          // position
+   WORD             wDirection;    // direction
+} TRAIL, *LPTRAIL;
+
+typedef struct tagEXPERIENCE
+{
+   WORD         wExp;                // current experience points
+   WORD         wReserved;
+   WORD         wLevel;              // current level
+   WORD         wCount;
+} EXPERIENCE, *LPEXPERIENCE;
+
+typedef struct tagALLEXPERIENCE
+{
+   EXPERIENCE        rgPrimaryExp[MAX_PLAYER_ROLES];
+   EXPERIENCE        rgHealthExp[MAX_PLAYER_ROLES];
+   EXPERIENCE        rgMagicExp[MAX_PLAYER_ROLES];
+   EXPERIENCE        rgAttackExp[MAX_PLAYER_ROLES];
+   EXPERIENCE        rgMagicPowerExp[MAX_PLAYER_ROLES];
+   EXPERIENCE        rgDefenseExp[MAX_PLAYER_ROLES];
+   EXPERIENCE        rgDexterityExp[MAX_PLAYER_ROLES];
+   EXPERIENCE        rgFleeExp[MAX_PLAYER_ROLES];
+} ALLEXPERIENCE, *LPALLEXPERIENCE;
+
+typedef struct tagPOISONSTATUS
+{
+   WORD              wPoisonID;       // kind of the poison
+   WORD              wPoisonScript;   // script entry
+} POISONSTATUS, *LPPOISONSTATUS;
+
+typedef struct tagGLOBALVARS
+{
+   FILES            f;
+   GAMEDATA         g;
+
+   BYTE             bCurrentSaveSlot;    // current save slot (1-5)
+   int              iCurMainMenuItem;    // current main menu item number
+   int              iCurSystemMenuItem;  // current system menu item number
+   int              iCurInvMenuItem;     // current inventory menu item number
+   int              iCurPlayingRNG;      // current playing RNG animation
+   BOOL             fGameStart;          // TRUE if the has just started
+   BOOL             fEnteringScene;      // TRUE if entering a new scene
+   BOOL             fNeedToFadeIn;       // TRUE if need to fade in when drawing scene
+   BOOL             fInBattle;           // TRUE if in battle
+   BOOL             fAutoBattle;         // TRUE if auto-battle
+#ifndef PAL_CLASSIC
+   BYTE             bBattleSpeed;        // Battle Speed (1 = Fastest, 5 = Slowest)
+#endif
+   WORD             wLastUnequippedItem; // last unequipped item
+
+   PLAYERROLES      rgEquipmentEffect[MAX_PLAYER_EQUIPMENTS + 1]; // equipment effects
+   WORD             rgPlayerStatus[MAX_PLAYER_ROLES][kStatusAll]; // player status
+
+   PAL_POS          viewport;            // viewport coordination
+   PAL_POS          partyoffset;
+   WORD             wLayer;
+   WORD             wMaxPartyMemberIndex;// max index of members in party (0 to MAX_PLAYERS_IN_PARTY - 1)
+   PARTY            rgParty[MAX_PLAYABLE_PLAYER_ROLES]; // player party
+   TRAIL            rgTrail[MAX_PLAYABLE_PLAYER_ROLES]; // player trail
+   WORD             wPartyDirection;     // direction of the party
+   WORD             wNumScene;           // current scene number
+   WORD             wNumPalette;         // current palette number
+   BOOL             fNightPalette;       // TRUE if use the darker night palette
+   WORD             wNumMusic;           // current music number
+   WORD             wNumBattleMusic;     // current music number in battle
+   WORD             wNumBattleField;     // current battle field number
+   WORD             wCollectValue;       // value of "collected" items
+   WORD             wScreenWave;         // level of screen waving
+   SHORT            sWaveProgression;
+   WORD             wChaseRange;
+   WORD             wChasespeedChangeCycles;
+   USHORT           nFollower;
+
+   DWORD            dwCash;              // amount of cash
+
+   ALLEXPERIENCE    Exp;                 // experience status
+   POISONSTATUS     rgPoisonStatus[MAX_POISONS][MAX_PLAYABLE_PLAYER_ROLES]; // poison status
+   INVENTORY        rgInventory[MAX_INVENTORY];  // inventory status
+
+   LPOBJECTDESC     lpObjectDesc;
+
+   DWORD            dwFrameNum;
+} GLOBALVARS, *LPGLOBALVARS;
+
+typedef struct tagSAVEDGAME
+{
+   WORD             wSavedTimes;             // saved times
+   WORD             wViewportX, wViewportY;  // viewport location
+   WORD             nPartyMember;            // number of members in party
+   WORD             wNumScene;               // scene number
+   WORD             wPaletteOffset;
+   WORD             wPartyDirection;         // party direction
+   WORD             wNumMusic;               // music number
+   WORD             wNumBattleMusic;         // battle music number
+   WORD             wNumBattleField;         // battle field number
+   WORD             wScreenWave;             // level of screen waving
+   WORD             wBattleSpeed;            // battle speed
+   WORD             wCollectValue;           // value of "collected" items
+   WORD             wLayer;
+   WORD             wChaseRange;
+   WORD             wChasespeedChangeCycles;
+   WORD             nFollower;
+   WORD             rgwReserved2[3];         // unused
+   DWORD            dwCash;                  // amount of cash
+   PARTY            rgParty[MAX_PLAYABLE_PLAYER_ROLES];       // player party
+   TRAIL            rgTrail[MAX_PLAYABLE_PLAYER_ROLES];       // player trail
+   ALLEXPERIENCE    Exp;                     // experience data
+   PLAYERROLES      PlayerRoles;
+   POISONSTATUS     rgPoisonStatus[MAX_POISONS][MAX_PLAYABLE_PLAYER_ROLES]; // poison status
+   INVENTORY        rgInventory[MAX_INVENTORY];               // inventory status
+   SCENE            rgScene[MAX_SCENES];
+   OBJECT           rgObject[MAX_OBJECTS];
+   EVENTOBJECT      rgEventObject[MAX_EVENT_OBJECTS];
+} SAVEDGAME, *LPSAVEDGAME;
+
+extern LPGLOBALVARS gpGlobals;
+
+INT
+PAL_InitGlobals(
+   VOID
+);
+
+VOID
+PAL_FreeGlobals(
+   VOID
+);
+
+VOID
+PAL_SaveGame(
+   LPCSTR        szFileName,
+   WORD          wSavedTimes
+);
+
+VOID
+PAL_InitGameData(
+   INT           iSaveSlot
+);
+
+BOOL
+PAL_AddItemToInventory(
+   WORD          wObjectID,
+   INT           iNum
+);
+
+BOOL
+PAL_IncreaseHPMP(
+   WORD          wPlayerRole,
+   SHORT         sHP,
+   SHORT         sMP
+);
+
+INT
+PAL_GetItemAmount(
+   WORD        wItem
+);
+
+VOID
+PAL_UpdateEquipments(
+   VOID
+);
+
+VOID
+PAL_CompressInventory(
+   VOID
+);
+
+VOID
+PAL_RemoveEquipmentEffect(
+   WORD         wPlayerRole,
+   WORD         wEquipPart
+);
+
+VOID
+PAL_AddPoisonForPlayer(
+   WORD           wPlayerRole,
+   WORD           wPoisonID
+);
+
+VOID
+PAL_CurePoisonByKind(
+   WORD           wPlayerRole,
+   WORD           wPoisonID
+);
+
+VOID
+PAL_CurePoisonByLevel(
+   WORD           wPlayerRole,
+   WORD           wMaxLevel
+);
+
+BOOL
+PAL_IsPlayerPoisonedByLevel(
+   WORD           wPlayerRole,
+   WORD           wMinLevel
+);
+
+BOOL
+PAL_IsPlayerPoisonedByKind(
+   WORD           wPlayerRole,
+   WORD           wPoisonID
+);
+
+WORD
+PAL_GetPlayerAttackStrength(
+   WORD           wPlayerRole
+);
+
+WORD
+PAL_GetPlayerMagicStrength(
+   WORD           wPlayerRole
+);
+
+WORD
+PAL_GetPlayerDefense(
+   WORD           wPlayerRole
+);
+
+WORD
+PAL_GetPlayerDexterity(
+   WORD           wPlayerRole
+);
+
+WORD
+PAL_GetPlayerFleeRate(
+   WORD           wPlayerRole
+);
+
+WORD
+PAL_GetPlayerPoisonResistance(
+   WORD           wPlayerRole
+);
+
+WORD
+PAL_GetPlayerElementalResistance(
+   WORD           wPlayerRole,
+   INT            iAttrib
+);
+
+WORD
+PAL_GetPlayerBattleSprite(
+   WORD           wPlayerRole
+);
+
+WORD
+PAL_GetPlayerCooperativeMagic(
+   WORD           wPlayerRole
+);
+
+BOOL
+PAL_PlayerCanAttackAll(
+   WORD           wPlayerRole
+);
+
+BOOL
+PAL_AddMagic(
+   WORD           wPlayerRole,
+   WORD           wMagic
+);
+
+VOID
+PAL_RemoveMagic(
+   WORD           wPlayerRole,
+   WORD           wMagic
+);
+
+VOID
+PAL_SetPlayerStatus(
+   WORD         wPlayerRole,
+   WORD         wStatusID,
+   WORD         wNumRound
+);
+
+VOID
+PAL_RemovePlayerStatus(
+   WORD         wPlayerRole,
+   WORD         wStatusID
+);
+
+VOID
+PAL_ClearAllPlayerStatus(
+   VOID
+);
+
+VOID
+PAL_PlayerLevelUp(
+   WORD          wPlayerRole,
+   WORD          wNumLevel
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 674 - 0
gpl.txt

@@ -0,0 +1,674 @@
+                     GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+   a) The work must carry prominent notices stating that you modified
+   it, and giving a relevant date.
+
+   b) The work must carry prominent notices stating that it is
+   released under this License and any conditions added under section
+   7.  This requirement modifies the requirement in section 4 to
+   "keep intact all notices".
+
+   c) You must license the entire work, as a whole, under this
+   License to anyone who comes into possession of a copy.  This
+   License will therefore apply, along with any applicable section 7
+   additional terms, to the whole of the work, and all its parts,
+   regardless of how they are packaged.  This License gives no
+   permission to license the work in any other way, but it does not
+   invalidate such permission if you have separately received it.
+
+   d) If the work has interactive user interfaces, each must display
+   Appropriate Legal Notices; however, if the Program has interactive
+   interfaces that do not display Appropriate Legal Notices, your
+   work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+   a) Convey the object code in, or embodied in, a physical product
+   (including a physical distribution medium), accompanied by the
+   Corresponding Source fixed on a durable physical medium
+   customarily used for software interchange.
+
+   b) Convey the object code in, or embodied in, a physical product
+   (including a physical distribution medium), accompanied by a
+   written offer, valid for at least three years and valid for as
+   long as you offer spare parts or customer support for that product
+   model, to give anyone who possesses the object code either (1) a
+   copy of the Corresponding Source for all the software in the
+   product that is covered by this License, on a durable physical
+   medium customarily used for software interchange, for a price no
+   more than your reasonable cost of physically performing this
+   conveying of source, or (2) access to copy the
+   Corresponding Source from a network server at no charge.
+
+   c) Convey individual copies of the object code with a copy of the
+   written offer to provide the Corresponding Source.  This
+   alternative is allowed only occasionally and noncommercially, and
+   only if you received the object code with such an offer, in accord
+   with subsection 6b.
+
+   d) Convey the object code by offering access from a designated
+   place (gratis or for a charge), and offer equivalent access to the
+   Corresponding Source in the same way through the same place at no
+   further charge.  You need not require recipients to copy the
+   Corresponding Source along with the object code.  If the place to
+   copy the object code is a network server, the Corresponding Source
+   may be on a different server (operated by you or a third party)
+   that supports equivalent copying facilities, provided you maintain
+   clear directions next to the object code saying where to find the
+   Corresponding Source.  Regardless of what server hosts the
+   Corresponding Source, you remain obligated to ensure that it is
+   available for as long as needed to satisfy these requirements.
+
+   e) Convey the object code using peer-to-peer transmission, provided
+   you inform other peers where the object code and Corresponding
+   Source of the work are being offered to the general public at no
+   charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+   a) Disclaiming warranty or limiting liability differently from the
+   terms of sections 15 and 16 of this License; or
+
+   b) Requiring preservation of specified reasonable legal notices or
+   author attributions in that material or in the Appropriate Legal
+   Notices displayed by works containing it; or
+
+   c) Prohibiting misrepresentation of the origin of that material, or
+   requiring that modified versions of such material be marked in
+   reasonable ways as different from the original version; or
+
+   d) Limiting the use for publicity purposes of names of licensors or
+   authors of the material; or
+
+   e) Declining to grant rights under trademark law for use of some
+   trade names, trademarks, or service marks; or
+
+   f) Requiring indemnification of licensors and authors of that
+   material by anyone who conveys the material (or modified versions of
+   it) with contractual assumptions of liability to the recipient, for
+   any liability that these contractual assumptions directly impose on
+   those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+   <one line to give the program's name and a brief idea of what it does.>
+   Copyright (C) <year>  <name of author>
+
+   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 3 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, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+   <program>  Copyright (C) <year>  <name of author>
+   This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+   This is free software, and you are welcome to redistribute it
+   under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.

+ 821 - 0
input.c

@@ -0,0 +1,821 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// Portions Copyright (c) 2009, netwan.
+//
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "main.h"
+#include <math.h>
+
+PALINPUTSTATE            g_InputState;
+static SDL_Joystick     *g_pJoy = NULL;
+BOOL                     g_fUseJoystick = TRUE;
+
+#if defined(GPH)
+#define MIN_DEADZONE -16384
+#define MAX_DEADZONE 16384
+#endif
+
+static VOID
+PAL_KeyboardEventFilter(
+   const SDL_Event       *lpEvent
+)
+/*++
+  Purpose:
+
+    Handle keyboard events.
+
+  Parameters:
+
+    [IN]  lpEvent - pointer to the event.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   switch (lpEvent->type)
+   {
+   case SDL_KEYDOWN:
+      //
+      // Pressed a key
+      //
+      if (lpEvent->key.keysym.mod & KMOD_ALT)
+      {
+         if (lpEvent->key.keysym.sym == SDLK_RETURN)
+         {
+            //
+            // Pressed Alt+Enter (toggle fullscreen)...
+            //
+            VIDEO_ToggleFullscreen();
+            return;
+         }
+         else if (lpEvent->key.keysym.sym == SDLK_F4)
+         {
+            //
+            // Pressed Alt+F4 (Exit program)...
+            //
+            PAL_Shutdown();
+            exit(0);
+         }
+      }
+
+      switch (lpEvent->key.keysym.sym)
+      {
+#ifdef __SYMBIAN32__
+      //
+      // Symbian-specific stuff
+      //
+      case SDLK_0:
+         VIDEO_ToggleScaleScreen();
+         break;
+      case SDLK_1:
+         SOUND_AdjustVolume(0);
+         break;
+      case SDLK_3:
+         SOUND_AdjustVolume(1);
+         break;
+#endif
+
+      case SDLK_UP:
+      case SDLK_KP8:
+         g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);
+         g_InputState.dir = kDirNorth;
+         g_InputState.dwKeyPress |= kKeyUp;
+         break;
+
+      case SDLK_DOWN:
+      case SDLK_KP2:
+         g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);
+         g_InputState.dir = kDirSouth;
+         g_InputState.dwKeyPress |= kKeyDown;
+         break;
+
+      case SDLK_LEFT:
+      case SDLK_KP4:
+         g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);
+         g_InputState.dir = kDirWest;
+         g_InputState.dwKeyPress |= kKeyLeft;
+         break;
+
+      case SDLK_RIGHT:
+      case SDLK_KP6:
+         g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);
+         g_InputState.dir = kDirEast;
+         g_InputState.dwKeyPress |= kKeyRight;
+         break;
+
+#if defined(DINGOO)
+      case SDLK_SPACE:
+		 g_InputState.dwKeyPress = kKeyMenu;
+         break;
+
+      case SDLK_LCTRL:
+		 g_InputState.dwKeyPress = kKeySearch;
+         break;
+#else
+      case SDLK_ESCAPE:
+      case SDLK_INSERT:
+      case SDLK_LALT:
+      case SDLK_RALT:
+      case SDLK_KP0:
+         g_InputState.dwKeyPress |= kKeyMenu;
+         break;
+
+      case SDLK_RETURN:
+      case SDLK_SPACE:
+      case SDLK_KP_ENTER:
+      case SDLK_LCTRL:
+         g_InputState.dwKeyPress |= kKeySearch;
+         break;
+
+      case SDLK_PAGEUP:
+      case SDLK_KP9:
+         g_InputState.dwKeyPress |= kKeyPgUp;
+         break;
+
+      case SDLK_PAGEDOWN:
+      case SDLK_KP3:
+         g_InputState.dwKeyPress |= kKeyPgDn;
+         break;
+
+      case SDLK_7: //7 for mobile device
+      case SDLK_r:
+         g_InputState.dwKeyPress |= kKeyRepeat;
+         break;
+
+      case SDLK_2: //2 for mobile device
+      case SDLK_a:
+         g_InputState.dwKeyPress |= kKeyAuto;
+         break;
+
+      case SDLK_d:
+         g_InputState.dwKeyPress |= kKeyDefend;
+         break;
+
+      case SDLK_e:
+         g_InputState.dwKeyPress |= kKeyUseItem;
+         break;
+
+      case SDLK_w:
+         g_InputState.dwKeyPress |= kKeyThrowItem;
+         break;
+
+      case SDLK_q:
+         g_InputState.dwKeyPress |= kKeyFlee;
+         break;
+
+      case SDLK_s:
+         g_InputState.dwKeyPress |= kKeyStatus;
+         break;
+
+      case SDLK_f:
+      case SDLK_5: // 5 for mobile device
+         g_InputState.dwKeyPress |= kKeyForce;
+         break;
+
+      case SDLK_HASH: //# for mobile device
+      case SDLK_p:
+         VIDEO_SaveScreenshot();
+         break;
+#endif
+
+      default:
+         break;
+      }
+      break;
+
+   case SDL_KEYUP:
+      //
+      // Released a key
+      //
+      switch (lpEvent->key.keysym.sym)
+      {
+      case SDLK_UP:
+      case SDLK_KP8:
+         if (g_InputState.dir == kDirNorth)
+         {
+            g_InputState.dir = g_InputState.prevdir;
+         }
+         g_InputState.prevdir = kDirUnknown;
+         break;
+
+      case SDLK_DOWN:
+      case SDLK_KP2:
+         if (g_InputState.dir == kDirSouth)
+         {
+            g_InputState.dir = g_InputState.prevdir;
+         }
+         g_InputState.prevdir = kDirUnknown;
+         break;
+
+      case SDLK_LEFT:
+      case SDLK_KP4:
+         if (g_InputState.dir == kDirWest)
+         {
+            g_InputState.dir = g_InputState.prevdir;
+         }
+         g_InputState.prevdir = kDirUnknown;
+         break;
+
+      case SDLK_RIGHT:
+      case SDLK_KP6:
+         if (g_InputState.dir == kDirEast)
+         {
+            g_InputState.dir = g_InputState.prevdir;
+         }
+         g_InputState.prevdir = kDirUnknown;
+         break;
+
+      default:
+         break;
+      }
+      break;
+   }
+}
+
+static VOID
+PAL_MouseEventFilter(
+   const SDL_Event *lpEvent
+)
+/*++
+  Purpose:
+
+    Handle mouse events.
+
+  Parameters:
+
+    [IN]  lpEvent - pointer to the event.
+
+  Return value:
+
+    None.
+
+--*/
+{
+#ifdef PAL_HAS_MOUSE
+   static short hitTest = 0; // Double click detect;	
+   const SDL_VideoInfo *vi;
+
+   double       screenWidth, gridWidth;
+   double       screenHeight, gridHeight;
+   double       mx, my;
+   double       thumbx;
+   double       thumby;
+   INT          gridIndex;
+   BOOL			isLeftMouseDBClick = FALSE;
+   BOOL			isLeftMouseClick = FALSE;
+   BOOL			isRightMouseClick = FALSE;
+   static INT   lastReleaseButtonTime, lastPressButtonTime, betweenTime;
+   static INT   lastPressx = 0;
+   static INT   lastPressy = 0;
+   static INT   lastReleasex = 0;
+   static INT   lastReleasey = 0;
+
+   if (lpEvent->type!= SDL_MOUSEBUTTONDOWN && lpEvent->type != SDL_MOUSEBUTTONUP)
+      return;
+
+   vi = SDL_GetVideoInfo();
+   screenWidth = vi->current_w;
+   screenHeight = vi->current_h;
+   gridWidth = screenWidth / 3;
+   gridHeight = screenHeight / 3;
+   mx = lpEvent->button.x;
+   my = lpEvent->button.y;
+   thumbx = ceil(mx / gridWidth);
+   thumby = floor(my / gridHeight);
+   gridIndex = thumbx + thumby * 3 - 1;
+   
+   switch (lpEvent->type)
+   {
+   case SDL_MOUSEBUTTONDOWN:
+      lastPressButtonTime = SDL_GetTicks();
+      lastPressx = lpEvent->button.x;
+      lastPressy = lpEvent->button.y;
+      switch (gridIndex)
+      {
+      case 2:
+         g_InputState.prevdir = g_InputState.dir;
+         g_InputState.dir = kDirNorth;
+         break;
+      case 6:
+         g_InputState.prevdir = g_InputState.dir;
+         g_InputState.dir = kDirSouth;
+         break;
+      case 0:
+         g_InputState.prevdir = g_InputState.dir;
+         g_InputState.dir = kDirWest;
+         break;
+      case 8:
+         g_InputState.prevdir = g_InputState.dir;
+         g_InputState.dir = kDirEast;
+         break;
+      case 1:
+    	 //g_InputState.prevdir = g_InputState.dir;
+    	 //g_InputState.dir = kDirNorth;
+         g_InputState.dwKeyPress |= kKeyUp;
+         break;
+      case 7:
+    	 //g_InputState.prevdir = g_InputState.dir;
+    	 //g_InputState.dir = kDirSouth; 
+         g_InputState.dwKeyPress |= kKeyDown;
+         break;
+      case 3:
+    	 //g_InputState.prevdir = g_InputState.dir;
+    	 //g_InputState.dir = kDirWest;
+    	 g_InputState.dwKeyPress |= kKeyLeft;
+         break;
+      case 5:
+         //g_InputState.prevdir = g_InputState.dir;
+         //g_InputState.dir = kDirEast;
+         g_InputState.dwKeyPress |= kKeyRight;
+         break;
+      }
+      break;
+   case SDL_MOUSEBUTTONUP:
+      lastReleaseButtonTime = SDL_GetTicks();
+      lastReleasex = lpEvent->button.x;
+      lastReleasey = lpEvent->button.y;
+      hitTest ++;
+      if (abs(lastPressx - lastReleasex) < 25 &&
+                     abs(lastPressy - lastReleasey) < 25)
+      {
+		  betweenTime = lastReleaseButtonTime - lastPressButtonTime;
+		  if (betweenTime >500)
+		  {
+			  isRightMouseClick = TRUE;
+		  }
+		  else if (betweenTime >=0)
+		  {
+			  if((betweenTime < 100) && (hitTest >= 2))
+			  {
+				  isLeftMouseClick = TRUE;
+			  	  hitTest = 0;  
+			  }
+			  else
+			  {  
+				  isLeftMouseClick = TRUE;
+				  if(betweenTime > 100)
+				  {
+					  hitTest = 0;
+				  }
+				  
+			  }
+		  }
+      }
+      switch (gridIndex)
+      {
+      case 2:
+    	 if( isLeftMouseDBClick )
+		 {
+			 SOUND_AdjustVolume(1);
+			 break;
+		 }
+      case 6:
+      case 0:
+    	 if( isLeftMouseDBClick )
+		 {
+			 SOUND_AdjustVolume(0);
+			 break;
+		 }
+      case 7:
+    	  if (isRightMouseClick) //repeat attack
+    	  {
+    	     g_InputState.dwKeyPress |= kKeyRepeat;
+    	     break;
+    	  }
+      case 8:
+         g_InputState.dir = kDirUnknown;
+         g_InputState.prevdir = kDirUnknown;
+         break;
+      case 1:
+    	 if( isRightMouseClick )
+		 {
+			 g_InputState.dwKeyPress |= kKeyForce;
+		 }
+    	 break;
+      case 3:
+    	 if( isRightMouseClick )
+		 {
+			 g_InputState.dwKeyPress |= kKeyAuto;
+		 }
+    	 break;
+      case 5:
+    	 if( isRightMouseClick )
+		 {
+			 g_InputState.dwKeyPress |= kKeyDefend;
+		 }
+		 break;
+      case 4:
+		if (isRightMouseClick) // menu
+		{
+		   g_InputState.dwKeyPress |= kKeyMenu;
+		}
+		else if (isLeftMouseClick) // search
+		{
+		   g_InputState.dwKeyPress |= kKeySearch;
+		}
+		
+        break;
+      }
+      break;
+   }
+#endif
+}
+
+static VOID
+PAL_JoystickEventFilter(
+   const SDL_Event       *lpEvent
+)
+/*++
+  Purpose:
+
+    Handle joystick events.
+
+  Parameters:
+
+    [IN]  lpEvent - pointer to the event.
+
+  Return value:
+
+    None.
+
+--*/
+{
+#ifdef PAL_HAS_JOYSTICKS
+   switch (lpEvent->type)
+   {
+#if defined (GEKKO)
+   case SDL_JOYHATMOTION:
+      switch (lpEvent->jhat.value)
+      {
+      case SDL_HAT_LEFT:
+        g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);
+        g_InputState.dir = kDirWest;
+        g_InputState.dwKeyPress = kKeyLeft;
+        break;
+
+      case SDL_HAT_RIGHT:
+        g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);
+        g_InputState.dir = kDirEast;
+        g_InputState.dwKeyPress = kKeyRight;
+        break;
+
+      case SDL_HAT_UP:
+        g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);
+        g_InputState.dir = kDirNorth;
+        g_InputState.dwKeyPress = kKeyUp;
+        break;
+
+      case SDL_HAT_DOWN:
+        g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);
+        g_InputState.dir = kDirSouth;
+        g_InputState.dwKeyPress = kKeyDown;
+        break;
+      }
+      break;
+#else
+   case SDL_JOYAXISMOTION:
+      //
+      // Moved an axis on joystick
+      //
+      switch (lpEvent->jaxis.axis)
+      {
+      case 0:
+         //
+         // X axis
+         //
+#if defined(GPH)
+		if (lpEvent->jaxis.value > MAX_DEADZONE) {
+			g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);
+			g_InputState.dir = kDirEast;
+			g_InputState.dwKeyPress = kKeyRight;
+		} else if (lpEvent->jaxis.value < MIN_DEADZONE) {
+			g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);
+			g_InputState.dir = kDirWest;
+			g_InputState.dwKeyPress = kKeyLeft;
+		} else {
+			g_InputState.dir = kDirUnknown;
+		}
+#else
+         if (lpEvent->jaxis.value > 20000)
+         {
+            if (g_InputState.dir != kDirEast)
+            {
+               g_InputState.dwKeyPress |= kKeyRight;
+            }
+            g_InputState.prevdir = g_InputState.dir;
+            g_InputState.dir = kDirEast;
+         }
+         else if (lpEvent->jaxis.value < -20000)
+         {
+            if (g_InputState.dir != kDirWest)
+            {
+               g_InputState.dwKeyPress |= kKeyLeft;
+            }
+            g_InputState.prevdir = g_InputState.dir;
+            g_InputState.dir = kDirWest;
+         }
+         else
+         {
+            if (g_InputState.prevdir != kDirEast &&
+               g_InputState.prevdir != kDirWest)
+            {
+               g_InputState.dir = g_InputState.prevdir;
+            }
+            g_InputState.prevdir = kDirUnknown;
+         }
+#endif
+         break;
+
+      case 1:
+         //
+         // Y axis
+         //
+#if defined(GPH)
+		if (lpEvent->jaxis.value > MAX_DEADZONE) {
+			g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);
+			g_InputState.dir = kDirSouth;
+			g_InputState.dwKeyPress = kKeyDown;
+		} else if (lpEvent->jaxis.value < MIN_DEADZONE) {
+			g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);
+			g_InputState.dir = kDirNorth;
+			g_InputState.dwKeyPress = kKeyUp;
+		} else {
+			g_InputState.dir = kDirUnknown;
+		}
+#else
+         if (lpEvent->jaxis.value > 20000)
+         {
+            if (g_InputState.dir != kDirSouth)
+            {
+               g_InputState.dwKeyPress |= kKeyDown;
+            }
+            g_InputState.prevdir = g_InputState.dir;
+            g_InputState.dir = kDirSouth;
+         }
+         else if (lpEvent->jaxis.value < -20000)
+         {
+            if (g_InputState.dir != kDirNorth)
+            {
+               g_InputState.dwKeyPress |= kKeyUp;
+            }
+            g_InputState.prevdir = g_InputState.dir;
+            g_InputState.dir = kDirNorth;
+         }
+         else
+         {
+            if (g_InputState.prevdir != kDirNorth &&
+               g_InputState.prevdir != kDirSouth)
+            {
+               g_InputState.dir = g_InputState.prevdir;
+            }
+            g_InputState.prevdir = kDirUnknown;
+         }
+#endif
+         break;
+      }
+      break;
+#endif
+
+   case SDL_JOYBUTTONDOWN:
+      //
+      // Pressed the joystick button
+      //
+#if defined(GPH)
+      switch (lpEvent->jbutton.button)
+      {
+#if defined(GP2XWIZ)
+		case 14:
+#elif defined(CAANOO)
+		case 3:
+#endif
+			g_InputState.dwKeyPress = kKeyMenu;
+			break;
+
+#if defined(GP2XWIZ)
+		case 13:
+#elif defined(CAANOO)
+		case 2:
+#endif
+			g_InputState.dwKeyPress = kKeySearch;
+			break;
+#else
+#if defined(GEKKO)
+      switch (lpEvent->jbutton.button)
+      {
+		case 2:
+         g_InputState.dwKeyPress |= kKeyMenu;
+         break;
+
+		case 3:
+         g_InputState.dwKeyPress |= kKeySearch;
+         break;
+#else
+      switch (lpEvent->jbutton.button & 1)
+      {
+      case 0:
+         g_InputState.dwKeyPress |= kKeyMenu;
+         break;
+
+      case 1:
+         g_InputState.dwKeyPress |= kKeySearch;
+         break;
+#endif
+#endif
+      }
+      break;
+   }
+#endif
+}
+
+#if SDL_MAJOR_VERSION == 1 && SDL_MINOR_VERSION <= 2
+static int SDLCALL
+PAL_EventFilter(
+   const SDL_Event       *lpEvent
+)
+#else
+static int SDLCALL
+PAL_EventFilter(
+   void                  *userdata,
+   const SDL_Event       *lpEvent
+)
+#endif
+/*++
+  Purpose:
+
+    SDL event filter function. A filter to process all events.
+
+  Parameters:
+
+    [IN]  lpEvent - pointer to the event.
+
+  Return value:
+
+    1 = the event will be added to the internal queue.
+    0 = the event will be dropped from the queue.
+
+--*/
+{
+   switch (lpEvent->type)
+   {
+   case SDL_VIDEORESIZE:
+      //
+      // resized the window
+      //
+      VIDEO_Resize(lpEvent->resize.w, lpEvent->resize.h);
+      break;
+
+   case SDL_QUIT:
+      //
+      // clicked on the close button of the window. Quit immediately.
+      //
+      PAL_Shutdown();
+      exit(0);
+   }
+
+   PAL_KeyboardEventFilter(lpEvent);
+   PAL_MouseEventFilter(lpEvent);
+   PAL_JoystickEventFilter(lpEvent);
+
+   //
+   // All events are handled here; don't put anything to the internal queue
+   //
+   return 0;
+}
+
+VOID
+PAL_ClearKeyState(
+   VOID
+)
+/*++
+  Purpose:
+
+    Clear the record of pressed keys.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   g_InputState.dwKeyPress = 0;
+}
+
+VOID
+PAL_InitInput(
+   VOID
+)
+/*++
+  Purpose:
+
+    Initialize the input subsystem.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   memset(&g_InputState, 0, sizeof(g_InputState));
+   g_InputState.dir = kDirUnknown;
+   g_InputState.prevdir = kDirUnknown;
+#if SDL_MAJOR_VERSION == 1 && SDL_MINOR_VERSION <= 2
+   SDL_SetEventFilter(PAL_EventFilter);
+#else
+   SDL_SetEventFilter(PAL_EventFilter, NULL);
+#endif
+
+   //
+   // Check for joystick
+   //
+#ifdef PAL_HAS_JOYSTICKS
+   if (SDL_NumJoysticks() > 0 && g_fUseJoystick)
+   {
+      g_pJoy = SDL_JoystickOpen(0);
+      if (g_pJoy != NULL)
+      {
+         SDL_JoystickEventState(SDL_ENABLE);
+      }
+   }
+#endif
+}
+
+VOID
+PAL_ShutdownInput(
+   VOID
+)
+/*++
+  Purpose:
+
+    Shutdown the input subsystem.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+#ifdef PAL_HAS_JOYSTICKS
+   if (SDL_JoystickOpened(0))
+   {
+      assert(g_pJoy != NULL);
+      SDL_JoystickClose(g_pJoy);
+      g_pJoy = NULL;
+   }
+#endif
+}
+
+VOID
+PAL_ProcessEvent(
+   VOID
+)
+/*++
+  Purpose:
+
+    Process all events.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+#ifdef PAL_HAS_NATIVEMIDI
+   MIDI_CheckLoop();
+#endif
+   while (SDL_PollEvent(NULL));
+}

+ 86 - 0
input.h

@@ -0,0 +1,86 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INPUT_H
+#define INPUT_H
+
+#include "common.h"
+#include "palcommon.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef struct tagPALINPUTSTATE
+{
+   PALDIRECTION           dir, prevdir;
+   DWORD                  dwKeyPress;
+} PALINPUTSTATE;
+
+extern PALINPUTSTATE g_InputState;
+
+enum PALKEY
+{
+   kKeyMenu        = (1 << 0),
+   kKeySearch      = (1 << 1),
+   kKeyDown        = (1 << 2),
+   kKeyLeft        = (1 << 3),
+   kKeyUp          = (1 << 4),
+   kKeyRight       = (1 << 5),
+   kKeyPgUp        = (1 << 6),
+   kKeyPgDn        = (1 << 7),
+   kKeyRepeat      = (1 << 8),
+   kKeyAuto        = (1 << 9),
+   kKeyDefend      = (1 << 10),
+   kKeyUseItem     = (1 << 11),
+   kKeyThrowItem   = (1 << 12),
+   kKeyFlee        = (1 << 13),
+   kKeyStatus      = (1 << 14),
+   kKeyForce       = (1 << 15),
+};
+
+VOID
+PAL_ClearKeyState(
+   VOID
+);
+
+VOID
+PAL_InitInput(
+   VOID
+);
+
+VOID
+PAL_ProcessEvent(
+   VOID
+);
+
+VOID
+PAL_ShutdownInput(
+   VOID
+);
+
+extern BOOL g_fUseJoystick;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 394 - 0
input_PSP.c

@@ -0,0 +1,394 @@
+//
+// Copyright (c) 2009, Pal_Bazzi.
+//
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "main.h"
+
+#ifdef PSP
+
+#include <math.h>
+#include <pspkernel.h>
+#include <pspctrl.h>
+#include <SDL_thread.h>
+
+PALINPUTSTATE            g_InputState;
+
+BOOL                     g_fUseJoystick = TRUE;
+
+static SceCtrlData pad;
+static SDL_sem *pad_sem = 0;
+static SDL_Thread *bthread = 0;
+static int running = 0;
+static unsigned int old_button=0;
+static unsigned char old_x = 0, old_y = 0;
+
+//
+// Collect pad data about once per frame
+//
+int PSP_JoystickUpdate(void *data)
+{
+	while (running)
+	{
+		SDL_SemWait(pad_sem);
+		sceCtrlPeekBufferPositive(&pad, 1); 
+		SDL_SemPost(pad_sem);
+		//
+		// Delay 1/60th of a second 
+		//
+		sceKernelDelayThread(1000000 / 60);  
+	}
+	return 0;
+}
+
+void PAL_calc_Axes(
+   unsigned char x,
+   unsigned char y
+)
+{
+   if(x<y && x+y<51)
+   {
+      g_InputState.dwKeyPress = kKeyLeft;
+      g_InputState.prevdir = g_InputState.dir;
+			g_InputState.dir = kDirWest;
+			return;
+   }
+   if(x<y && x+y>51)
+   {
+      g_InputState.dwKeyPress = kKeyDown;
+      g_InputState.prevdir = g_InputState.dir;
+			g_InputState.dir = kDirSouth;
+			return;
+   }
+   if(x>y && x+y<51)
+   {
+      g_InputState.dwKeyPress = kKeyUp;
+      g_InputState.prevdir = g_InputState.dir;
+			g_InputState.dir = kDirNorth;
+			return;
+   }
+   if(x>y && x+y>51)
+   {
+      g_InputState.dwKeyPress = kKeyRight;
+      g_InputState.prevdir = g_InputState.dir;
+			g_InputState.dir = kDirEast;
+			return;
+	 }
+	 g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);
+	 g_InputState.dir = kDirUnknown;
+}
+
+VOID
+PAL_JoystickEventFilter(
+   VOID
+)
+/*++
+  Purpose:
+
+    Handle joystick events.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   unsigned int button;
+   unsigned char x, y;
+ 
+   SDL_SemWait(pad_sem);
+   button = pad.Buttons;
+   x = pad.Lx;
+   y = pad.Ly;
+   SDL_SemPost(pad_sem);
+
+   //
+   //Axes
+   //
+   x /= 5;
+   y /= 5;
+   BOOL onCenter=(x>16 && x<32) && (y>16 && y<32);
+   if(!onCenter)
+   {
+		 if(old_x != x || old_y != y) 
+     {
+		    PAL_calc_Axes(x,y);
+		    old_y = y;
+		    old_x = x;
+		 }
+	 }
+	 else if (!button)
+	 {
+	   g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);
+	   g_InputState.dir = kDirUnknown;
+	 }
+
+   //
+   //Buttons
+   //
+   int changed = (button != old_button);
+   old_button = button;
+   if(changed)
+   {
+	   if (button & PSP_CTRL_UP)
+	   {
+			 g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);
+			 g_InputState.dir = kDirNorth;
+			 g_InputState.dwKeyPress = kKeyUp;
+			 return;
+	   } 
+		 if (button & PSP_CTRL_DOWN)
+		 {
+		 	 g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);
+			 g_InputState.dir = kDirSouth;
+			 g_InputState.dwKeyPress = kKeyDown;
+			 return;
+		 } 
+		 if (button & PSP_CTRL_LEFT)
+		 {
+			 g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);
+			 g_InputState.dir = kDirWest;
+			 g_InputState.dwKeyPress = kKeyLeft;
+			 return;
+		 } 
+		 if (button & PSP_CTRL_RIGHT)
+		 {
+			 g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);
+			 g_InputState.dir = kDirEast;
+			 g_InputState.dwKeyPress = kKeyRight;
+			 return;
+		 }   
+		 if (button & PSP_CTRL_SQUARE)
+		 {
+		   g_InputState.dwKeyPress = kKeyForce;
+			 return;
+		 }
+		 if (button & PSP_CTRL_TRIANGLE)
+		 {
+			g_InputState.dwKeyPress = kKeyThrowItem;
+			return;
+		 } 
+		 if (button & PSP_CTRL_CIRCLE)
+		 {
+			 g_InputState.dwKeyPress = kKeySearch;
+			 return;
+		 } 
+		 if (button & PSP_CTRL_CROSS)
+		 {
+			 g_InputState.dwKeyPress = kKeyMenu;
+			 return;
+		 } 
+		 if (button & PSP_CTRL_START)
+		 {
+			 g_InputState.dwKeyPress = kKeySearch;
+			 return;
+		 }
+		 if (button & PSP_CTRL_SELECT)
+		 {
+			 g_InputState.dwKeyPress = kKeyMenu;
+			 return;
+		 }
+		 if (button & PSP_CTRL_LTRIGGER)
+		 {
+			 g_InputState.dwKeyPress = kKeyUseItem;
+			 return;
+		 }
+		 if (button & PSP_CTRL_RTRIGGER)
+		 {
+			 g_InputState.dwKeyPress = kKeyRepeat;
+			 return;
+		 }
+	   g_InputState.prevdir = (gpGlobals->fInBattle ? kDirUnknown : g_InputState.dir);
+	   g_InputState.dir = kDirUnknown;
+   }
+}
+
+
+#if SDL_MAJOR_VERSION == 1 && SDL_MINOR_VERSION <= 2
+static int SDLCALL
+PAL_EventFilter(
+   const SDL_Event       *lpEvent
+)
+#else
+static int SDLCALL
+PAL_EventFilter(
+   void                  *userdata,
+   const SDL_Event       *lpEvent
+)
+#endif
+/*++
+  Purpose:
+
+    SDL event filter function. A filter to process all events.
+
+  Parameters:
+
+    [IN]  lpEvent - pointer to the event.
+
+  Return value:
+
+    1 = the event will be added to the internal queue.
+    0 = the event will be dropped from the queue.
+
+--*/
+{
+   switch (lpEvent->type)
+   {
+   case SDL_QUIT:
+      //
+      // clicked on the close button of the window. Quit immediately.
+      //
+      PAL_Shutdown();
+      exit(0);
+   }
+
+   //
+   // All events are handled here; don't put anything to the internal queue
+   //
+   return 0;
+}
+
+VOID
+PAL_ClearKeyState(
+   VOID
+)
+/*++
+  Purpose:
+
+    Clear the record of pressed keys.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   g_InputState.dwKeyPress = 0;
+}
+
+VOID
+PAL_InitInput(
+   VOID
+)
+/*++
+  Purpose:
+
+    Initialize the input subsystem.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{ 
+   memset(&g_InputState, 0, sizeof(g_InputState));
+   g_InputState.dir = kDirUnknown;
+   g_InputState.prevdir = kDirUnknown;
+#if SDL_MAJOR_VERSION == 1 && SDL_MINOR_VERSION <= 2
+   SDL_SetEventFilter(PAL_EventFilter);
+#else
+   SDL_SetEventFilter(PAL_EventFilter, NULL);
+#endif
+
+   //
+   // Setup input
+   //
+   sceCtrlSetSamplingCycle(0);
+   sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);
+   pad.Buttons = 0;
+
+	 //
+	 // Start thread to read data 
+	 //
+	 if((pad_sem =  SDL_CreateSemaphore(1)) == NULL)
+	 {
+		 TerminateOnError("Can't create input semaphore\n");
+		 return;
+	 }
+	 running = 1;
+	 if((bthread = SDL_CreateThread(PSP_JoystickUpdate, NULL)) == NULL)
+	 {
+		 TerminateOnError("Can't create input thread\n");
+		 return;
+	 }
+}
+
+VOID
+PAL_ShutdownInput(
+   VOID
+)
+/*++
+  Purpose:
+
+    Shutdown the input subsystem.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+	//
+	// Cleanup Threads and Semaphore. 
+	//
+	running = 0;
+	SDL_WaitThread(bthread, NULL);
+	SDL_DestroySemaphore(pad_sem);
+}
+
+VOID
+PAL_ProcessEvent(
+   VOID
+)
+/*++
+  Purpose:
+
+    Process all events.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   while (SDL_PollEvent(NULL));
+   PAL_JoystickEventFilter();
+}
+
+#endif

+ 429 - 0
itemmenu.c

@@ -0,0 +1,429 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "main.h"
+
+static int     g_iNumInventory = 0;
+static WORD    g_wItemFlags = 0;
+static BOOL    g_fNoDesc = FALSE;
+
+WORD
+PAL_ItemSelectMenuUpdate(
+   VOID
+)
+/*++
+  Purpose:
+
+    Initialize the item selection menu.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    The object ID of the selected item. 0 if cancelled, 0xFFFF if not confirmed.
+
+--*/
+{
+   int                i, j, k;
+   WORD               wObject;
+   BYTE               bColor;
+   static BYTE        bufImage[2048];
+   static WORD        wPrevImageIndex = 0xFFFF;
+
+   //
+   // Process input
+   //
+   if (g_InputState.dwKeyPress & kKeyUp)
+   {
+      gpGlobals->iCurInvMenuItem -= 3;
+   }
+   else if (g_InputState.dwKeyPress & kKeyDown)
+   {
+      gpGlobals->iCurInvMenuItem += 3;
+   }
+   else if (g_InputState.dwKeyPress & kKeyLeft)
+   {
+      gpGlobals->iCurInvMenuItem--;
+   }
+   else if (g_InputState.dwKeyPress & kKeyRight)
+   {
+      gpGlobals->iCurInvMenuItem++;
+   }
+   else if (g_InputState.dwKeyPress & kKeyPgUp)
+   {
+      gpGlobals->iCurInvMenuItem -= 3 * 7;
+   }
+   else if (g_InputState.dwKeyPress & kKeyPgDn)
+   {
+      gpGlobals->iCurInvMenuItem += 3 * 7;
+   }
+   else if (g_InputState.dwKeyPress & kKeyMenu)
+   {
+      return 0;
+   }
+
+   //
+   // Make sure the current menu item index is in bound
+   //
+   if (gpGlobals->iCurInvMenuItem < 0)
+   {
+      gpGlobals->iCurInvMenuItem = 0;
+   }
+   else if (gpGlobals->iCurInvMenuItem >= g_iNumInventory)
+   {
+      gpGlobals->iCurInvMenuItem = g_iNumInventory - 1;
+   }
+
+   //
+   // Redraw the box
+   //
+   PAL_CreateBox(PAL_XY(2, 0), 6, 17, 1, FALSE);
+
+   //
+   // Draw the texts in the current page
+   //
+   i = gpGlobals->iCurInvMenuItem / 3 * 3 - 3 * 4;
+   if (i < 0)
+   {
+      i = 0;
+   }
+
+   for (j = 0; j < 7; j++)
+   {
+      for (k = 0; k < 3; k++)
+      {
+         wObject = gpGlobals->rgInventory[i].wItem;
+         bColor = MENUITEM_COLOR;
+
+         if (i >= MAX_INVENTORY || wObject == 0)
+         {
+            //
+            // End of the list reached
+            //
+            j = 7;
+            break;
+         }
+
+         if (i == gpGlobals->iCurInvMenuItem)
+         {
+            if (!(gpGlobals->g.rgObject[wObject].item.wFlags & g_wItemFlags) ||
+               (SHORT)gpGlobals->rgInventory[i].nAmount <= (SHORT)gpGlobals->rgInventory[i].nAmountInUse)
+            {
+               //
+               // This item is not selectable
+               //
+               bColor = MENUITEM_COLOR_SELECTED_INACTIVE;
+            }
+            else
+            {
+               //
+               // This item is selectable
+               //
+               if (gpGlobals->rgInventory[i].nAmount == 0)
+               {
+                  bColor = MENUITEM_COLOR_EQUIPPEDITEM;
+               }
+               else
+               {
+                  bColor = MENUITEM_COLOR_SELECTED;
+               }
+            }
+         }
+         else if (!(gpGlobals->g.rgObject[wObject].item.wFlags & g_wItemFlags) ||
+            (SHORT)gpGlobals->rgInventory[i].nAmount <= (SHORT)gpGlobals->rgInventory[i].nAmountInUse)
+         {
+            //
+            // This item is not selectable
+            //
+            bColor = MENUITEM_COLOR_INACTIVE;
+         }
+         else if (gpGlobals->rgInventory[i].nAmount == 0)
+         {
+            bColor = MENUITEM_COLOR_EQUIPPEDITEM;
+         }
+
+         //
+         // Draw the text
+         //
+         PAL_DrawText(PAL_GetWord(wObject), PAL_XY(15 + k * 100, 12 + j * 18),
+            bColor, TRUE, FALSE);
+
+         //
+         // Draw the cursor on the current selected item
+         //
+         if (i == gpGlobals->iCurInvMenuItem)
+         {
+            PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_CURSOR),
+               gpScreen, PAL_XY(40 + k * 100, 22 + j * 18));
+         }
+
+         //
+         // Draw the amount of this item
+         //
+		 if ((SHORT)gpGlobals->rgInventory[i].nAmount - (SHORT)gpGlobals->rgInventory[i].nAmountInUse > 1)
+		 {
+            PAL_DrawNumber(gpGlobals->rgInventory[i].nAmount - gpGlobals->rgInventory[i].nAmountInUse,
+               2, PAL_XY(96 + k * 100, 17 + j * 18), kNumColorCyan, kNumAlignRight);
+		 }
+
+         i++;
+      }
+   }
+
+   //
+   // Draw the picture of current selected item
+   //
+   PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_ITEMBOX), gpScreen,
+      PAL_XY(5, 140));
+
+   wObject = gpGlobals->rgInventory[gpGlobals->iCurInvMenuItem].wItem;
+
+   if (gpGlobals->g.rgObject[wObject].item.wBitmap != wPrevImageIndex)
+   {
+      if (PAL_MKFReadChunk(bufImage, 2048,
+         gpGlobals->g.rgObject[wObject].item.wBitmap, gpGlobals->f.fpBALL) > 0)
+      {
+         wPrevImageIndex = gpGlobals->g.rgObject[wObject].item.wBitmap;
+      }
+      else
+      {
+         wPrevImageIndex = 0xFFFF;
+      }
+   }
+
+   if (wPrevImageIndex != 0xFFFF)
+   {
+      PAL_RLEBlitToSurface(bufImage, gpScreen, PAL_XY(12, 148));
+   }
+
+   //
+   // Draw the description of the selected item
+   //
+   if (!g_fNoDesc && gpGlobals->lpObjectDesc != NULL)
+   {
+      char szDesc[512], *next;
+      const char *d = PAL_GetObjectDesc(gpGlobals->lpObjectDesc, wObject);
+
+      if (d != NULL)
+      {
+         k = 150;
+         strcpy(szDesc, d);
+         d = szDesc;
+
+         while (TRUE)
+         {
+            next = strchr(d, '*');
+            if (next != NULL)
+            {
+               *next = '\0';
+               next++;
+            }
+
+            PAL_DrawText(d, PAL_XY(75, k), DESCTEXT_COLOR, TRUE, FALSE);
+            k += 16;
+
+            if (next == NULL)
+            {
+               break;
+            }
+
+            d = next;
+         }
+      }
+   }
+
+   if (g_InputState.dwKeyPress & kKeySearch)
+   {
+      if ((gpGlobals->g.rgObject[wObject].item.wFlags & g_wItemFlags) &&
+         (SHORT)gpGlobals->rgInventory[gpGlobals->iCurInvMenuItem].nAmount >
+         (SHORT)gpGlobals->rgInventory[gpGlobals->iCurInvMenuItem].nAmountInUse)
+      {
+         if (gpGlobals->rgInventory[gpGlobals->iCurInvMenuItem].nAmount > 0)
+         {
+            j = (gpGlobals->iCurInvMenuItem < 3 * 4) ? (gpGlobals->iCurInvMenuItem / 3) : 4;
+            k = gpGlobals->iCurInvMenuItem % 3;
+
+            PAL_DrawText(PAL_GetWord(wObject), PAL_XY(15 + k * 100, 12 + j * 18),
+               MENUITEM_COLOR_CONFIRMED, FALSE, FALSE);
+         }
+
+         return wObject;
+      }
+   }
+
+   return 0xFFFF;
+}
+
+VOID
+PAL_ItemSelectMenuInit(
+   WORD                      wItemFlags
+)
+/*++
+  Purpose:
+
+    Initialize the item selection menu.
+
+  Parameters:
+
+    [IN]  wItemFlags - flags for usable item.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   int           i, j;
+   WORD          w;
+
+   g_wItemFlags = wItemFlags;
+
+   //
+   // Compress the inventory
+   //
+   PAL_CompressInventory();
+
+   //
+   // Count the total number of items in inventory
+   //
+   g_iNumInventory = 0;
+   while (g_iNumInventory < MAX_INVENTORY &&
+      gpGlobals->rgInventory[g_iNumInventory].wItem != 0)
+   {
+      g_iNumInventory++;
+   }
+
+   //
+   // Also add usable equipped items to the list
+   //
+   if ((wItemFlags & kItemFlagUsable) && !gpGlobals->fInBattle)
+   {
+      for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++)
+      {
+         w = gpGlobals->rgParty[i].wPlayerRole;
+
+         for (j = 0; j < MAX_PLAYER_EQUIPMENTS; j++)
+         {
+            if (gpGlobals->g.rgObject[gpGlobals->g.PlayerRoles.rgwEquipment[j][w]].item.wFlags & kItemFlagUsable)
+            {
+               if (g_iNumInventory < MAX_INVENTORY)
+               {
+                  gpGlobals->rgInventory[g_iNumInventory].wItem = gpGlobals->g.PlayerRoles.rgwEquipment[j][w];
+                  gpGlobals->rgInventory[g_iNumInventory].nAmount = 0;
+                  gpGlobals->rgInventory[g_iNumInventory].nAmountInUse = (WORD)-1;
+
+                  g_iNumInventory++;
+               }
+            }
+         }
+      }
+   }
+}
+
+WORD
+PAL_ItemSelectMenu(
+   LPITEMCHANGED_CALLBACK    lpfnMenuItemChanged,
+   WORD                      wItemFlags
+)
+/*++
+  Purpose:
+
+    Show the item selection menu.
+
+  Parameters:
+
+    [IN]  lpfnMenuItemChanged - Callback function which is called when user
+                                changed the current menu item.
+
+    [IN]  wItemFlags - flags for usable item.
+
+  Return value:
+
+    The object ID of the selected item. 0 if cancelled.
+
+--*/
+{
+   int              iPrevIndex;
+   WORD             w;
+   DWORD            dwTime;
+
+   PAL_ItemSelectMenuInit(wItemFlags);
+   iPrevIndex = gpGlobals->iCurInvMenuItem;
+
+   PAL_ClearKeyState();
+
+   if (lpfnMenuItemChanged != NULL)
+   {
+      g_fNoDesc = TRUE;
+      (*lpfnMenuItemChanged)(gpGlobals->rgInventory[gpGlobals->iCurInvMenuItem].wItem);
+   }
+
+   dwTime = SDL_GetTicks();
+
+   while (TRUE)
+   {
+      if (lpfnMenuItemChanged == NULL)
+      {
+         PAL_MakeScene();
+      }
+
+      w = PAL_ItemSelectMenuUpdate();
+      VIDEO_UpdateScreen(NULL);
+
+      PAL_ClearKeyState();
+
+      PAL_ProcessEvent();
+      while (SDL_GetTicks() < dwTime)
+      {
+         PAL_ProcessEvent();
+         if (g_InputState.dwKeyPress != 0)
+         {
+            break;
+         }
+         SDL_Delay(5);
+      }
+
+      dwTime = SDL_GetTicks() + FRAME_TIME;
+
+      if (w != 0xFFFF)
+      {
+         g_fNoDesc = FALSE;
+         return w;
+      }
+
+      if (iPrevIndex != gpGlobals->iCurInvMenuItem)
+      {
+         if (gpGlobals->iCurInvMenuItem >= 0 && gpGlobals->iCurInvMenuItem < MAX_INVENTORY)
+         {
+            if (lpfnMenuItemChanged != NULL)
+            {
+               (*lpfnMenuItemChanged)(gpGlobals->rgInventory[gpGlobals->iCurInvMenuItem].wItem);
+            }
+         }
+
+         iPrevIndex = gpGlobals->iCurInvMenuItem;
+      }
+   }
+
+   assert(FALSE);
+   return 0; // should not really reach here
+}

+ 48 - 0
itemmenu.h

@@ -0,0 +1,48 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef ITEMMENU_H
+#define ITEMMENU_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+WORD
+PAL_ItemSelectMenuUpdate(
+   VOID
+);
+
+VOID
+PAL_ItemSelectMenuInit(
+   WORD                      wItemFlags
+);
+
+WORD
+PAL_ItemSelectMenu(
+   LPITEMCHANGED_CALLBACK    lpfnMenuItemChanged,
+   WORD                      wItemFlags
+);
+
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 432 - 0
magicmenu.c

@@ -0,0 +1,432 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "main.h"
+
+static struct MAGICITEM
+{
+   WORD         wMagic;
+   WORD         wMP;
+   BOOL         fEnabled;
+} rgMagicItem[MAX_PLAYER_MAGICS];
+
+static int     g_iNumMagic = 0;
+static int     g_iCurrentItem = 0;
+static WORD    g_wPlayerMP = 0;
+
+WORD
+PAL_MagicSelectionMenuUpdate(
+   VOID
+)
+/*++
+  Purpose:
+
+    Update the magic selection menu.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    The selected magic. 0 if cancelled, 0xFFFF if not confirmed.
+
+--*/
+{
+   int         i, j, k;
+   BYTE        bColor;
+
+   //
+   // Check for inputs
+   //
+   if (g_InputState.dwKeyPress & kKeyUp)
+   {
+      g_iCurrentItem -= 3;
+   }
+   else if (g_InputState.dwKeyPress & kKeyDown)
+   {
+      g_iCurrentItem += 3;
+   }
+   else if (g_InputState.dwKeyPress & kKeyLeft)
+   {
+      g_iCurrentItem--;
+   }
+   else if (g_InputState.dwKeyPress & kKeyRight)
+   {
+      g_iCurrentItem++;
+   }
+   else if (g_InputState.dwKeyPress & kKeyPgUp)
+   {
+      g_iCurrentItem -= 3 * 5;
+   }
+   else if (g_InputState.dwKeyPress & kKeyPgDn)
+   {
+      g_iCurrentItem += 3 * 5;
+   }
+   else if (g_InputState.dwKeyPress & kKeyMenu)
+   {
+      return 0;
+   }
+
+   //
+   // Make sure the current menu item index is in bound
+   //
+   if (g_iCurrentItem < 0)
+   {
+      g_iCurrentItem = 0;
+   }
+   else if (g_iCurrentItem >= g_iNumMagic)
+   {
+      g_iCurrentItem = g_iNumMagic - 1;
+   }
+
+   //
+   // Create the box.
+   //
+   PAL_CreateBox(PAL_XY(10, 42), 4, 16, 1, FALSE);
+
+   if (gpGlobals->lpObjectDesc == NULL)
+   {
+      //
+      // Draw the cash amount.
+      //
+      PAL_CreateSingleLineBox(PAL_XY(0, 0), 5, FALSE);
+      PAL_DrawText(PAL_GetWord(CASH_LABEL), PAL_XY(10, 10), 0, FALSE, FALSE);
+      PAL_DrawNumber(gpGlobals->dwCash, 6, PAL_XY(49, 14), kNumColorYellow, kNumAlignRight);
+
+      //
+      // Draw the MP of the selected magic.
+      //
+      PAL_CreateSingleLineBox(PAL_XY(215, 0), 5, FALSE);
+      PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_SLASH),
+         gpScreen, PAL_XY(260, 14));
+      PAL_DrawNumber(rgMagicItem[g_iCurrentItem].wMP, 4, PAL_XY(230, 14),
+         kNumColorYellow, kNumAlignRight);
+      PAL_DrawNumber(g_wPlayerMP, 4, PAL_XY(265, 14), kNumColorCyan, kNumAlignRight);
+   }
+   else
+   {
+      char szDesc[512], *next;
+      const char *d = PAL_GetObjectDesc(gpGlobals->lpObjectDesc, rgMagicItem[g_iCurrentItem].wMagic);
+
+      //
+      // Draw the magic description.
+      //
+      if (d != NULL)
+      {
+         k = 3;
+         strcpy(szDesc, d);
+         d = szDesc;
+
+         while (TRUE)
+         {
+            next = strchr(d, '*');
+            if (next != NULL)
+            {
+               *next = '\0';
+               next++;
+            }
+
+            PAL_DrawText(d, PAL_XY(100, k), DESCTEXT_COLOR, TRUE, FALSE);
+            k += 16;
+
+            if (next == NULL)
+            {
+               break;
+            }
+
+            d = next;
+         }
+      }
+
+      //
+      // Draw the MP of the selected magic.
+      //
+      PAL_CreateSingleLineBox(PAL_XY(0, 0), 5, FALSE);
+      PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_SLASH),
+         gpScreen, PAL_XY(45, 14));
+      PAL_DrawNumber(rgMagicItem[g_iCurrentItem].wMP, 4, PAL_XY(15, 14),
+         kNumColorYellow, kNumAlignRight);
+      PAL_DrawNumber(g_wPlayerMP, 4, PAL_XY(50, 14), kNumColorCyan, kNumAlignRight);
+   }
+
+   //
+   // Draw the texts of the current page
+   //
+   i = g_iCurrentItem / 3 * 3 - 3 * 2;
+   if (i < 0)
+   {
+      i = 0;
+   }
+
+   for (j = 0; j < 5; j++)
+   {
+      for (k = 0; k < 3; k++)
+      {
+         bColor = MENUITEM_COLOR;
+
+         if (i >= g_iNumMagic)
+         {
+            //
+            // End of the list reached
+            //
+            j = 5;
+            break;
+         }
+
+         if (i == g_iCurrentItem)
+         {
+            if (rgMagicItem[i].fEnabled)
+            {
+               bColor = MENUITEM_COLOR_SELECTED;
+            }
+            else
+            {
+               bColor = MENUITEM_COLOR_SELECTED_INACTIVE;
+            }
+         }
+         else if (!rgMagicItem[i].fEnabled)
+         {
+            bColor = MENUITEM_COLOR_INACTIVE;
+         }
+
+         //
+         // Draw the text
+         //
+         PAL_DrawText(PAL_GetWord(rgMagicItem[i].wMagic),
+            PAL_XY(35 + k * 87, 54 + j * 18), bColor, TRUE, FALSE);
+
+         //
+         // Draw the cursor on the current selected item
+         //
+         if (i == g_iCurrentItem)
+         {
+            PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_CURSOR),
+               gpScreen, PAL_XY(60 + k * 87, 64 + j * 18));
+         }
+
+         i++;
+      }
+   }
+
+   if (g_InputState.dwKeyPress & kKeySearch)
+   {
+      if (rgMagicItem[g_iCurrentItem].fEnabled)
+      {
+         j = g_iCurrentItem % 3;
+         k = (g_iCurrentItem < 3 * 2) ? (g_iCurrentItem / 3) : 2;
+
+         j = 35 + j * 87;
+         k = 54 + k * 18;
+
+         PAL_DrawText(PAL_GetWord(rgMagicItem[g_iCurrentItem].wMagic), PAL_XY(j, k),
+            MENUITEM_COLOR_CONFIRMED, FALSE, TRUE);
+
+         return rgMagicItem[g_iCurrentItem].wMagic;
+      }
+   }
+
+   return 0xFFFF;
+}
+
+VOID
+PAL_MagicSelectionMenuInit(
+   WORD         wPlayerRole,
+   BOOL         fInBattle,
+   WORD         wDefaultMagic
+)
+/*++
+  Purpose:
+
+    Initialize the magic selection menu.
+
+  Parameters:
+
+    [IN]  wPlayerRole - the player ID.
+
+    [IN]  fInBattle - TRUE if in battle, FALSE if not.
+
+    [IN]  wDefaultMagic - the default magic item.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   WORD       w;
+   int        i, j;
+
+   g_iCurrentItem = 0;
+   g_iNumMagic = 0;
+
+   g_wPlayerMP = gpGlobals->g.PlayerRoles.rgwMP[wPlayerRole];
+
+   //
+   // Put all magics of this player to the array
+   //
+   for (i = 0; i < MAX_PLAYER_MAGICS; i++)
+   {
+      w = gpGlobals->g.PlayerRoles.rgwMagic[i][wPlayerRole];
+      if (w != 0)
+      {
+         rgMagicItem[g_iNumMagic].wMagic = w;
+
+         w = gpGlobals->g.rgObject[w].magic.wMagicNumber;
+         rgMagicItem[g_iNumMagic].wMP = gpGlobals->g.lprgMagic[w].wCostMP;
+
+         rgMagicItem[g_iNumMagic].fEnabled = TRUE;
+
+         if (rgMagicItem[g_iNumMagic].wMP > g_wPlayerMP)
+         {
+            rgMagicItem[g_iNumMagic].fEnabled = FALSE;
+         }
+
+         w = gpGlobals->g.rgObject[rgMagicItem[g_iNumMagic].wMagic].magic.wFlags;
+         if (fInBattle)
+         {
+            if (!(w & kMagicFlagUsableInBattle))
+            {
+               rgMagicItem[g_iNumMagic].fEnabled = FALSE;
+            }
+         }
+         else
+         {
+            if (!(w & kMagicFlagUsableOutsideBattle))
+            {
+               rgMagicItem[g_iNumMagic].fEnabled = FALSE;
+            }
+         }
+
+         g_iNumMagic++;
+      }
+   }
+
+   //
+   // Sort the array
+   //
+   for (i = 0; i < g_iNumMagic - 1; i++)
+   {
+      BOOL fCompleted = TRUE;
+
+      for (j = 0; j < g_iNumMagic - 1 - i; j++)
+      {
+         if (rgMagicItem[j].wMagic > rgMagicItem[j + 1].wMagic)
+         {
+            struct MAGICITEM t = rgMagicItem[j];
+            rgMagicItem[j] = rgMagicItem[j + 1];
+            rgMagicItem[j + 1] = t;
+
+            fCompleted = FALSE;
+         }
+      }
+
+      if (fCompleted)
+      {
+         break;
+      }
+   }
+
+   //
+   // Place the cursor to the default item
+   //
+   for (i = 0; i < g_iNumMagic; i++)
+   {
+      if (rgMagicItem[i].wMagic == wDefaultMagic)
+      {
+         g_iCurrentItem = i;
+         break;
+      }
+   }
+}
+
+WORD
+PAL_MagicSelectionMenu(
+   WORD         wPlayerRole,
+   BOOL         fInBattle,
+   WORD         wDefaultMagic
+)
+/*++
+  Purpose:
+
+    Show the magic selection menu.
+
+  Parameters:
+
+    [IN]  wPlayerRole - the player ID.
+
+    [IN]  fInBattle - TRUE if in battle, FALSE if not.
+
+    [IN]  wDefaultMagic - the default magic item.
+
+  Return value:
+
+    The selected magic. 0 if cancelled.
+
+--*/
+{
+   WORD            w;
+   int             i;
+   DWORD           dwTime;
+
+   PAL_MagicSelectionMenuInit(wPlayerRole, fInBattle, wDefaultMagic);
+   PAL_ClearKeyState();
+
+   dwTime = SDL_GetTicks();
+
+   while (TRUE)
+   {
+      PAL_MakeScene();
+
+      w = 45;
+
+      for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++)
+      {
+         PAL_PlayerInfoBox(PAL_XY(w, 165), gpGlobals->rgParty[i].wPlayerRole, 100,
+            TIMEMETER_COLOR_DEFAULT, FALSE);
+         w += 78;
+      }
+
+      w = PAL_MagicSelectionMenuUpdate();
+      VIDEO_UpdateScreen(NULL);
+
+      PAL_ClearKeyState();
+
+      if (w != 0xFFFF)
+      {
+         return w;
+      }
+
+      PAL_ProcessEvent();
+      while (SDL_GetTicks() < dwTime)
+      {
+         PAL_ProcessEvent();
+         if (g_InputState.dwKeyPress != 0)
+         {
+            break;
+         }
+         SDL_Delay(5);
+      }
+
+      dwTime = SDL_GetTicks() + FRAME_TIME;
+   }
+
+   return 0; // should not really reach here
+}

+ 51 - 0
magicmenu.h

@@ -0,0 +1,51 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef MAGICMENU_H
+#define MAGICMENU_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+WORD
+PAL_MagicSelectionMenuUpdate(
+   VOID
+);
+
+VOID
+PAL_MagicSelectionMenuInit(
+   WORD         wPlayerRole,
+   BOOL         fInBattle,
+   WORD         wDefaultMagic
+);
+
+WORD
+PAL_MagicSelectionMenu(
+   WORD         wPlayerRole,
+   BOOL         fInBattle,
+   WORD         wDefaultMagic
+);
+
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 594 - 0
main.c

@@ -0,0 +1,594 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "main.h"
+#include "getopt.h"
+
+#ifdef PSP
+#include "main_PSP.h"
+#endif
+
+#if defined (NDS) && defined (GEKKO)
+#include <fat.h>
+#endif
+
+#define BITMAPNUM_SPLASH_UP         0x26
+#define BITMAPNUM_SPLASH_DOWN       0x27
+#define SPRITENUM_SPLASH_TITLE      0x47
+#define SPRITENUM_SPLASH_CRANE      0x49
+#define NUM_RIX_TITLE               0x5
+
+static VOID
+PAL_Init(
+   WORD             wScreenWidth,
+   WORD             wScreenHeight,
+   BOOL             fFullScreen
+)
+/*++
+  Purpose:
+
+    Initialize everything needed by the game.
+
+  Parameters:
+
+    [IN]  wScreenWidth - width of the screen.
+
+    [IN]  wScreenHeight - height of the screen.
+
+    [IN]  fFullScreen - TRUE to use full screen mode, FALSE to use windowed mode.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   int           e;
+
+#if defined (NDS) && defined (GEKKO)
+   fatInitDefault();
+#endif
+
+   //
+   // Initialize defaults, video and audio
+   //
+#if defined(DINGOO)
+   if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) == -1)
+#else
+   if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_CDROM | SDL_INIT_NOPARACHUTE | SDL_INIT_JOYSTICK) == -1)
+#endif
+   {
+#if defined (_WIN32) && SDL_MAJOR_VERSION == 1 && SDL_MINOR_VERSION <= 2
+      //
+      // Try the WINDIB driver if DirectX failed.
+      //
+      putenv("SDL_VIDEODRIVER=windib");
+      if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE | SDL_INIT_JOYSTICK) == -1)
+      {
+         TerminateOnError("Could not initialize SDL: %s.\n", SDL_GetError());
+      }
+#else
+      TerminateOnError("Could not initialize SDL: %s.\n", SDL_GetError());
+#endif
+   }
+
+   //
+   // Initialize subsystems.
+   //
+#ifdef GEKKO
+   e = VIDEO_Init_GEKKO(wScreenWidth, wScreenHeight, fFullScreen);
+#else
+   e = VIDEO_Init(wScreenWidth, wScreenHeight, fFullScreen);
+#endif
+   if (e != 0)
+   {
+      TerminateOnError("Could not initialize Video: %d.\n", e);
+   }
+
+   SDL_WM_SetCaption("Loading...", NULL);
+
+   e = PAL_InitGlobals();
+   if (e != 0)
+   {
+      TerminateOnError("Could not initialize global data: %d.\n", e);
+   }
+
+   e = PAL_InitFont();
+   if (e != 0)
+   {
+      TerminateOnError("Could not load fonts: %d.\n", e);
+   }
+
+   e = PAL_InitUI();
+   if (e != 0)
+   {
+      TerminateOnError("Could not initialize UI subsystem: %d.\n", e);
+   }
+
+   e = PAL_InitText();
+   if (e != 0)
+   {
+      TerminateOnError("Could not initialize text subsystem: %d.\n", e);
+   }
+
+   PAL_InitInput();
+   PAL_InitResources();
+   SOUND_OpenAudio();
+
+#ifdef _DEBUG
+   SDL_WM_SetCaption("Pal (Debug Build)", NULL);
+#else
+   SDL_WM_SetCaption("Pal", NULL);
+#endif
+}
+
+VOID
+PAL_Shutdown(
+   VOID
+)
+/*++
+  Purpose:
+
+    Free everything needed by the game.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   SOUND_CloseAudio();
+   PAL_FreeFont();
+   PAL_FreeResources();
+   PAL_FreeGlobals();
+   PAL_FreeUI();
+   PAL_FreeText();
+   PAL_ShutdownInput();
+   VIDEO_Shutdown();
+
+   UTIL_CloseLog();
+
+   SDL_Quit();
+#if defined(GPH)
+	chdir("/usr/gp2x");
+	execl("./gp2xmenu", "./gp2xmenu", NULL);
+#endif
+}
+
+VOID
+PAL_TrademarkScreen(
+   VOID
+)
+/*++
+  Purpose:
+
+    Show the trademark screen.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   PAL_SetPalette(3, FALSE);
+   PAL_RNGPlay(6, 0, 1000, 25);
+   UTIL_Delay(1000);
+   PAL_FadeOut(1);
+}
+
+VOID
+PAL_SplashScreen(
+   VOID
+)
+/*++
+  Purpose:
+
+    Show the splash screen.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   SDL_Color     *palette = PAL_GetPalette(1, FALSE);
+   SDL_Color      rgCurrentPalette[256];
+   SDL_Surface   *lpBitmapDown, *lpBitmapUp;
+   SDL_Rect       srcrect, dstrect;
+   LPSPRITE       lpSpriteCrane;
+   LPBITMAPRLE    lpBitmapTitle;
+   LPBYTE         buf, buf2;
+   int            cranepos[9][3], i, iImgPos = 200, iCraneFrame = 0, iTitleHeight;
+   DWORD          dwTime, dwBeginTime;
+   BOOL           fUseCD = TRUE;
+
+   if (palette == NULL)
+   {
+      fprintf(stderr, "ERROR: PAL_SplashScreen(): palette == NULL\n");
+      return;
+   }
+
+   //
+   // Allocate all the needed memory at once for simplification
+   //
+   buf = (LPBYTE)UTIL_calloc(1, 320 * 200 * 2);
+   buf2 = (LPBYTE)(buf + 320 * 200);
+   lpSpriteCrane = (LPSPRITE)buf2 + 32000;
+
+   //
+   // Create the surfaces
+   //
+   lpBitmapDown = SDL_CreateRGBSurface(gpScreen->flags, 320, 200, 8,
+      gpScreen->format->Rmask, gpScreen->format->Gmask, gpScreen->format->Bmask,
+      gpScreen->format->Amask);
+   lpBitmapUp = SDL_CreateRGBSurface(gpScreen->flags, 320, 200, 8,
+      gpScreen->format->Rmask, gpScreen->format->Gmask, gpScreen->format->Bmask,
+      gpScreen->format->Amask);
+
+   //
+   // Read the bitmaps
+   //
+   PAL_MKFReadChunk(buf, 320 * 200, BITMAPNUM_SPLASH_UP, gpGlobals->f.fpFBP);
+   Decompress(buf, buf2, 320 * 200);
+   PAL_FBPBlitToSurface(buf2, lpBitmapUp);
+   PAL_MKFReadChunk(buf, 320 * 200, BITMAPNUM_SPLASH_DOWN, gpGlobals->f.fpFBP);
+   Decompress(buf, buf2, 320 * 200);
+   PAL_FBPBlitToSurface(buf2, lpBitmapDown);
+   PAL_MKFReadChunk(buf, 32000, SPRITENUM_SPLASH_TITLE, gpGlobals->f.fpMGO);
+   Decompress(buf, buf2, 32000);
+   lpBitmapTitle = (LPBITMAPRLE)PAL_SpriteGetFrame(buf2, 0);
+   PAL_MKFReadChunk(buf, 32000, SPRITENUM_SPLASH_CRANE, gpGlobals->f.fpMGO);
+   Decompress(buf, lpSpriteCrane, 32000);
+
+   iTitleHeight = PAL_RLEGetHeight(lpBitmapTitle);
+   lpBitmapTitle[2] = 0;
+   lpBitmapTitle[3] = 0; // HACKHACK
+
+   //
+   // Generate the positions of the cranes
+   //
+   for (i = 0; i < 9; i++)
+   {
+      cranepos[i][0] = RandomLong(300, 600);
+      cranepos[i][1] = RandomLong(0, 80);
+      cranepos[i][2] = RandomLong(0, 8);
+   }
+
+   //
+   // Play the title music
+   //
+   if (!SOUND_PlayCDA(7))
+   {
+      fUseCD = FALSE;
+      PAL_PlayMUS(NUM_RIX_TITLE, TRUE, 2);
+   }
+
+   //
+   // Clear all of the events and key states
+   //
+   PAL_ProcessEvent();
+   PAL_ClearKeyState();
+
+   dwBeginTime = SDL_GetTicks();
+
+   srcrect.x = 0;
+   srcrect.w = 320;
+   dstrect.x = 0;
+   dstrect.w = 320;
+
+   while (TRUE)
+   {
+      PAL_ProcessEvent();
+      dwTime = SDL_GetTicks() - dwBeginTime;
+
+      //
+      // Set the palette
+      //
+      if (dwTime < 15000)
+      {
+         for (i = 0; i < 256; i++)
+         {
+            rgCurrentPalette[i].r = (BYTE)(palette[i].r * ((float)dwTime / 15000));
+            rgCurrentPalette[i].g = (BYTE)(palette[i].g * ((float)dwTime / 15000));
+            rgCurrentPalette[i].b = (BYTE)(palette[i].b * ((float)dwTime / 15000));
+         }
+      }
+
+      VIDEO_SetPalette(rgCurrentPalette);
+
+      //
+      // Draw the screen
+      //
+      if (iImgPos > 1)
+      {
+         iImgPos--;
+      }
+
+      //
+      // The upper part...
+      //
+      srcrect.y = iImgPos;
+      srcrect.h = 200 - iImgPos;
+
+      dstrect.y = 0;
+      dstrect.h = srcrect.h;
+
+      SDL_BlitSurface(lpBitmapUp, &srcrect, gpScreen, &dstrect);
+
+      //
+      // The lower part...
+      //
+      srcrect.y = 0;
+      srcrect.h = iImgPos;
+
+      dstrect.y = 200 - iImgPos;
+      dstrect.h = srcrect.h;
+
+      SDL_BlitSurface(lpBitmapDown, &srcrect, gpScreen, &dstrect);
+
+      //
+      // Draw the cranes...
+      //
+      for (i = 0; i < 9; i++)
+      {
+         LPCBITMAPRLE lpFrame = PAL_SpriteGetFrame(lpSpriteCrane,
+            cranepos[i][2] = (cranepos[i][2] + (iCraneFrame & 1)) % 8);
+         cranepos[i][1] += ((iImgPos > 1) && (iImgPos & 1)) ? 1 : 0;
+         PAL_RLEBlitToSurface(lpFrame, gpScreen,
+            PAL_XY(cranepos[i][0], cranepos[i][1]));
+         cranepos[i][0]--;
+      }
+      iCraneFrame++;
+
+      //
+      // Draw the title...
+      //
+      if (PAL_RLEGetHeight(lpBitmapTitle) < iTitleHeight)
+      {
+         //
+         // HACKHACK
+         //
+         WORD w = lpBitmapTitle[2] | (lpBitmapTitle[3] << 8);
+         w++;
+         lpBitmapTitle[2] = (w & 0xFF);
+         lpBitmapTitle[3] = (w >> 8);
+      }
+
+      PAL_RLEBlitToSurface(lpBitmapTitle, gpScreen, PAL_XY(255, 10));
+
+      VIDEO_UpdateScreen(NULL);
+
+      //
+      // Check for keypress...
+      //
+      if (g_InputState.dwKeyPress & (kKeyMenu | kKeySearch))
+      {
+         //
+         // User has pressed a key...
+         //
+         lpBitmapTitle[2] = iTitleHeight & 0xFF;
+         lpBitmapTitle[3] = iTitleHeight >> 8; // HACKHACK
+
+         PAL_RLEBlitToSurface(lpBitmapTitle, gpScreen, PAL_XY(255, 10));
+
+         VIDEO_UpdateScreen(NULL);
+
+         if (dwTime < 15000)
+         {
+            //
+            // If the picture has not completed fading in, complete the rest
+            //
+            while (dwTime < 15000)
+            {
+               for (i = 0; i < 256; i++)
+               {
+                  rgCurrentPalette[i].r = (BYTE)(palette[i].r * ((float)dwTime / 15000));
+                  rgCurrentPalette[i].g = (BYTE)(palette[i].g * ((float)dwTime / 15000));
+                  rgCurrentPalette[i].b = (BYTE)(palette[i].b * ((float)dwTime / 15000));
+               }
+               VIDEO_SetPalette(rgCurrentPalette);
+               UTIL_Delay(8);
+               dwTime += 250;
+            }
+            UTIL_Delay(500);
+         }
+
+         //
+         // Quit the splash screen
+         //
+         break;
+      }
+
+      //
+      // Delay a while...
+      //
+      PAL_ProcessEvent();
+      while (SDL_GetTicks() - dwBeginTime < dwTime + 85)
+      {
+         SDL_Delay(1);
+         PAL_ProcessEvent();
+      }
+   }
+
+   SDL_FreeSurface(lpBitmapDown);
+   SDL_FreeSurface(lpBitmapUp);
+   free(buf);
+
+   if (!fUseCD)
+   {
+      PAL_PlayMUS(0, FALSE, 1);
+   }
+
+   PAL_FadeOut(1);
+}
+
+int
+main(
+   int      argc,
+   char    *argv[]
+)
+/*++
+  Purpose:
+
+    Program entry.
+
+  Parameters:
+
+    argc - Number of arguments.
+
+    argv - Array of arguments.
+
+  Return value:
+
+    Integer value.
+
+--*/
+{
+   WORD          wScreenWidth = 0, wScreenHeight = 0;
+   int           c;
+   BOOL          fFullScreen = FALSE;
+
+   UTIL_OpenLog();
+
+#ifdef _WIN32
+#if SDL_MAJOR_VERSION == 1 && SDL_MINOR_VERSION <= 2
+   putenv("SDL_VIDEODRIVER=directx");
+#else
+   putenv("SDL_VIDEODRIVER=win32");
+#endif
+#endif
+
+#ifndef __SYMBIAN32__
+   //
+   // Parse parameters.
+   //
+   while ((c = getopt(argc, argv, "w:h:fjm")) != -1)
+   {
+      switch (c)
+      {
+      case 'w':
+         //
+         // Set the width of the screen
+         //
+         wScreenWidth = atoi(optarg);
+         if (wScreenHeight == 0)
+         {
+            wScreenHeight = wScreenWidth * 200 / 320;
+         }
+         break;
+
+      case 'h':
+         //
+         // Set the height of the screen
+         //
+         wScreenHeight = atoi(optarg);
+         if (wScreenWidth == 0)
+         {
+            wScreenWidth = wScreenHeight * 320 / 200;
+         }
+         break;
+
+      case 'f':
+         //
+         // Fullscreen Mode
+         //
+         fFullScreen = TRUE;
+         break;
+
+      case 'j':
+         //
+         // Disable joystick
+         //
+         g_fUseJoystick = FALSE;
+         break;
+
+#ifdef PAL_HAS_NATIVEMIDI
+      case 'm':
+         //
+         // Use MIDI music
+         //
+         g_fUseMidi = TRUE;
+         break;
+#endif
+      }
+   }
+#endif
+
+   //
+   // Default resolution is 640x400 (windowed) or 640x480 (fullscreen).
+   //
+   if (wScreenWidth == 0)
+   {
+#ifdef __SYMBIAN32__
+#ifdef __S60_5X__
+      wScreenWidth = 640;
+      wScreenHeight = 360;
+#else
+      wScreenWidth = 320;
+      wScreenHeight = 240;
+#endif
+#else
+#if defined(GPH) || defined(DINGOO)
+      wScreenWidth = 320;
+      wScreenHeight = 240;
+#else
+      wScreenWidth = 640;
+      wScreenHeight = fFullScreen ? 480 : 400;
+#endif
+#endif
+   }
+
+   //
+   // Initialize everything
+   //
+#ifdef PSP
+   sdlpal_psp_init();
+#endif
+   PAL_Init(wScreenWidth, wScreenHeight, fFullScreen);
+
+   //
+   // Show the trademark screen and splash screen
+   //
+   PAL_TrademarkScreen();
+   PAL_SplashScreen();
+
+   //
+   // Run the main game routine
+   //
+   PAL_GameMain();
+
+   //
+   // Should not really reach here...
+   //
+   assert(FALSE);
+   return 255;
+}

+ 60 - 0
main.h

@@ -0,0 +1,60 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef MAIN_H
+#define MAIN_H
+
+#include "common.h"
+#include "util.h"
+#include "palcommon.h"
+#include "font.h"
+#include "global.h"
+#include "map.h"
+#include "res.h"
+#include "scene.h"
+#include "rixplay.h"
+#include "sound.h"
+#include "video.h"
+#include "input.h"
+#include "text.h"
+#include "ui.h"
+#include "uigame.h"
+#include "uibattle.h"
+#include "magicmenu.h"
+#include "itemmenu.h"
+#include "palette.h"
+#include "rngplay.h"
+#include "ending.h"
+#include "script.h"
+#include "battle.h"
+#include "fight.h"
+#include "play.h"
+#include "game.h"
+
+#ifdef PAL_HAS_NATIVEMIDI
+#include "midi.h"
+#endif
+
+VOID
+PAL_Shutdown(
+   VOID
+);
+
+#endif

+ 107 - 0
main_PSP.h

@@ -0,0 +1,107 @@
+//
+// Copyright (c) 2009, Pal_Bazzi.
+//
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include <pspkernel.h>
+#include <pspdebug.h>
+#include <pspsdk.h>
+#include <psppower.h>
+#include <pspthreadman.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define PSP_HEAP_MEMSIZE 12288
+
+PSP_MODULE_INFO("SDLPAL", 0, 1, 1);
+PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER);
+PSP_HEAP_SIZE_KB(PSP_HEAP_MEMSIZE);
+
+//
+//Exit callback
+//
+int PSPExitCallback(int arg1, int arg2, void *common)
+{
+	exit(0);
+	return 0;
+}
+
+//
+//Reopen MKF files when resume from suspend
+//
+int PSPSuspendCallback(int arg1, int arg2, void *common)
+{
+  if (pwrflags & PSP_POWER_CB_RESUME_COMPLETE)
+  {
+    gpGlobals->f.fpFBP = UTIL_OpenRequiredFile("fbp.mkf");
+    gpGlobals->f.fpDATA = UTIL_OpenRequiredFile("data.mkf");
+    gpGlobals->f.fpFIRE = UTIL_OpenRequiredFile("fire.mkf");
+    gpGlobals->f.fpSSS = UTIL_OpenRequiredFile("sss.mkf");
+    gpGlobals->lpObjectDesc = PAL_LoadObjectDesc(va("%s%s", PAL_PREFIX, "desc.dat"));
+    SOUND_Reload_VOC();
+  }
+  int cbid;
+  cbid = sceKernelCreateCallback("suspend Callback", PSPSuspendCallback, NULL);
+	scePowerRegisterCallback(0, cbid);
+  return 0;
+}
+
+//
+//setup callbacks thread
+//
+int PSPRegisterCallbackThread(SceSize args, void *argp)
+{
+	int cbid;
+	cbid = sceKernelCreateCallback("Exit Callback", PSPExitCallback, NULL);
+	sceKernelRegisterExitCallback(cbid);
+	cbid = sceKernelCreateCallback("suspend Callback", PSPSuspendCallback, NULL);
+	scePowerRegisterCallback(0, cbid);
+	sceKernelSleepThreadCB();
+	return 0;
+}
+
+//
+//setup exit callback
+//
+int PSPSetupCallbacks(void)
+{
+	int thid = 0;
+	thid = sceKernelCreateThread("update_thread", PSPRegisterCallbackThread, 0x11, 0xFA0, 0, 0);
+	if(thid >= 0)
+		sceKernelStartThread(thid, 0, 0);
+	return thid;
+}
+
+//
+//Init on PSP
+//
+void sdlpal_psp_init(void)
+{
+   // Init Debug Screen
+   pspDebugScreenInit();
+
+   // PSP set callbacks
+   PSPSetupCallbacks();
+
+   // Register sceKernelExitGame() to be called when we exit 
+   atexit(sceKernelExitGame);
+
+   // set PSP CPU clock
+   scePowerSetClockFrequency(333 , 333 , 166);
+}

+ 417 - 0
map.c

@@ -0,0 +1,417 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "palcommon.h"
+#include "map.h"
+
+LPPALMAP
+PAL_LoadMap(
+   INT               iMapNum,
+   FILE             *fpMapMKF,
+   FILE             *fpGopMKF
+)
+/*++
+  Purpose:
+
+    Load the specified map from the MKF file, as well as the tile bitmaps.
+
+  Parameters:
+
+    [IN]  iMapNum - Number of the map to load.
+
+    [IN]  fpMapMKF - Pointer to the fopen'ed map.mkf file, which
+                     contains the map tile data.
+
+    [IN]  fpGopMKF - Pointer to the fopen'ed gop.mkf file, which
+                     contains the tile bitmaps. The bitmap can be read
+                     by PAL_SpriteGetFrame() function.
+
+  Return value:
+
+    Pointer to the loaded map. NULL if failed.
+
+--*/
+{
+   LPBYTE                     buf;
+   INT                        size, i, j;
+   LPPALMAP                   map;
+
+   //
+   // Check for invalid map number.
+   //
+   if (iMapNum >= PAL_MKFGetChunkCount(fpMapMKF) ||
+      iMapNum >= PAL_MKFGetChunkCount(fpGopMKF) ||
+      iMapNum <= 0)
+   {
+      return NULL;
+   }
+
+   //
+   // Load the map tile data.
+   //
+   size = PAL_MKFGetChunkSize(iMapNum, fpMapMKF);
+
+   //
+   // Allocate a temporary buffer for the compressed data.
+   //
+   buf = (LPBYTE)malloc(size);
+   if (buf == NULL)
+   {
+      return NULL;
+   }
+
+   //
+   // Create the map instance.
+   //
+   map = (LPPALMAP)malloc(sizeof(PALMAP));
+   if (map == NULL)
+   {
+      return NULL;
+   }
+
+   //
+   // Read the map data.
+   //
+   if (PAL_MKFReadChunk(buf, size, iMapNum, fpMapMKF) < 0)
+   {
+      free(buf);
+      free(map);
+      return NULL;
+   }
+
+   //
+   // Decompress the tile data.
+   //
+   if (Decompress(buf, (LPBYTE)(map->Tiles), sizeof(map->Tiles)) < 0)
+   {
+      free(map);
+      free(buf);
+      return NULL;
+   }
+
+   //
+   // The compressed data is useless now; delete it.
+   //
+   free(buf);
+
+   //
+   // Adjust the endianness of the decompressed data.
+   //
+   for (i = 0; i < 128; i++)
+   {
+      for (j = 0; j < 64; j++)
+      {
+         map->Tiles[i][j][0] = SWAP32(map->Tiles[i][j][0]);
+         map->Tiles[i][j][1] = SWAP32(map->Tiles[i][j][1]);
+      }
+   }
+
+   //
+   // Load the tile bitmaps.
+   //
+   size = PAL_MKFGetChunkSize(iMapNum, fpGopMKF);
+   if (size <= 0)
+   {
+      free(map);
+      return NULL;
+   }
+   map->pTileSprite = (LPSPRITE)malloc(size);
+   if (map->pTileSprite == NULL)
+   {
+      free(map);
+      return NULL;
+   }
+   if (PAL_MKFReadChunk(map->pTileSprite, size, iMapNum, fpGopMKF) < 0)
+   {
+      free(map);
+      return NULL;
+   }
+
+   //
+   // Done.
+   //
+   map->iMapNum = iMapNum;
+
+   return map;
+}
+
+VOID
+PAL_FreeMap(
+   LPPALMAP          lpMap
+)
+/*++
+  Purpose:
+
+    Free a loaded map, as well as the tile bitmaps.
+
+  Parameters:
+
+    [IN]  lpMap - Pointer to the loaded map structure.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   //
+   // Check for NULL pointer.
+   //
+   if (lpMap == NULL)
+   {
+      return;
+   }
+
+   //
+   // Free the tile bitmaps.
+   //
+   if (lpMap->pTileSprite != NULL)
+   {
+      free(lpMap->pTileSprite);
+   }
+
+   //
+   // Delete the instance.
+   //
+   free(lpMap);
+}
+
+LPCBITMAPRLE
+PAL_MapGetTileBitmap(
+   BYTE       x,
+   BYTE       y,
+   BYTE       h,
+   BYTE       ucLayer,
+   LPCPALMAP  lpMap
+)
+/*++
+  Purpose:
+
+    Get the tile bitmap on the specified layer at the location (x, y, h).
+
+  Parameters:
+
+    [IN]  x - Column number of the tile.
+
+    [IN]  y - Line number in the map.
+
+    [IN]  h - Each line in the map has two lines of tiles, 0 and 1.
+              (See map.h for details.)
+
+    [IN]  ucLayer - The layer. 0 for bottom, 1 for top.
+
+    [IN]  lpMap - Pointer to the loaded map.
+
+  Return value:
+
+    Pointer to the bitmap. NULL if failed.
+
+--*/
+{
+   DWORD d;
+
+   //
+   // Check for invalid parameters.
+   //
+   if (x >= 64 || y >= 128 || h > 1 || lpMap == NULL)
+   {
+      return NULL;
+   }
+
+   //
+   // Get the tile data of the specified location.
+   //
+   d = lpMap->Tiles[y][x][h];
+
+   if (ucLayer == 0)
+   {
+      //
+      // Bottom layer
+      //
+      return PAL_SpriteGetFrame(lpMap->pTileSprite, (d & 0xFF) | ((d >> 4) & 0x100));
+   }
+   else
+   {
+      //
+      // Top layer
+      //
+      d >>= 16;
+      return PAL_SpriteGetFrame(lpMap->pTileSprite, ((d & 0xFF) | ((d >> 4) & 0x100)) - 1);
+   }
+}
+
+BOOL
+PAL_MapTileIsBlocked(
+   BYTE       x,
+   BYTE       y,
+   BYTE       h,
+   LPCPALMAP  lpMap
+)
+/*++
+  Purpose:
+
+    Check if the tile at the specified location is blocked.
+
+  Parameters:
+
+    [IN]  x - Column number of the tile.
+
+    [IN]  y - Line number in the map.
+
+    [IN]  h - Each line in the map has two lines of tiles, 0 and 1.
+              (See map.h for details.)
+
+    [IN]  lpMap - Pointer to the loaded map.
+
+  Return value:
+
+    TRUE if the tile is blocked, FALSE if not.
+
+--*/
+{
+   //
+   // Check for invalid parameters.
+   //
+   if (x >= 64 || y >= 128 || h > 1 || lpMap == NULL)
+   {
+      return TRUE;
+   }
+
+   return (lpMap->Tiles[y][x][h] & 0x2000) >> 13;
+}
+
+BYTE
+PAL_MapGetTileHeight(
+   BYTE       x,
+   BYTE       y,
+   BYTE       h,
+   BYTE       ucLayer,
+   LPCPALMAP  lpMap
+)
+/*++
+  Purpose:
+
+    Get the logical height value of the specified tile. This value is used
+    to judge whether the tile bitmap should cover the sprites or not.
+
+  Parameters:
+
+    [IN]  x - Column number of the tile.
+
+    [IN]  y - Line number in the map.
+
+    [IN]  h - Each line in the map has two lines of tiles, 0 and 1.
+              (See map.h for details.)
+
+    [IN]  ucLayer - The layer. 0 for bottom, 1 for top.
+
+    [IN]  lpMap - Pointer to the loaded map.
+
+  Return value:
+
+    The logical height value of the specified tile.
+
+--*/
+{
+   DWORD      d;
+
+   //
+   // Check for invalid parameters.
+   //
+   if (y >= 128 || x >= 64 || h > 1 || lpMap == NULL)
+   {
+      return 0;
+   }
+
+   d = lpMap->Tiles[y][x][h];
+
+   if (ucLayer)
+   {
+      d >>= 16;
+   }
+
+   d >>= 8;
+   return (BYTE)(d & 0xf);
+}
+
+VOID
+PAL_MapBlitToSurface(
+   LPCPALMAP             lpMap,
+   SDL_Surface          *lpSurface,
+   const SDL_Rect       *lpSrcRect,
+   BYTE                  ucLayer
+)
+/*++
+  Purpose:
+
+    Blit the specified map area to a SDL Surface.
+
+  Parameters:
+
+    [IN]  lpMap - Pointer to the map.
+
+    [OUT] lpSurface - Pointer to the destination surface.
+
+    [IN]  lpSrcRect - Pointer to the source area.
+
+    [IN]  ucLayer - The layer. 0 for bottom, 1 for top.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   int              sx, sy, dx, dy, x, y, h, xPos, yPos;
+   LPCBITMAPRLE     lpBitmap = NULL;
+
+   //
+   // Convert the coordinate
+   //
+   sy = lpSrcRect->y / 16 - 1;
+   dy = (lpSrcRect->y + lpSrcRect->h) / 16 + 2;
+   sx = lpSrcRect->x / 32 - 1;
+   dx = (lpSrcRect->x + lpSrcRect->w) / 32 + 2;
+
+   //
+   // Do the drawing.
+   //
+   yPos = sy * 16 - 8 - lpSrcRect->y;
+   for (y = sy; y < dy; y++)
+   {
+      for (h = 0; h < 2; h++, yPos += 8)
+      {
+         xPos = sx * 32 + h * 16 - 16 - lpSrcRect->x;
+         for (x = sx; x < dx; x++, xPos += 32)
+         {
+            lpBitmap = PAL_MapGetTileBitmap((BYTE)x, (BYTE)y, (BYTE)h, ucLayer, lpMap);
+            if (lpBitmap == NULL)
+            {
+               if (ucLayer)
+               {
+                  continue;
+               }
+               lpBitmap = PAL_MapGetTileBitmap(0, 0, 0, ucLayer, lpMap);
+            }
+            PAL_RLEBlitToSurface(lpBitmap, lpSurface, PAL_XY(xPos, yPos));
+         }
+      }
+   }
+}

+ 138 - 0
map.h

@@ -0,0 +1,138 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef _MAP_H
+#define _MAP_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "common.h"
+
+//
+// Map format:
+//
+// +----------------------------------------------> x
+// | * * * * * * * * * * ... * * * * * * * * * *  (y = 0, h = 0)
+// |  * * * * * * * * * * ... * * * * * * * * * * (y = 0, h = 1)
+// | * * * * * * * * * * ... * * * * * * * * * *  (y = 1, h = 0)
+// |  * * * * * * * * * * ... * * * * * * * * * * (y = 1, h = 1)
+// | * * * * * * * * * * ... * * * * * * * * * *  (y = 2, h = 0)
+// |  * * * * * * * * * * ... * * * * * * * * * * (y = 2, h = 1)
+// | ............................................
+// v
+// y
+//
+// Note:
+//
+// Tiles are in diamond shape (32x15).
+//
+// Each tile is represented with a DWORD value, which contains information
+// about the tile bitmap, block flag, height, etc.
+//
+// Bottom layer sprite index:
+//  (d & 0xFF) | ((d >> 4) & 0x100)
+//
+// Top layer sprite index:
+//  d >>= 16;
+//  ((d & 0xFF) | ((d >> 4) & 0x100)) - 1)
+//
+// Block flag (player cannot walk through this tile):
+//  d & 0x2000
+//
+
+typedef struct tagPALMAP
+{
+   DWORD          Tiles[128][64][2];
+   LPSPRITE       pTileSprite;
+   INT            iMapNum;
+} PALMAP, *LPPALMAP;
+
+typedef const PALMAP *LPCPALMAP;
+
+LPPALMAP
+PAL_LoadMap(
+   INT               iMapNum,
+   FILE             *fpMapMKF,
+   FILE             *fpGopMKF
+);
+
+VOID
+PAL_FreeMap(
+   LPPALMAP          lpMap
+);
+
+LPCBITMAPRLE
+PAL_MapGetTileBitmap(
+   BYTE       x,
+   BYTE       y,
+   BYTE       h,
+   BYTE       ucLayer,
+   LPCPALMAP  lpMap
+);
+
+BOOL
+PAL_MapTileIsBlocked(
+   BYTE       x,
+   BYTE       y,
+   BYTE       h,
+   LPCPALMAP  lpMap
+);
+
+BYTE
+PAL_MapGetTileHeight(
+   BYTE       x,
+   BYTE       y,
+   BYTE       h,
+   BYTE       ucLayer,
+   LPCPALMAP  lpMap
+);
+
+VOID
+PAL_MapBlitToSurface(
+   LPCPALMAP             lpMap,
+   SDL_Surface          *lpSurface,
+   const SDL_Rect       *lpSrcRect,
+   BYTE                  ucLayer
+);
+
+//
+// Convert map location to the real location
+//
+#define PAL_XYH_TO_POS(x, y, h)                       \
+   PAL_POS((x) * 32 + (h) * 16, (y) * 16 + (h) * 8)
+
+//
+// Convert real location to map location
+//
+#define PAL_POS_TO_XYH(pos, x, y, h)                  \
+{                                                     \
+   (h) = (BYTE)(((PAL_X(pos) % 32) != 0) ? 1 : 0);    \
+   (x) = (BYTE)(PAL_X(pos) / 32);                     \
+   (y) = (BYTE)(PAL_Y(pos) / 16);                     \
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 122 - 0
midi.c

@@ -0,0 +1,122 @@
+//
+// Copyright (c) 2011, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "main.h"
+
+#if !defined (CYGWIN) && !defined (DINGOO) &&  !defined (GEKKO) && !defined (GPH)
+
+static INT iMidCurrent = -1;
+static BOOL fMidLoop = FALSE;
+
+static NativeMidiSong *g_pMid = NULL;
+
+VOID
+MIDI_Play(
+   INT       iNumRIX,
+   BOOL      fLoop
+)
+/*++
+  Purpose:
+
+    Start playing the specified music in MIDI format.
+
+  Parameters:
+
+    [IN]  iNumRIX - number of the music. 0 to stop playing current music.
+
+    [IN]  fLoop - Whether the music should be looped or not.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   FILE            *fp;
+   unsigned char   *buf;
+   int              size;
+   SDL_RWops       *rw;
+
+   if (g_pMid != NULL && iNumRIX == iMidCurrent && native_midi_active())
+   {
+      return;
+   }
+
+   SOUND_PlayCDA(-1);
+   native_midi_freesong(g_pMid);
+   g_pMid = NULL;
+   iMidCurrent = -1;
+
+   if (g_fNoMusic || iNumRIX <= 0)
+   {
+      return;
+   }
+
+   fp = fopen(PAL_PREFIX "midi.mkf", "rb");
+   if (fp == NULL)
+   {
+      return;
+   }
+
+   if (iNumRIX > PAL_MKFGetChunkCount(fp))
+   {
+      fclose(fp);
+      return;
+   }
+
+   size = PAL_MKFGetChunkSize(iNumRIX, fp);
+   if (size <= 0)
+   {
+      fclose(fp);
+      return;
+   }
+
+   buf = (unsigned char *)UTIL_malloc(size);
+
+   PAL_MKFReadChunk((LPBYTE)buf, size, iNumRIX, fp);
+   fclose(fp);
+
+   rw = SDL_RWFromConstMem((const void *)buf, size);
+
+   g_pMid = native_midi_loadsong_RW(rw);
+   if (g_pMid != NULL)
+   {
+      native_midi_start(g_pMid);
+
+      iMidCurrent = iNumRIX;
+      fMidLoop = fLoop;
+   }
+
+   SDL_RWclose(rw);
+   free(buf);
+}
+
+VOID
+MIDI_CheckLoop(
+   VOID
+)
+{
+   if (fMidLoop && g_pMid != NULL && !native_midi_active())
+   {
+      MIDI_Play(iMidCurrent, TRUE);
+   }
+}
+
+#endif

+ 48 - 0
midi.h

@@ -0,0 +1,48 @@
+//
+// Copyright (c) 2011, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef PAL_MIDI_H
+#define PAL_MIDI_H
+
+#include "common.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "native_midi/native_midi.h"
+
+VOID
+MIDI_Play(
+   INT       iNumRIX,
+   BOOL      fLoop
+);
+
+VOID
+MIDI_CheckLoop(
+   VOID
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 45 - 0
pal_s60-10.cpp

@@ -0,0 +1,45 @@
+//
+// Copyright (c) 2009, netwan. All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifdef __SYMBIAN32__
+
+#include <eikstart.h>
+#include <sdlmain.h>
+#include <sdlepocapi.h>
+
+/*
+This file demonstrates how to change CSDL when using sdlexe.lib - i.e. if default flags are not
+ok  - you dont have to use CSDL API directly, you can write this file add add it in to your S60 SDL
+compilation. Then you dont statically link  sdlmain.lib or  sdlmaint.lib libraries
+*/
+
+GLREF_C TInt E32Main()
+    {
+#ifdef __S60_5X__
+    return SDLEnv::SetMain(SDL_main,CSDL::EEnableFocusStop
+                    | CSDL::EAutoOrientation /*| CSDL::EAllowImageResizeKeepRatio | CSDL::EDrawModeGdi*/,
+             NULL/*, SDLEnv::EParamQuery | SDLEnv::EEnableVirtualMouseMoveEvents*/);
+#else
+    return SDLEnv::SetMain(SDL_main,CSDL::EEnableFocusStop
+                | CSDL::EAutoOrientation | CSDL::EAllowImageResizeKeepRatio /*| CSDL::EDrawModeGdi*/,
+         NULL, /*SDLEnv::EParamQuery |*/ SDLEnv::EEnableVirtualMouseMoveEvents);
+#endif
+    }
+
+#endif

+ 923 - 0
palcommon.c

@@ -0,0 +1,923 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// Based on PAL MapEditor by Baldur.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "palcommon.h"
+
+INT
+PAL_RLEBlitToSurface(
+   LPCBITMAPRLE      lpBitmapRLE,
+   SDL_Surface      *lpDstSurface,
+   PAL_POS           pos
+)
+/*++
+  Purpose:
+
+    Blit an RLE-compressed bitmap to an SDL surface.
+    NOTE: Assume the surface is already locked, and the surface is a 8-bit one.
+
+  Parameters:
+
+    [IN]  lpBitmapRLE - pointer to the RLE-compressed bitmap to be decoded.
+
+    [OUT] lpDstSurface - pointer to the destination SDL surface.
+
+    [IN]  pos - position of the destination area.
+
+  Return value:
+
+    0 = success, -1 = error.
+
+--*/
+{
+   UINT          i, j;
+   INT           x, y;
+   UINT          uiLen       = 0;
+   UINT          uiWidth     = 0;
+   UINT          uiHeight    = 0;
+   BYTE          T;
+   INT           dx          = PAL_X(pos);
+   INT           dy          = PAL_Y(pos);
+
+   //
+   // Check for NULL pointer.
+   //
+   if (lpBitmapRLE == NULL || lpDstSurface == NULL)
+   {
+      return -1;
+   }
+
+   //
+   // Skip the 0x00000002 in the file header.
+   //
+   if (lpBitmapRLE[0] == 0x02 && lpBitmapRLE[1] == 0x00 &&
+      lpBitmapRLE[2] == 0x00 && lpBitmapRLE[3] == 0x00)
+   {
+      lpBitmapRLE += 4;
+   }
+
+   //
+   // Get the width and height of the bitmap.
+   //
+   uiWidth = lpBitmapRLE[0] | (lpBitmapRLE[1] << 8);
+   uiHeight = lpBitmapRLE[2] | (lpBitmapRLE[3] << 8);
+
+   //
+   // Calculate the total length of the bitmap.
+   // The bitmap is 8-bpp, each pixel will use 1 byte.
+   //
+   uiLen = uiWidth * uiHeight;
+
+   //
+   // Start decoding and blitting the bitmap.
+   //
+   lpBitmapRLE += 4;
+   for (i = 0; i < uiLen;)
+   {
+      T = *lpBitmapRLE++;
+      if ((T & 0x80) && T <= 0x80 + uiWidth)
+      {
+         i += T - 0x80;
+      }
+      else
+      {
+         for (j = 0; j < T; j++)
+         {
+            //
+            // Calculate the destination coordination.
+            // FIXME: This could be optimized
+            //
+            y = (i + j) / uiWidth + dy;
+            x = (i + j) % uiWidth + dx;
+
+            //
+            // Skip the points which are out of the surface.
+            //
+            if (x < 0)
+            {
+               j += -x - 1;
+               continue;
+            }
+            else if (x >= lpDstSurface->w)
+            {
+               j += x - lpDstSurface->w;
+               continue;
+            }
+
+            if (y < 0)
+            {
+               j += -y * uiWidth - 1;
+               continue;
+            }
+            else if (y >= lpDstSurface->h)
+            {
+               goto end; // No more pixels needed, break out
+            }
+
+            //
+            // Put the pixel onto the surface (FIXME: inefficient).
+            //
+            ((LPBYTE)lpDstSurface->pixels)[y * lpDstSurface->pitch + x] = lpBitmapRLE[j];
+         }
+         lpBitmapRLE += T;
+         i += T;
+      }
+   }
+
+end:
+   //
+   // Success
+   //
+   return 0;
+}
+
+INT
+PAL_RLEBlitWithColorShift(
+   LPCBITMAPRLE      lpBitmapRLE,
+   SDL_Surface      *lpDstSurface,
+   PAL_POS           pos,
+   INT               iColorShift
+)
+/*++
+  Purpose:
+
+    Blit an RLE-compressed bitmap to an SDL surface.
+    NOTE: Assume the surface is already locked, and the surface is a 8-bit one.
+
+  Parameters:
+
+    [IN]  lpBitmapRLE - pointer to the RLE-compressed bitmap to be decoded.
+
+    [OUT] lpDstSurface - pointer to the destination SDL surface.
+
+    [IN]  pos - position of the destination area.
+
+    [IN]  iColorShift - shift the color by this value.
+
+  Return value:
+
+    0 = success, -1 = error.
+
+--*/
+{
+   UINT          i, j;
+   INT           x, y;
+   UINT          uiLen       = 0;
+   UINT          uiWidth     = 0;
+   UINT          uiHeight    = 0;
+   BYTE          T, b;
+   INT           dx          = PAL_X(pos);
+   INT           dy          = PAL_Y(pos);
+
+   //
+   // Check for NULL pointer.
+   //
+   if (lpBitmapRLE == NULL || lpDstSurface == NULL)
+   {
+      return -1;
+   }
+
+   //
+   // Skip the 0x00000002 in the file header.
+   //
+   if (lpBitmapRLE[0] == 0x02 && lpBitmapRLE[1] == 0x00 &&
+      lpBitmapRLE[2] == 0x00 && lpBitmapRLE[3] == 0x00)
+   {
+      lpBitmapRLE += 4;
+   }
+
+   //
+   // Get the width and height of the bitmap.
+   //
+   uiWidth = lpBitmapRLE[0] | (lpBitmapRLE[1] << 8);
+   uiHeight = lpBitmapRLE[2] | (lpBitmapRLE[3] << 8);
+
+   //
+   // Calculate the total length of the bitmap.
+   // The bitmap is 8-bpp, each pixel will use 1 byte.
+   //
+   uiLen = uiWidth * uiHeight;
+
+   //
+   // Start decoding and blitting the bitmap.
+   //
+   lpBitmapRLE += 4;
+   for (i = 0; i < uiLen;)
+   {
+      T = *lpBitmapRLE++;
+      if ((T & 0x80) && T <= 0x80 + uiWidth)
+      {
+         i += T - 0x80;
+      }
+      else
+      {
+         for (j = 0; j < T; j++)
+         {
+            //
+            // Calculate the destination coordination.
+            // FIXME: This could be optimized
+            //
+            y = (i + j) / uiWidth + dy;
+            x = (i + j) % uiWidth + dx;
+
+            //
+            // Skip the points which are out of the surface.
+            //
+            if (x < 0)
+            {
+               j += -x - 1;
+               continue;
+            }
+            else if (x >= lpDstSurface->w)
+            {
+               j += x - lpDstSurface->w;
+               continue;
+            }
+
+            if (y < 0)
+            {
+               j += -y * uiWidth - 1;
+               continue;
+            }
+            else if (y >= lpDstSurface->h)
+            {
+               goto end; // No more pixels needed, break out
+            }
+
+            //
+            // Put the pixel onto the surface (FIXME: inefficient).
+            //
+            b = (lpBitmapRLE[j] & 0x0F);
+            if ((INT)b + iColorShift > 0x0F)
+            {
+               b = 0x0F;
+            }
+            else if ((INT)b + iColorShift < 0)
+            {
+               b = 0;
+            }
+            else
+            {
+               b += iColorShift;
+            }
+
+            ((LPBYTE)lpDstSurface->pixels)[y * lpDstSurface->pitch + x] =
+               (b | (lpBitmapRLE[j] & 0xF0));
+         }
+         lpBitmapRLE += T;
+         i += T;
+      }
+   }
+
+end:
+   //
+   // Success
+   //
+   return 0;
+}
+
+INT
+PAL_RLEBlitMonoColor(
+   LPCBITMAPRLE      lpBitmapRLE,
+   SDL_Surface      *lpDstSurface,
+   PAL_POS           pos,
+   BYTE              bColor,
+   INT               iColorShift
+)
+/*++
+  Purpose:
+
+    Blit an RLE-compressed bitmap to an SDL surface in mono-color form.
+    NOTE: Assume the surface is already locked, and the surface is a 8-bit one.
+
+  Parameters:
+
+    [IN]  lpBitmapRLE - pointer to the RLE-compressed bitmap to be decoded.
+
+    [OUT] lpDstSurface - pointer to the destination SDL surface.
+
+    [IN]  pos - position of the destination area.
+
+    [IN]  bColor - the color to be used while drawing.
+
+    [IN]  iColorShift - shift the color by this value.
+
+  Return value:
+
+    0 = success, -1 = error.
+
+--*/
+{
+   UINT          i, j;
+   INT           x, y;
+   UINT          uiLen       = 0;
+   UINT          uiWidth     = 0;
+   UINT          uiHeight    = 0;
+   BYTE          T, b;
+   INT           dx          = PAL_X(pos);
+   INT           dy          = PAL_Y(pos);
+
+   //
+   // Check for NULL pointer.
+   //
+   if (lpBitmapRLE == NULL || lpDstSurface == NULL)
+   {
+      return -1;
+   }
+
+   //
+   // Skip the 0x00000002 in the file header.
+   //
+   if (lpBitmapRLE[0] == 0x02 && lpBitmapRLE[1] == 0x00 &&
+      lpBitmapRLE[2] == 0x00 && lpBitmapRLE[3] == 0x00)
+   {
+      lpBitmapRLE += 4;
+   }
+
+   //
+   // Get the width and height of the bitmap.
+   //
+   uiWidth = lpBitmapRLE[0] | (lpBitmapRLE[1] << 8);
+   uiHeight = lpBitmapRLE[2] | (lpBitmapRLE[3] << 8);
+
+   //
+   // Calculate the total length of the bitmap.
+   // The bitmap is 8-bpp, each pixel will use 1 byte.
+   //
+   uiLen = uiWidth * uiHeight;
+
+   //
+   // Start decoding and blitting the bitmap.
+   //
+   lpBitmapRLE += 4;
+   bColor &= 0xF0;
+   for (i = 0; i < uiLen;)
+   {
+      T = *lpBitmapRLE++;
+      if ((T & 0x80) && T <= 0x80 + uiWidth)
+      {
+         i += T - 0x80;
+      }
+      else
+      {
+         for (j = 0; j < T; j++)
+         {
+            //
+            // Calculate the destination coordination.
+            // FIXME: This could be optimized
+            //
+            y = (i + j) / uiWidth + dy;
+            x = (i + j) % uiWidth + dx;
+
+            //
+            // Skip the points which are out of the surface.
+            //
+            if (x < 0)
+            {
+               j += -x - 1;
+               continue;
+            }
+            else if (x >= lpDstSurface->w)
+            {
+               j += x - lpDstSurface->w;
+               continue;
+            }
+
+            if (y < 0)
+            {
+               j += -y * uiWidth - 1;
+               continue;
+            }
+            else if (y >= lpDstSurface->h)
+            {
+               goto end; // No more pixels needed, break out
+            }
+
+            //
+            // Put the pixel onto the surface (FIXME: inefficient).
+            //
+            b = lpBitmapRLE[j] & 0x0F;
+            if ((INT)b + iColorShift > 0x0F)
+            {
+               b = 0x0F;
+            }
+            else if ((INT)b + iColorShift < 0)
+            {
+               b = 0;
+            }
+            else
+            {
+               b += iColorShift;
+            }
+            ((LPBYTE)lpDstSurface->pixels)[y * lpDstSurface->pitch + x] = (b | bColor);
+         }
+         lpBitmapRLE += T;
+         i += T;
+      }
+   }
+
+end:
+   //
+   // Success
+   //
+   return 0;
+}
+
+INT
+PAL_FBPBlitToSurface(
+   LPBYTE            lpBitmapFBP,
+   SDL_Surface      *lpDstSurface
+)
+/*++
+  Purpose:
+
+    Blit an uncompressed bitmap in FBP.MKF to an SDL surface.
+    NOTE: Assume the surface is already locked, and the surface is a 8-bit 320x200 one.
+
+  Parameters:
+
+    [IN]  lpBitmapFBP - pointer to the RLE-compressed bitmap to be decoded.
+
+    [OUT] lpDstSurface - pointer to the destination SDL surface.
+
+  Return value:
+
+    0 = success, -1 = error.
+
+--*/
+{
+   int       x, y;
+   LPBYTE    p;
+
+   if (lpBitmapFBP == NULL || lpDstSurface == NULL ||
+      lpDstSurface->w != 320 || lpDstSurface->h != 200)
+   {
+      return -1;
+   }
+
+   //
+   // simply copy everything to the surface
+   //
+   for (y = 0; y < 200; y++)
+   {
+      p = (LPBYTE)(lpDstSurface->pixels) + y * lpDstSurface->pitch;
+      for (x = 0; x < 320; x++)
+      {
+         *(p++) = *(lpBitmapFBP++);
+      }
+   }
+
+   return 0;
+}
+
+UINT
+PAL_RLEGetWidth(
+   LPCBITMAPRLE    lpBitmapRLE
+)
+/*++
+  Purpose:
+
+    Get the width of an RLE-compressed bitmap.
+
+  Parameters:
+
+    [IN]  lpBitmapRLE - pointer to an RLE-compressed bitmap.
+
+  Return value:
+
+    Integer value which indicates the height of the bitmap.
+
+--*/
+{
+   if (lpBitmapRLE == NULL)
+   {
+      return 0;
+   }
+
+   //
+   // Skip the 0x00000002 in the file header.
+   //
+   if (lpBitmapRLE[0] == 0x02 && lpBitmapRLE[1] == 0x00 &&
+      lpBitmapRLE[2] == 0x00 && lpBitmapRLE[3] == 0x00)
+   {
+      lpBitmapRLE += 4;
+   }
+
+   //
+   // Return the width of the bitmap.
+   //
+   return lpBitmapRLE[0] | (lpBitmapRLE[1] << 8);
+}
+
+UINT
+PAL_RLEGetHeight(
+   LPCBITMAPRLE       lpBitmapRLE
+)
+/*++
+  Purpose:
+
+    Get the height of an RLE-compressed bitmap.
+
+  Parameters:
+
+    [IN]  lpBitmapRLE - pointer of an RLE-compressed bitmap.
+
+  Return value:
+
+    Integer value which indicates the height of the bitmap.
+
+--*/
+{
+   if (lpBitmapRLE == NULL)
+   {
+      return 0;
+   }
+
+   //
+   // Skip the 0x00000002 in the file header.
+   //
+   if (lpBitmapRLE[0] == 0x02 && lpBitmapRLE[1] == 0x00 &&
+      lpBitmapRLE[2] == 0x00 && lpBitmapRLE[3] == 0x00)
+   {
+      lpBitmapRLE += 4;
+   }
+
+   //
+   // Return the height of the bitmap.
+   //
+   return lpBitmapRLE[2] | (lpBitmapRLE[3] << 8);
+}
+
+WORD
+PAL_SpriteGetNumFrames(
+   LPCSPRITE       lpSprite
+)
+/*++
+  Purpose:
+
+    Get the total number of frames of a sprite.
+
+  Parameters:
+
+    [IN]  lpSprite - pointer to the sprite.
+
+  Return value:
+
+    Number of frames of the sprite.
+
+--*/
+{
+   if (lpSprite == NULL)
+   {
+      return 0;
+   }
+
+   return (lpSprite[0] | (lpSprite[1] << 8)) - 1;
+}
+
+LPCBITMAPRLE
+PAL_SpriteGetFrame(
+   LPCSPRITE       lpSprite,
+   INT             iFrameNum
+)
+/*++
+  Purpose:
+
+    Get the pointer to the specified frame from a sprite.
+
+  Parameters:
+
+    [IN]  lpSprite - pointer to the sprite.
+
+    [IN]  iFrameNum - number of the frame.
+
+  Return value:
+
+    Pointer to the specified frame. NULL if the frame does not exist.
+
+--*/
+{
+   int imagecount, offset;
+
+   if (lpSprite == NULL)
+   {
+      return NULL;
+   }
+
+   //
+   // Hack for broken sprites like the Bloody-Mouth Bug
+   //
+//   imagecount = (lpSprite[0] | (lpSprite[1] << 8)) - 1;
+   imagecount = (lpSprite[0] | (lpSprite[1] << 8));
+
+   if (iFrameNum < 0 || iFrameNum >= imagecount)
+   {
+      //
+      // The frame does not exist
+      //
+      return NULL;
+   }
+
+   //
+   // Get the offset of the frame
+   //
+   iFrameNum <<= 1;
+   offset = (WORD)((lpSprite[iFrameNum] | (lpSprite[iFrameNum + 1] << 8)) << 1);
+   return &lpSprite[offset];
+}
+
+INT
+PAL_MKFGetChunkCount(
+   FILE *fp
+)
+/*++
+  Purpose:
+
+    Get the number of chunks in an MKF archive.
+
+  Parameters:
+
+    [IN]  fp - pointer to an fopen'ed MKF file.
+
+  Return value:
+
+    Integer value which indicates the number of chunks in the specified MKF file.
+
+--*/
+{
+   INT iNumChunk;
+   if (fp == NULL)
+   {
+      return 0;
+   }
+
+   fseek(fp, 0, SEEK_SET);
+   fread(&iNumChunk, sizeof(INT), 1, fp);
+
+   iNumChunk = (SWAP32(iNumChunk) - 4) / 4;
+   return iNumChunk;
+}
+
+INT
+PAL_MKFGetChunkSize(
+   UINT    uiChunkNum,
+   FILE   *fp
+)
+/*++
+  Purpose:
+
+    Get the size of a chunk in an MKF archive.
+
+  Parameters:
+
+    [IN]  uiChunkNum - the number of the chunk in the MKF archive.
+
+    [IN]  fp - pointer to the fopen'ed MKF file.
+
+  Return value:
+
+    Integer value which indicates the size of the chunk.
+    -1 if the chunk does not exist.
+
+--*/
+{
+   UINT    uiOffset       = 0;
+   UINT    uiNextOffset   = 0;
+   UINT    uiChunkCount   = 0;
+
+   //
+   // Get the total number of chunks.
+   //
+   uiChunkCount = PAL_MKFGetChunkCount(fp);
+   if (uiChunkNum >= uiChunkCount)
+   {
+      return -1;
+   }
+
+   //
+   // Get the offset of the specified chunk and the next chunk.
+   //
+   fseek(fp, 4 * uiChunkNum, SEEK_SET);
+   fread(&uiOffset, sizeof(UINT), 1, fp);
+   fread(&uiNextOffset, sizeof(UINT), 1, fp);
+   uiOffset = SWAP32(uiOffset);
+   uiNextOffset = SWAP32(uiNextOffset);
+
+   //
+   // Return the length of the chunk.
+   //
+   return uiNextOffset - uiOffset;
+}
+
+INT
+PAL_MKFReadChunk(
+   LPBYTE          lpBuffer,
+   UINT            uiBufferSize,
+   UINT            uiChunkNum,
+   FILE           *fp
+)
+/*++
+  Purpose:
+
+    Read a chunk from an MKF archive into lpBuffer.
+
+  Parameters:
+
+    [OUT] lpBuffer - pointer to the destination buffer.
+
+    [IN]  uiBufferSize - size of the destination buffer.
+
+    [IN]  uiChunkNum - the number of the chunk in the MKF archive to read.
+
+    [IN]  fp - pointer to the fopen'ed MKF file.
+
+  Return value:
+
+    Integer value which indicates the size of the chunk.
+    -1 if there are error in parameters.
+    -2 if buffer size is not enough.
+
+--*/
+{
+   UINT     uiOffset       = 0;
+   UINT     uiNextOffset   = 0;
+   UINT     uiChunkCount;
+   UINT     uiChunkLen;
+
+   if (lpBuffer == NULL || fp == NULL || uiBufferSize == 0)
+   {
+      return -1;
+   }
+
+   //
+   // Get the total number of chunks.
+   //
+   uiChunkCount = PAL_MKFGetChunkCount(fp);
+   if (uiChunkNum >= uiChunkCount)
+   {
+      return -1;
+   }
+
+   //
+   // Get the offset of the chunk.
+   //
+   fseek(fp, 4 * uiChunkNum, SEEK_SET);
+   fread(&uiOffset, 4, 1, fp);
+   fread(&uiNextOffset, 4, 1, fp);
+   uiOffset = SWAP32(uiOffset);
+   uiNextOffset = SWAP32(uiNextOffset);
+
+   //
+   // Get the length of the chunk.
+   //
+   uiChunkLen = uiNextOffset - uiOffset;
+
+   if (uiChunkLen > uiBufferSize)
+   {
+      return -2;
+   }
+
+   if (uiChunkLen != 0)
+   {
+      fseek(fp, uiOffset, SEEK_SET);
+      fread(lpBuffer, uiChunkLen, 1, fp);
+   }
+   else
+   {
+      return -1;
+   }
+
+   return (INT)uiChunkLen;
+}
+
+INT
+PAL_MKFGetDecompressedSize(
+   UINT    uiChunkNum,
+   FILE   *fp
+)
+/*++
+  Purpose:
+
+    Get the decompressed size of a compressed chunk in an MKF archive.
+
+  Parameters:
+
+    [IN]  uiChunkNum - the number of the chunk in the MKF archive.
+
+    [IN]  fp - pointer to the fopen'ed MKF file.
+
+  Return value:
+
+    Integer value which indicates the size of the chunk.
+    -1 if the chunk does not exist.
+
+--*/
+{
+   DWORD         buf[2];
+   UINT          uiOffset;
+   UINT          uiChunkCount;
+
+   if (fp == NULL)
+   {
+      return -1;
+   }
+
+   //
+   // Get the total number of chunks.
+   //
+   uiChunkCount = PAL_MKFGetChunkCount(fp);
+   if (uiChunkNum >= uiChunkCount)
+   {
+      return -1;
+   }
+
+   //
+   // Get the offset of the chunk.
+   //
+   fseek(fp, 4 * uiChunkNum, SEEK_SET);
+   fread(&uiOffset, 4, 1, fp);
+   uiOffset = SWAP32(uiOffset);
+
+   //
+   // Read the header.
+   //
+   fseek(fp, uiOffset, SEEK_SET);
+   fread(buf, sizeof(DWORD), 2, fp);
+
+   buf[0] = SWAP32(buf[0]);
+   buf[1] = SWAP32(buf[1]);
+
+   return (buf[0] != 0x315f4a59) ? -1 : (INT)buf[1];
+}
+
+INT
+PAL_MKFDecompressChunk(
+   LPBYTE          lpBuffer,
+   UINT            uiBufferSize,
+   UINT            uiChunkNum,
+   FILE           *fp
+)
+/*++
+  Purpose:
+
+    Decompress a compressed chunk from an MKF archive into lpBuffer.
+
+  Parameters:
+
+    [OUT] lpBuffer - pointer to the destination buffer.
+
+    [IN]  uiBufferSize - size of the destination buffer.
+
+    [IN]  uiChunkNum - the number of the chunk in the MKF archive to read.
+
+    [IN]  fp - pointer to the fopen'ed MKF file.
+
+  Return value:
+
+    Integer value which indicates the size of the chunk.
+    -1 if there are error in parameters, or buffer size is not enough.
+    -3 if cannot allocate memory for decompression.
+
+--*/
+{
+   LPBYTE          buf;
+   int             len;
+
+   len = PAL_MKFGetChunkSize(uiChunkNum, fp);
+
+   if (len <= 0)
+   {
+      return len;
+   }
+
+   buf = (LPBYTE)malloc(len);
+   if (buf == NULL)
+   {
+      return -3;
+   }
+
+   PAL_MKFReadChunk(buf, len, uiChunkNum, fp);
+
+   len = Decompress(buf, lpBuffer, uiBufferSize);
+   free(buf);
+
+   return len;
+}

+ 145 - 0
palcommon.h

@@ -0,0 +1,145 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef _PALUTILS_H
+#define _PALUTILS_H
+
+#include "common.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef LPBYTE      LPSPRITE, LPBITMAPRLE;
+typedef LPCBYTE     LPCSPRITE, LPCBITMAPRLE;
+
+typedef DWORD           PAL_POS;
+
+#define PAL_XY(x, y)    (PAL_POS)(((((WORD)(y)) << 16) & 0xFFFF0000) | (((WORD)(x)) & 0xFFFF))
+#define PAL_X(xy)       (SHORT)((xy) & 0xFFFF)
+#define PAL_Y(xy)       (SHORT)(((xy) >> 16) & 0xFFFF)
+
+typedef enum tagPALDIRECTION
+{
+   kDirSouth = 0,
+   kDirWest,
+   kDirNorth,
+   kDirEast,
+   kDirUnknown
+} PALDIRECTION, *LPPALDIRECTION;
+
+INT
+PAL_RLEBlitToSurface(
+   LPCBITMAPRLE      lpBitmapRLE,
+   SDL_Surface      *lpDstSurface,
+   PAL_POS           pos
+);
+
+INT
+PAL_RLEBlitWithColorShift(
+   LPCBITMAPRLE      lpBitmapRLE,
+   SDL_Surface      *lpDstSurface,
+   PAL_POS           pos,
+   INT               iColorShift
+);
+
+INT
+PAL_RLEBlitMonoColor(
+   LPCBITMAPRLE      lpBitmapRLE,
+   SDL_Surface      *lpDstSurface,
+   PAL_POS           pos,
+   BYTE              bColor,
+   INT               iColorShift
+);
+
+INT
+PAL_FBPBlitToSurface(
+   LPBYTE            lpBitmapFBP,
+   SDL_Surface      *lpDstSurface
+);
+
+UINT
+PAL_RLEGetWidth(
+   LPCBITMAPRLE      lpBitmapRLE
+);
+
+UINT
+PAL_RLEGetHeight(
+   LPCBITMAPRLE      lpBitmapRLE
+);
+
+WORD
+PAL_SpriteGetNumFrames(
+   LPCSPRITE       lpSprite
+);
+
+LPCBITMAPRLE
+PAL_SpriteGetFrame(
+   LPCSPRITE       lpSprite,
+   INT             iFrameNum
+);
+
+INT
+PAL_MKFGetChunkCount(
+   FILE *fp
+);
+
+INT
+PAL_MKFGetChunkSize(
+   UINT    uiChunkNum,
+   FILE   *fp
+);
+
+INT
+PAL_MKFReadChunk(
+   LPBYTE          lpBuffer,
+   UINT            uiBufferSize,
+   UINT            uiChunkNum,
+   FILE           *fp
+);
+
+INT
+PAL_MKFGetDecompressedSize(
+   UINT    uiChunkNum,
+   FILE   *fp
+);
+
+INT
+PAL_MKFDecompressChunk(
+   LPBYTE          lpBuffer,
+   UINT            uiBufferSize,
+   UINT            uiChunkNum,
+   FILE           *fp
+);
+
+// From yj1.c:
+INT
+Decompress(
+   LPCVOID      Source,
+   LPVOID       Destination,
+   INT          DestSize
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _PALUTILS_H

+ 652 - 0
palette.c

@@ -0,0 +1,652 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "main.h"
+
+SDL_Color *
+PAL_GetPalette(
+   INT         iPaletteNum,
+   BOOL        fNight
+)
+/*++
+  Purpose:
+
+    Get the specified palette in pat.mkf file.
+
+  Parameters:
+
+    [IN]  iPaletteNum - number of the palette.
+
+    [IN]  fNight - whether use the night palette or not.
+
+  Return value:
+
+    Pointer to the palette. NULL if failed.
+
+--*/
+{
+   static SDL_Color      palette[256];
+   PAL_LARGE BYTE        buf[1536];
+   INT                   i;
+   FILE                 *fp;
+
+   fp = UTIL_OpenRequiredFile("pat.mkf");
+
+   //
+   // Read the palette data from the pat.mkf file
+   //
+   i = PAL_MKFReadChunk(buf, 1536, iPaletteNum, fp);
+
+   fclose(fp);
+
+   if (i < 0)
+   {
+      //
+      // Read failed
+      //
+      return NULL;
+   }
+   else if (i <= 256 * 3)
+   {
+      //
+      // There is no night colors in the palette
+      //
+      fNight = FALSE;
+   }
+
+   for (i = 0; i < 256; i++)
+   {
+      palette[i].r = buf[(fNight ? 256 * 3 : 0) + i * 3] << 2;
+      palette[i].g = buf[(fNight ? 256 * 3 : 0) + i * 3 + 1] << 2;
+      palette[i].b = buf[(fNight ? 256 * 3 : 0) + i * 3 + 2] << 2;
+#if 0
+      palette[i].r += (255 - palette[i].r) / 5;
+      palette[i].g += (255 - palette[i].g) / 5;
+      palette[i].b += (255 - palette[i].b) / 5;
+#endif
+   }
+
+   return palette;
+}
+
+VOID
+PAL_SetPalette(
+   INT         iPaletteNum,
+   BOOL        fNight
+)
+/*++
+  Purpose:
+
+    Set the screen palette to the specified one.
+
+  Parameters:
+
+    [IN]  iPaletteNum - number of the palette.
+
+    [IN]  fNight - whether use the night palette or not.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   SDL_Color *p = PAL_GetPalette(iPaletteNum, fNight);
+
+   if (p != NULL)
+   {
+      VIDEO_SetPalette(p);
+   }
+}
+
+VOID
+PAL_FadeOut(
+   INT         iDelay
+)
+/*++
+  Purpose:
+
+    Fadeout screen to black from the specified palette.
+
+  Parameters:
+
+    [IN]  iPaletteNum - number of the palette.
+
+    [IN]  fNight - whether use the night palette or not.
+
+    [IN]  iDelay - delay time for each step.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   int                      i, j;
+   UINT                     time;
+   PAL_LARGE SDL_Color      palette[256];
+   PAL_LARGE SDL_Color      newpalette[256];
+
+   //
+   // Get the original palette...
+   //
+   for (i = 0; i < 256; i++)
+   {
+      palette[i] = VIDEO_GetPalette()[i];
+   }
+
+   //
+   // Start fading out...
+   //
+   time = SDL_GetTicks() + iDelay * 10 * 60;
+
+   while (TRUE)
+   {
+      //
+      // Set the current palette...
+      //
+      j = (int)(time - SDL_GetTicks()) / iDelay / 10;
+      if (j < 0)
+      {
+         break;
+      }
+
+      for (i = 0; i < 256; i++)
+      {
+         newpalette[i].r = (palette[i].r * j) >> 6;
+         newpalette[i].g = (palette[i].g * j) >> 6;
+         newpalette[i].b = (palette[i].b * j) >> 6;
+      }
+
+      VIDEO_SetPalette(newpalette);
+
+      UTIL_Delay(10);
+   }
+
+   memset(newpalette, 0, sizeof(newpalette));
+   VIDEO_SetPalette(newpalette);
+}
+
+VOID
+PAL_FadeIn(
+   INT         iPaletteNum,
+   BOOL        fNight,
+   INT         iDelay
+)
+/*++
+  Purpose:
+
+    Fade in the screen to the specified palette.
+
+  Parameters:
+
+    [IN]  iPaletteNum - number of the palette.
+
+    [IN]  fNight - whether use the night palette or not.
+
+    [IN]  iDelay - delay time for each step.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   int                      i, j;
+   UINT                     time;
+   SDL_Color               *palette;
+   PAL_LARGE SDL_Color      newpalette[256];
+
+   //
+   // Get the new palette...
+   //
+   palette = PAL_GetPalette(iPaletteNum, fNight);
+
+   //
+   // Start fading in...
+   //
+   time = SDL_GetTicks() + iDelay * 10 * 60;
+   while (TRUE)
+   {
+      //
+      // Set the current palette...
+      //
+      j = (int)(time - SDL_GetTicks()) / iDelay / 10;
+      if (j < 0)
+      {
+         break;
+      }
+
+      j = 60 - j;
+
+      for (i = 0; i < 256; i++)
+      {
+         newpalette[i].r = (palette[i].r * j) >> 6;
+         newpalette[i].g = (palette[i].g * j) >> 6;
+         newpalette[i].b = (palette[i].b * j) >> 6;
+      }
+
+      VIDEO_SetPalette(newpalette);
+
+      UTIL_Delay(10);
+   }
+
+   VIDEO_SetPalette(palette);
+}
+
+VOID
+PAL_SceneFade(
+   INT         iPaletteNum,
+   BOOL        fNight,
+   INT         iStep
+)
+/*++
+  Purpose:
+
+    Fade in or fade out the screen. Update the scene during the process.
+
+  Parameters:
+
+    [IN]  iPaletteNum - number of the palette.
+
+    [IN]  fNight - whether use the night palette or not.
+
+    [IN]  iStep - positive to fade in, nagative to fade out.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   SDL_Color            *palette, newpalette[256];
+   int                   i, j;
+   DWORD                 time;
+
+   palette = PAL_GetPalette(iPaletteNum, fNight);
+
+   if (palette == NULL)
+   {
+      return;
+   }
+
+   if (iStep == 0)
+   {
+      iStep = 1;
+   }
+
+   gpGlobals->fNeedToFadeIn = FALSE;
+
+   if (iStep > 0)
+   {
+      for (i = 0; i < 64; i += iStep)
+      {
+         time = SDL_GetTicks() + 100;
+
+         //
+         // Generate the scene
+         //
+         PAL_ClearKeyState();
+         g_InputState.dir = kDirUnknown;
+         g_InputState.prevdir = kDirUnknown;
+         PAL_GameUpdate(FALSE);
+         PAL_MakeScene();
+         VIDEO_UpdateScreen(NULL);
+
+         //
+         // Calculate the current palette...
+         //
+         for (j = 0; j < 256; j++)
+         {
+            newpalette[j].r = (palette[j].r * i) >> 6;
+            newpalette[j].g = (palette[j].g * i) >> 6;
+            newpalette[j].b = (palette[j].b * i) >> 6;
+         }
+         VIDEO_SetPalette(newpalette);
+
+         while (SDL_PollEvent(NULL));
+
+         while (SDL_GetTicks() < time)
+         {
+            while (SDL_PollEvent(NULL));
+            SDL_Delay(5);
+         }
+      }
+   }
+   else
+   {
+      for (i = 63; i >= 0; i += iStep)
+      {
+         time = SDL_GetTicks() + 100;
+
+         //
+         // Generate the scene
+         //
+         PAL_ClearKeyState();
+         g_InputState.dir = kDirUnknown;
+         g_InputState.prevdir = kDirUnknown;
+         PAL_GameUpdate(FALSE);
+         PAL_MakeScene();
+         VIDEO_UpdateScreen(NULL);
+
+         //
+         // Calculate the current palette...
+         //
+         for (j = 0; j < 256; j++)
+         {
+            newpalette[j].r = (palette[j].r * i) >> 6;
+            newpalette[j].g = (palette[j].g * i) >> 6;
+            newpalette[j].b = (palette[j].b * i) >> 6;
+         }
+         VIDEO_SetPalette(newpalette);
+
+         while (SDL_PollEvent(NULL));
+
+         while (SDL_GetTicks() < time)
+         {
+            while (SDL_PollEvent(NULL));
+            SDL_Delay(5);
+         }
+      }
+   }
+}
+
+VOID
+PAL_PaletteFade(
+   INT         iPaletteNum,
+   BOOL        fNight,
+   BOOL        fUpdateScene
+)
+/*++
+  Purpose:
+
+    Fade from the current palette to the specified one.
+
+  Parameters:
+
+    [IN]  iPaletteNum - number of the palette.
+
+    [IN]  fNight - whether use the night palette or not.
+
+    [IN]  fUpdateScene - TRUE if update the scene in the progress.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   int            i, j;
+   UINT           time;
+   SDL_Color     *newpalette = PAL_GetPalette(iPaletteNum, fNight);
+   PAL_LARGE SDL_Color      palette[256];
+   PAL_LARGE SDL_Color		t[256];
+
+   if (newpalette == NULL)
+   {
+      return;
+   }
+
+   for (i = 0; i < 256; i++)
+   {
+      palette[i] = VIDEO_GetPalette()[i];
+   }
+
+   //
+   // Start fading...
+   //
+   for (i = 0; i < 32; i++)
+   {
+      time = SDL_GetTicks() + (fUpdateScene ? FRAME_TIME : FRAME_TIME / 4);
+
+      for (j = 0; j < 256; j++)
+      {
+         t[j].r =
+            (BYTE)(((INT)(palette[j].r) * (31 - i) + (INT)(newpalette[j].r) * i) / 31);
+         t[j].g =
+            (BYTE)(((INT)(palette[j].g) * (31 - i) + (INT)(newpalette[j].g) * i) / 31);
+         t[j].b =
+            (BYTE)(((INT)(palette[j].b) * (31 - i) + (INT)(newpalette[j].b) * i) / 31);
+      }
+      VIDEO_SetPalette(t);
+
+      if (fUpdateScene)
+      {
+         PAL_ClearKeyState();
+         g_InputState.dir = kDirUnknown;
+         g_InputState.prevdir = kDirUnknown;
+         PAL_GameUpdate(FALSE);
+         PAL_MakeScene();
+         VIDEO_UpdateScreen(NULL);
+      }
+
+      while (SDL_PollEvent(NULL));
+
+      while (SDL_GetTicks() < time)
+      {
+         while (SDL_PollEvent(NULL));
+         SDL_Delay(5);
+      }
+   }
+}
+
+VOID
+PAL_ColorFade(
+   INT        iDelay,
+   BYTE       bColor,
+   BOOL       fFrom
+)
+/*++
+  Purpose:
+
+    Fade the palette from/to the specified color.
+
+  Parameters:
+
+    [IN]  iDelay - the delay time of each step.
+
+    [IN]  bColor - the color to fade from/to.
+
+    [IN]  fFrom - if TRUE then fade from bColor, else fade to bColor.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   SDL_Color       *palette;
+   PAL_LARGE SDL_Color        newpalette[256];
+   int              i, j;
+
+   palette = PAL_GetPalette(gpGlobals->wNumPalette, gpGlobals->fNightPalette);
+
+   iDelay *= 10;
+   if (iDelay == 0)
+   {
+      iDelay = 10;
+   }
+
+   if (fFrom)
+   {
+      for (i = 0; i < 256; i++)
+      {
+         newpalette[i] = palette[bColor];
+      }
+
+      for (i = 0; i < 64; i++)
+      {
+         for (j = 0; j < 256; j++)
+         {
+            if (newpalette[j].r > palette[j].r)
+            {
+               newpalette[j].r -= 4;
+            }
+            else if (newpalette[j].r < palette[j].r)
+            {
+               newpalette[j].r += 4;
+            }
+
+            if (newpalette[j].g > palette[j].g)
+            {
+               newpalette[j].g -= 4;
+            }
+            else if (newpalette[j].g < palette[j].g)
+            {
+               newpalette[j].g += 4;
+            }
+
+            if (newpalette[j].b > palette[j].b)
+            {
+               newpalette[j].b -= 4;
+            }
+            else if (newpalette[j].b < palette[j].b)
+            {
+               newpalette[j].b += 4;
+            }
+         }
+
+         VIDEO_SetPalette(newpalette);
+         UTIL_Delay(iDelay);
+      }
+
+      VIDEO_SetPalette(palette);
+   }
+   else
+   {
+      memcpy(newpalette, palette, sizeof(newpalette));
+
+      for (i = 0; i < 64; i++)
+      {
+         for (j = 0; j < 256; j++)
+         {
+            if (newpalette[j].r > palette[bColor].r)
+            {
+               newpalette[j].r -= 4;
+            }
+            else if (newpalette[j].r < palette[bColor].r)
+            {
+               newpalette[j].r += 4;
+            }
+
+            if (newpalette[j].g > palette[bColor].g)
+            {
+               newpalette[j].g -= 4;
+            }
+            else if (newpalette[j].g < palette[bColor].g)
+            {
+               newpalette[j].g += 4;
+            }
+
+            if (newpalette[j].b > palette[bColor].b)
+            {
+               newpalette[j].b -= 4;
+            }
+            else if (newpalette[j].b < palette[bColor].b)
+            {
+               newpalette[j].b += 4;
+            }
+         }
+
+         VIDEO_SetPalette(newpalette);
+         UTIL_Delay(iDelay);
+      }
+
+      for (i = 0; i < 256; i++)
+      {
+         newpalette[i] = palette[bColor];
+      }
+
+      VIDEO_SetPalette(newpalette);
+   }
+}
+
+VOID
+PAL_FadeToRed(
+   VOID
+)
+/*++
+  Purpose:
+
+    Fade the whole screen to red color.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   SDL_Color                 *palette;
+   PAL_LARGE SDL_Color        newpalette[256];
+   int                        i, j;
+   BYTE                       color;
+
+   palette = PAL_GetPalette(gpGlobals->wNumPalette, gpGlobals->fNightPalette);
+   memcpy(newpalette, palette, sizeof(newpalette));
+
+   for (i = 0; i < gpScreen->pitch * gpScreen->h; i++)
+   {
+      if (((LPBYTE)(gpScreen->pixels))[i] == 0x4F)
+      {
+         ((LPBYTE)(gpScreen->pixels))[i] = 0x4E; // HACKHACK
+      }
+   }
+
+   VIDEO_UpdateScreen(NULL);
+
+   for (i = 0; i < 32; i++)
+   {
+      for (j = 0; j < 256; j++)
+      {
+         if (j == 0x4F)
+         {
+            continue; // so that texts will not be affected
+         }
+
+         color = ((INT)palette[j].r + (INT)palette[j].g + (INT)palette[j].b) / 4 + 64;
+
+         if (newpalette[j].r > color)
+         {
+            newpalette[j].r -= (newpalette[j].r - color > 8 ? 8 : newpalette[j].r - color);
+         }
+         else if (newpalette[j].r < color)
+         {
+            newpalette[j].r += (color - newpalette[j].r > 8 ? 8 : color - newpalette[j].r);
+         }
+
+         if (newpalette[j].g > 0)
+         {
+            newpalette[j].g -= (newpalette[j].g > 8 ? 8 : newpalette[j].g);
+         }
+
+         if (newpalette[j].b > 0)
+         {
+            newpalette[j].b -= (newpalette[j].b > 8 ? 8 : newpalette[j].b);
+         }
+      }
+
+      VIDEO_SetPalette(newpalette);
+      UTIL_Delay(75);
+   }
+}

+ 85 - 0
palette.h

@@ -0,0 +1,85 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef PALETTE_H
+#define PALETTE_H
+
+#include "common.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+SDL_Color *
+PAL_GetPalette(
+   INT         iPaletteNum,
+   BOOL        fNight
+);
+
+VOID
+PAL_SetPalette(
+   INT         iPaletteNum,
+   BOOL        fNight
+);
+
+VOID
+PAL_FadeOut(
+   INT         iDelay
+);
+
+VOID
+PAL_FadeIn(
+   INT         iPaletteNum,
+   BOOL        fNight,
+   INT         iDelay
+);
+
+VOID
+PAL_SceneFade(
+   INT         iPaletteNum,
+   BOOL        fNight,
+   INT         iStep
+);
+
+VOID
+PAL_PaletteFade(
+   INT         iPaletteNum,
+   BOOL        fNight,
+   INT         iDelay
+);
+
+VOID
+PAL_ColorFade(
+   INT        iDelay,
+   BYTE       bColor,
+   BOOL       fFrom
+);
+
+VOID
+PAL_FadeToRed(
+   VOID
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 623 - 0
play.c

@@ -0,0 +1,623 @@
+//
+// Copyright (c) 2008, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// Portions based on PALx Project by palxex.
+// Copyright (c) 2006, Pal Lockheart <palxex@gmail.com>.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "main.h"
+
+VOID
+PAL_GameUpdate(
+   BOOL       fTrigger
+)
+/*++
+  Purpose:
+
+    The main game logic routine. Update the status of everything.
+
+  Parameters:
+
+    [IN]  fTrigger - whether to process trigger events or not.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   WORD            wEventObjectID, wDir;
+   int             i;
+   LPEVENTOBJECT   p;
+
+   //
+   // Check for trigger events
+   //
+   if (fTrigger)
+   {
+      //
+      // Check if we are entering a new scene
+      //
+      if (gpGlobals->fEnteringScene)
+      {
+         //
+         // Run the script for entering the scene
+         //
+         gpGlobals->fEnteringScene = FALSE;
+
+         i = gpGlobals->wNumScene - 1;
+         gpGlobals->g.rgScene[i].wScriptOnEnter =
+            PAL_RunTriggerScript(gpGlobals->g.rgScene[i].wScriptOnEnter, 0xFFFF);
+
+         if (gpGlobals->fEnteringScene || gpGlobals->fGameStart)
+         {
+            //
+            // Don't go further as we're switching to another scene
+            //
+            return;
+         }
+
+         PAL_ClearKeyState();
+         PAL_MakeScene();
+      }
+
+      //
+      // Update the vanish time for all event objects
+      //
+      for (wEventObjectID = 0; wEventObjectID < gpGlobals->g.nEventObject; wEventObjectID++)
+      {
+         p = &gpGlobals->g.lprgEventObject[wEventObjectID];
+
+         if (p->sVanishTime != 0)
+         {
+            p->sVanishTime += ((p->sVanishTime < 0) ? 1 : -1);
+         }
+      }
+
+      //
+      // Loop through all event objects in the current scene
+      //
+      for (wEventObjectID = gpGlobals->g.rgScene[gpGlobals->wNumScene - 1].wEventObjectIndex + 1;
+         wEventObjectID <= gpGlobals->g.rgScene[gpGlobals->wNumScene].wEventObjectIndex;
+         wEventObjectID++)
+      {
+         p = &gpGlobals->g.lprgEventObject[wEventObjectID - 1];
+
+         if (p->sVanishTime != 0)
+         {
+            continue;
+         }
+
+         if (p->sState < 0)
+         {
+            if (p->x < PAL_X(gpGlobals->viewport) ||
+               p->x > PAL_X(gpGlobals->viewport) + 320 ||
+               p->y < PAL_Y(gpGlobals->viewport) ||
+               p->y > PAL_Y(gpGlobals->viewport) + 320)
+            {
+               p->sState = abs(p->sState);
+               p->wCurrentFrameNum = 0;
+            }
+         }
+         else if (p->sState > 0 && p->wTriggerMode >= kTriggerTouchNear)
+         {
+            //
+            // This event object can be triggered without manually exploring
+            //
+            if (abs(PAL_X(gpGlobals->viewport) + PAL_X(gpGlobals->partyoffset) - p->x) +
+               abs(PAL_Y(gpGlobals->viewport) + PAL_Y(gpGlobals->partyoffset) - p->y) * 2 <
+               (p->wTriggerMode - kTriggerTouchNear) * 32 + 16)
+            {
+               //
+               // Player is in the trigger zone.
+               //
+
+               if (p->nSpriteFrames)
+               {
+                  //
+                  // The sprite has multiple frames. Try to adjust the direction.
+                  //
+                  int                xOffset, yOffset;
+
+                  p->wCurrentFrameNum = 0;
+
+                  xOffset = PAL_X(gpGlobals->viewport) + PAL_X(gpGlobals->partyoffset) - p->x;
+                  yOffset = PAL_Y(gpGlobals->viewport) + PAL_Y(gpGlobals->partyoffset) - p->y;
+
+                  if (xOffset > 0)
+                  {
+                     p->wDirection = ((yOffset > 0) ? kDirEast : kDirNorth);
+                  }
+                  else
+                  {
+                     p->wDirection = ((yOffset > 0) ? kDirSouth : kDirWest);
+                  }
+
+                  //
+                  // Redraw the scene
+                  //
+                  PAL_UpdatePartyGestures(FALSE);
+
+                  PAL_MakeScene();
+                  VIDEO_UpdateScreen(NULL);
+               }
+
+               //
+               // Execute the script.
+               //
+               p->wTriggerScript = PAL_RunTriggerScript(p->wTriggerScript, wEventObjectID);
+
+               PAL_ClearKeyState();
+
+               if (gpGlobals->fEnteringScene || gpGlobals->fGameStart)
+               {
+                  //
+                  // Don't go further on scene switching
+                  //
+                  return;
+               }
+            }
+         }
+      }
+   }
+
+   //
+   // Run autoscript for each event objects
+   //
+   for (wEventObjectID = gpGlobals->g.rgScene[gpGlobals->wNumScene - 1].wEventObjectIndex + 1;
+      wEventObjectID <= gpGlobals->g.rgScene[gpGlobals->wNumScene].wEventObjectIndex;
+      wEventObjectID++)
+   {
+      p = &gpGlobals->g.lprgEventObject[wEventObjectID - 1];
+
+      if (p->sState > 0 && p->sVanishTime == 0)
+      {
+         WORD wScriptEntry = p->wAutoScript;
+         if (wScriptEntry != 0)
+         {
+            p->wAutoScript = PAL_RunAutoScript(wScriptEntry, wEventObjectID);
+            if (gpGlobals->fEnteringScene || gpGlobals->fGameStart)
+            {
+               //
+               // Don't go further on scene switching
+               //
+               return;
+            }
+         }
+      }
+
+      //
+      // Check if the player is in the way
+      //
+      if (fTrigger && p->sState >= kObjStateBlocker && p->wSpriteNum != 0 &&
+         abs(p->x - PAL_X(gpGlobals->viewport) - PAL_X(gpGlobals->partyoffset)) +
+         abs(p->y - PAL_Y(gpGlobals->viewport) - PAL_Y(gpGlobals->partyoffset)) * 2 <= 12)
+      {
+         //
+         // Player is in the way, try to move a step
+         //
+         wDir = (p->wDirection + 1) % 4;
+         for (i = 0; i < 4; i++)
+         {
+            int              x, y;
+            PAL_POS          pos;
+
+            x = PAL_X(gpGlobals->viewport) + PAL_X(gpGlobals->partyoffset);
+            y = PAL_Y(gpGlobals->viewport) + PAL_Y(gpGlobals->partyoffset);
+
+            x += ((wDir == kDirWest || wDir == kDirSouth) ? -16 : 16);
+            y += ((wDir == kDirWest || wDir == kDirNorth) ? -8 : 8);
+
+            pos = PAL_XY(x, y);
+
+            if (!PAL_CheckObstacle(pos, TRUE, 0))
+            {
+               //
+               // move here
+               //
+               gpGlobals->viewport = PAL_XY(
+                  PAL_X(pos) - PAL_X(gpGlobals->partyoffset),
+                  PAL_Y(pos) - PAL_Y(gpGlobals->partyoffset));
+
+               break;
+            }
+
+            wDir = (wDir + 1) % 4;
+         }
+      }
+   }
+
+   gpGlobals->dwFrameNum++;
+}
+
+VOID
+PAL_GameUseItem(
+   VOID
+)
+/*++
+  Purpose:
+
+    Allow player use an item in the game.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   WORD         wObject;
+
+   while (TRUE)
+   {
+      wObject = PAL_ItemSelectMenu(NULL, kItemFlagUsable);
+
+      if (wObject == 0)
+      {
+         return;
+      }
+
+      if (!(gpGlobals->g.rgObject[wObject].item.wFlags & kItemFlagApplyToAll))
+      {
+         //
+         // Select the player to use the item on
+         //
+         WORD     wPlayer = 0;
+
+         while (TRUE)
+         {
+            wPlayer = PAL_ItemUseMenu(wObject);
+
+            if (wPlayer == MENUITEM_VALUE_CANCELLED)
+            {
+               break;
+            }
+
+            //
+            // Run the script
+            //
+            gpGlobals->g.rgObject[wObject].item.wScriptOnUse =
+               PAL_RunTriggerScript(gpGlobals->g.rgObject[wObject].item.wScriptOnUse, wPlayer);
+
+            //
+            // Remove the item if the item is consuming and the script succeeded
+            //
+            if ((gpGlobals->g.rgObject[wObject].item.wFlags & kItemFlagConsuming) &&
+               g_fScriptSuccess)
+            {
+               PAL_AddItemToInventory(wObject, -1);
+            }
+         }
+      }
+      else
+      {
+         //
+         // Run the script
+         //
+         gpGlobals->g.rgObject[wObject].item.wScriptOnUse =
+            PAL_RunTriggerScript(gpGlobals->g.rgObject[wObject].item.wScriptOnUse, 0xFFFF);
+
+         //
+         // Remove the item if the item is consuming and the script succeeded
+         //
+         if ((gpGlobals->g.rgObject[wObject].item.wFlags & kItemFlagConsuming) &&
+            g_fScriptSuccess)
+         {
+            PAL_AddItemToInventory(wObject, -1);
+         }
+
+         return;
+      }
+   }
+}
+
+VOID
+PAL_GameEquipItem(
+   VOID
+)
+/*++
+  Purpose:
+
+    Allow player equip an item in the game.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   WORD      wObject;
+
+   while (TRUE)
+   {
+      wObject = PAL_ItemSelectMenu(NULL, kItemFlagEquipable);
+
+      if (wObject == 0)
+      {
+         return;
+      }
+
+      PAL_EquipItemMenu(wObject);
+   }
+}
+
+VOID
+PAL_Search(
+   VOID
+)
+/*++
+  Purpose:
+
+    Process searching trigger events.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   int                x, y, xOffset, yOffset, dx, dy, dh, ex, ey, eh, i, k, l;
+   LPEVENTOBJECT      p;
+   PAL_POS            rgPos[13];
+
+   //
+   // Get the party location
+   //
+   x = PAL_X(gpGlobals->viewport) + PAL_X(gpGlobals->partyoffset);
+   y = PAL_Y(gpGlobals->viewport) + PAL_Y(gpGlobals->partyoffset);
+
+   if (gpGlobals->wPartyDirection == kDirNorth || gpGlobals->wPartyDirection == kDirEast)
+   {
+      xOffset = 16;
+   }
+   else
+   {
+      xOffset = -16;
+   }
+
+   if (gpGlobals->wPartyDirection == kDirEast || gpGlobals->wPartyDirection == kDirSouth)
+   {
+      yOffset = 8;
+   }
+   else
+   {
+      yOffset = -8;
+   }
+
+   rgPos[0] = PAL_XY(x, y);
+
+   for (i = 0; i < 4; i++)
+   {
+      rgPos[i * 3 + 1] = PAL_XY(x + xOffset, y + yOffset);
+      rgPos[i * 3 + 2] = PAL_XY(x, y + yOffset * 2);
+      rgPos[i * 3 + 3] = PAL_XY(x + xOffset, y);
+      x += xOffset;
+      y += yOffset;
+   }
+
+   for (i = 0; i < 13; i++)
+   {
+      //
+      // Convert to map location
+      //
+      dh = ((PAL_X(rgPos[i]) % 32) ? 1 : 0);
+      dx = PAL_X(rgPos[i]) / 32;
+      dy = PAL_Y(rgPos[i]) / 16;
+
+      //
+      // Loop through all event objects
+      //
+      for (k = gpGlobals->g.rgScene[gpGlobals->wNumScene - 1].wEventObjectIndex;
+         k < gpGlobals->g.rgScene[gpGlobals->wNumScene].wEventObjectIndex; k++)
+      {
+         p = &(gpGlobals->g.lprgEventObject[k]);
+         ex = p->x / 32;
+         ey = p->y / 16;
+         eh = ((p->x % 32) ? 1 : 0);
+
+         if (p->sState <= 0 || p->wTriggerMode >= kTriggerTouchNear ||
+            p->wTriggerMode * 6 - 4 < i || dx != ex || dy != ey || dh != eh)
+         {
+            continue;
+         }
+
+         //
+         // Adjust direction/gesture for party members and the event object
+         //
+         if (p->nSpriteFrames * 4 > p->wCurrentFrameNum)
+         {
+            p->wCurrentFrameNum = 0; // use standing gesture
+            p->wDirection = (gpGlobals->wPartyDirection + 2) % 4; // face the party
+
+            for (l = 0; l <= gpGlobals->wMaxPartyMemberIndex; l++)
+            {
+               //
+               // All party members should face the event object
+               //
+               gpGlobals->rgParty[l].wFrame = gpGlobals->wPartyDirection * 3;
+            }
+
+            //
+            // Redraw everything
+            //
+            PAL_MakeScene();
+            VIDEO_UpdateScreen(NULL);
+         }
+
+         //
+         // Execute the script
+         //
+         p->wTriggerScript = PAL_RunTriggerScript(p->wTriggerScript, k + 1);
+
+         //
+         // Clear inputs and delay for a short time
+         //
+         UTIL_Delay(50);
+         PAL_ClearKeyState();
+
+         return; // don't go further
+      }
+   }
+}
+
+VOID
+PAL_StartFrame(
+   VOID
+)
+/*++
+  Purpose:
+
+    Starts a video frame. Called once per video frame.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   //
+   // Run the game logic of one frame
+   //
+   PAL_GameUpdate(TRUE);
+   if (gpGlobals->fEnteringScene)
+   {
+      return;
+   }
+
+   //
+   // Update the positions and gestures of party members
+   //
+   PAL_UpdateParty();
+
+   //
+   // Update the scene
+   //
+   PAL_MakeScene();
+   VIDEO_UpdateScreen(NULL);
+
+   if (g_InputState.dwKeyPress & kKeyMenu)
+   {
+      //
+      // Show the in-game menu
+      //
+      PAL_InGameMenu();
+   }
+   else if (g_InputState.dwKeyPress & kKeyUseItem)
+   {
+      //
+      // Show the use item menu
+      //
+      PAL_GameUseItem();
+   }
+   else if (g_InputState.dwKeyPress & kKeyThrowItem)
+   {
+      //
+      // Show the equipment menu
+      //
+      PAL_GameEquipItem();
+   }
+   else if (g_InputState.dwKeyPress & kKeyForce)
+   {
+      //
+      // Show the magic menu
+      //
+      PAL_InGameMagicMenu();
+   }
+   else if (g_InputState.dwKeyPress & kKeyStatus)
+   {
+      //
+      // Show the player status
+      //
+      PAL_PlayerStatus();
+   }
+   else if (g_InputState.dwKeyPress & kKeySearch)
+   {
+      //
+      // Process search events
+      //
+      PAL_Search();
+   }
+   else if (g_InputState.dwKeyPress & kKeyFlee)
+   {
+      //
+      // Quit Game
+      //
+      if (PAL_ConfirmMenu())
+      {
+         PAL_PlayMUS(0, FALSE, 2);
+         PAL_FadeOut(2);
+         PAL_Shutdown();
+         exit(0);
+      }
+   }
+
+   if (--gpGlobals->wChasespeedChangeCycles == 0)
+   {
+      gpGlobals->wChaseRange = 1;
+   }
+}
+
+VOID
+PAL_WaitForKey(
+   WORD      wTimeOut
+)
+/*++
+  Purpose:
+
+    Wait for any key.
+
+  Parameters:
+
+    [IN]  wTimeOut - the maximum time of the waiting. 0 = wait forever.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   DWORD     dwTimeOut = SDL_GetTicks() + wTimeOut;
+
+   PAL_ClearKeyState();
+
+   while (wTimeOut == 0 || SDL_GetTicks() < dwTimeOut)
+   {
+      UTIL_Delay(5);
+
+      if (g_InputState.dwKeyPress & (kKeySearch | kKeyMenu))
+      {
+         break;
+      }
+   }
+}

+ 58 - 0
play.h

@@ -0,0 +1,58 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef PLAY_H
+#define PLAY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+VOID
+PAL_GameUpdate(
+   BOOL       fTrigger
+);
+
+VOID
+PAL_GameUseItem(
+   VOID
+);
+
+VOID
+PAL_GameEquipItem(
+   VOID
+);
+
+VOID
+PAL_StartFrame(
+   VOID
+);
+
+VOID
+PAL_WaitForKey(
+   WORD      wTimeOut
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 3 - 0
private.c

@@ -0,0 +1,3 @@
+#ifdef CYGWIN
+	SDLPAL_ICON ICON DISCARDABLE "sdlpal.ico"
+#endif

+ 429 - 0
res.c

@@ -0,0 +1,429 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "main.h"
+
+typedef struct tagRESOURCES
+{
+   BYTE             bLoadFlags;
+
+   LPPALMAP         lpMap;                                      // current loaded map
+   LPSPRITE        *lppEventObjectSprites;                      // event object sprites
+   int              nEventObject;                               // number of event objects
+
+   LPSPRITE         rglpPlayerSprite[MAX_PLAYERS_IN_PARTY + 1]; // player sprites
+} RESOURCES, *LPRESOURCES;
+
+static LPRESOURCES gpResources = NULL;
+
+static VOID
+PAL_FreeEventObjectSprites(
+   VOID
+)
+/*++
+  Purpose:
+
+    Free all sprites of event objects on the scene.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   int i;
+
+   if (gpResources->lppEventObjectSprites != NULL)
+   {
+      for (i = 0; i < gpResources->nEventObject; i++)
+      {
+         free(gpResources->lppEventObjectSprites[i]);
+      }
+
+      free(gpResources->lppEventObjectSprites);
+
+      gpResources->lppEventObjectSprites = NULL;
+      gpResources->nEventObject = 0;
+   }
+}
+
+static VOID
+PAL_FreePlayerSprites(
+   VOID
+)
+/*++
+  Purpose:
+
+    Free all player sprites.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   int i;
+
+   for (i = 0; i < MAX_PLAYERS_IN_PARTY + 1; i++)
+   {
+      free(gpResources->rglpPlayerSprite[i]);
+      gpResources->rglpPlayerSprite[i] = NULL;
+   }
+}
+
+VOID
+PAL_InitResources(
+   VOID
+)
+/*++
+  Purpose:
+
+    Initialze the resource manager.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   gpResources = (LPRESOURCES)UTIL_calloc(1, sizeof(RESOURCES));
+}
+
+VOID
+PAL_FreeResources(
+   VOID
+)
+/*++
+  Purpose:
+
+    Free all loaded resources.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   if (gpResources != NULL)
+   {
+      //
+      // Free all loaded sprites
+      //
+      PAL_FreePlayerSprites();
+      PAL_FreeEventObjectSprites();
+
+      //
+      // Free map
+      //
+      PAL_FreeMap(gpResources->lpMap);
+
+      //
+      // Delete the instance
+      //
+      free(gpResources);
+   }
+
+   gpResources = NULL;
+}
+
+VOID
+PAL_SetLoadFlags(
+   BYTE       bFlags
+)
+/*++
+  Purpose:
+
+    Set flags to load resources.
+
+  Parameters:
+
+    [IN]  bFlags - flags to be set.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   if (gpResources == NULL)
+   {
+      return;
+   }
+
+   gpResources->bLoadFlags |= bFlags;
+}
+
+VOID
+PAL_LoadResources(
+   VOID
+)
+/*++
+  Purpose:
+
+    Load the game resources if needed.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   int                i, index, l, n;
+   WORD               wPlayerID, wSpriteNum;
+
+   if (gpResources == NULL || gpResources->bLoadFlags == 0)
+   {
+      return;
+   }
+
+   //
+   // Load scene
+   //
+   if (gpResources->bLoadFlags & kLoadScene)
+   {
+      FILE              *fpMAP, *fpGOP;
+
+      fpMAP = UTIL_OpenRequiredFile("map.mkf");
+      fpGOP = UTIL_OpenRequiredFile("gop.mkf");
+
+      if (gpGlobals->fEnteringScene)
+      {
+         gpGlobals->wScreenWave = 0;
+         gpGlobals->sWaveProgression = 0;
+      }
+
+      //
+      // Free previous loaded scene (sprites and map)
+      //
+      PAL_FreeEventObjectSprites();
+      PAL_FreeMap(gpResources->lpMap);
+
+      //
+      // Load map
+      //
+      i = gpGlobals->wNumScene - 1;
+      gpResources->lpMap = PAL_LoadMap(gpGlobals->g.rgScene[i].wMapNum,
+         fpMAP, fpGOP);
+
+      if (gpResources->lpMap == NULL)
+      {
+         fclose(fpMAP);
+         fclose(fpGOP);
+
+         TerminateOnError("PAL_LoadResources(): Fail to load map #%d (scene #%d) !",
+            gpGlobals->g.rgScene[i].wMapNum, gpGlobals->wNumScene);
+      }
+
+      //
+      // Load sprites
+      //
+      index = gpGlobals->g.rgScene[i].wEventObjectIndex;
+      gpResources->nEventObject = gpGlobals->g.rgScene[i + 1].wEventObjectIndex;
+      gpResources->nEventObject -= index;
+
+      if (gpResources->nEventObject > 0)
+      {
+         gpResources->lppEventObjectSprites =
+            (LPSPRITE *)UTIL_calloc(gpResources->nEventObject, sizeof(LPSPRITE));
+      }
+
+      for (i = 0; i < gpResources->nEventObject; i++, index++)
+      {
+         n = gpGlobals->g.lprgEventObject[index].wSpriteNum;
+         if (n == 0)
+         {
+            //
+            // this event object has no sprite
+            //
+            gpResources->lppEventObjectSprites[i] = NULL;
+            continue;
+         }
+
+         l = PAL_MKFGetDecompressedSize(n, gpGlobals->f.fpMGO);
+
+         gpResources->lppEventObjectSprites[i] = (LPSPRITE)UTIL_malloc(l);
+
+         if (PAL_MKFDecompressChunk(gpResources->lppEventObjectSprites[i], l,
+            n, gpGlobals->f.fpMGO) > 0)
+         {
+            gpGlobals->g.lprgEventObject[index].nSpriteFramesAuto =
+               PAL_SpriteGetNumFrames(gpResources->lppEventObjectSprites[i]);
+         }
+      }
+
+      gpGlobals->partyoffset = PAL_XY(160, 112);
+
+      fclose(fpGOP);
+      fclose(fpMAP);
+   }
+
+   //
+   // Load player sprites
+   //
+   if (gpResources->bLoadFlags & kLoadPlayerSprite)
+   {
+      //
+      // Free previous loaded player sprites
+      //
+      PAL_FreePlayerSprites();
+
+      for (i = 0; i <= (short)gpGlobals->wMaxPartyMemberIndex; i++)
+      {
+         wPlayerID = gpGlobals->rgParty[i].wPlayerRole;
+         assert(wPlayerID < MAX_PLAYER_ROLES);
+
+         //
+         // Load player sprite
+         //
+         wSpriteNum = gpGlobals->g.PlayerRoles.rgwSpriteNum[wPlayerID];
+
+         l = PAL_MKFGetDecompressedSize(wSpriteNum, gpGlobals->f.fpMGO);
+
+         gpResources->rglpPlayerSprite[i] = (LPSPRITE)UTIL_malloc(l);
+
+         PAL_MKFDecompressChunk(gpResources->rglpPlayerSprite[i], l, wSpriteNum,
+            gpGlobals->f.fpMGO);
+      }
+
+      if (gpGlobals->nFollower > 0)
+      {
+         //
+         // Load the follower sprite
+         //
+         wSpriteNum = gpGlobals->rgParty[i].wPlayerRole;
+
+         l = PAL_MKFGetDecompressedSize(wSpriteNum, gpGlobals->f.fpMGO);
+
+         gpResources->rglpPlayerSprite[i] = (LPSPRITE)UTIL_malloc(l);
+
+         PAL_MKFDecompressChunk(gpResources->rglpPlayerSprite[i], l, wSpriteNum,
+            gpGlobals->f.fpMGO);
+      }
+   }
+
+   //
+   // Clear all of the load flags
+   //
+   gpResources->bLoadFlags = 0;
+}
+
+LPPALMAP
+PAL_GetCurrentMap(
+   VOID
+)
+/*++
+  Purpose:
+
+    Get the current loaded map.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    Pointer to the current loaded map. NULL if no map is loaded.
+
+--*/
+{
+   if (gpResources == NULL)
+   {
+      return NULL;
+   }
+
+   return gpResources->lpMap;
+}
+
+LPSPRITE
+PAL_GetPlayerSprite(
+   BYTE      bPlayerIndex
+)
+/*++
+  Purpose:
+
+    Get the player sprite.
+
+  Parameters:
+
+    [IN]  bPlayerIndex - index of player in party (starts from 0).
+
+  Return value:
+
+    Pointer to the player sprite.
+
+--*/
+{
+   if (gpResources == NULL || bPlayerIndex > MAX_PLAYERS_IN_PARTY)
+   {
+      return NULL;
+   }
+
+   return gpResources->rglpPlayerSprite[bPlayerIndex];
+}
+
+LPSPRITE
+PAL_GetEventObjectSprite(
+   WORD      wEventObjectID
+)
+/*++
+  Purpose:
+
+    Get the sprite of the specified event object.
+
+  Parameters:
+
+    [IN]  wEventObjectID - the ID of event object.
+
+  Return value:
+
+    Pointer to the sprite.
+
+--*/
+{
+   wEventObjectID -= gpGlobals->g.rgScene[gpGlobals->wNumScene - 1].wEventObjectIndex;
+   wEventObjectID--;
+
+   if (gpResources == NULL || wEventObjectID >= gpResources->nEventObject)
+   {
+      return NULL;
+   }
+
+   return gpResources->lppEventObjectSprites[wEventObjectID];
+}

+ 79 - 0
res.h

@@ -0,0 +1,79 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef RES_H
+#define RES_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef enum tagLOADRESFLAG
+{
+   kLoadScene          = (1 << 0),    // load a scene
+   kLoadPlayerSprite   = (1 << 1),    // load player sprites
+} LOADRESFLAG, *LPLOADRESFLAG;
+
+VOID
+PAL_InitResources(
+   VOID
+);
+
+VOID
+PAL_FreeResources(
+   VOID
+);
+
+VOID
+PAL_SetLoadFlags(
+   BYTE       bFlags
+);
+
+VOID
+PAL_LoadResources(
+   VOID
+);
+
+LPPALMAP
+PAL_GetCurrentMap(
+   VOID
+);
+
+LPSPRITE
+PAL_GetPlayerSprite(
+   BYTE      bPlayerIndex
+);
+
+LPSPRITE
+PAL_GetBattleSprite(
+   BYTE      bPlayerIndex
+);
+
+LPSPRITE
+PAL_GetEventObjectSprite(
+   WORD      wEventObjectID
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 383 - 0
rixplay.cpp

@@ -0,0 +1,383 @@
+//
+// Copyright (c) 2008, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "rixplay.h"
+
+#define USE_SURROUNDOPL      0
+#define USE_DEMUOPL          0
+
+#include "adplug/opl.h"
+#include "adplug/emuopl.h"
+#include "adplug/demuopl.h"
+#include "adplug/surroundopl.h"
+#include "adplug/rix.h"
+
+extern "C" BOOL g_fNoMusic;
+extern "C" INT  g_iVolume;
+
+#include "sound.h"
+
+typedef struct tagRIXPLAYER
+{
+   tagRIXPLAYER() : iCurrentMusic(-1) {}
+   Copl                      *opl;
+   CrixPlayer                *rix;
+   INT                        iCurrentMusic; // current playing music number
+   INT                        iNextMusic; // the next music number to switch to
+   DWORD                      dwStartFadeTime;
+   DWORD                      dwEndFadeTime;
+   enum { FADE_IN, FADE_OUT } FadeType; // fade in or fade out ?
+   BOOL                       fLoop;
+   BOOL                       fNextLoop;
+   BYTE                       buf[PAL_SAMPLE_RATE / 70 * 2 * PAL_CHANNELS];
+   LPBYTE                     pos;
+} RIXPLAYER, *LPRIXPLAYER;
+
+static LPRIXPLAYER gpRixPlayer = NULL;
+
+VOID
+RIX_FillBuffer(
+   LPBYTE     stream,
+   INT        len
+)
+/*++
+  Purpose:
+
+    Fill the background music into the sound buffer. Called by the SDL sound
+    callback function only (sound.c: SOUND_FillAudio).
+
+  Parameters:
+
+    [OUT] stream - pointer to the stream buffer.
+
+    [IN]  len - Length of the buffer.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   INT       i, l, oldlen, volume = SDL_MIX_MAXVOLUME / 2;
+   UINT      t = SDL_GetTicks();
+
+#ifdef __SYMBIAN32__
+   volume = g_iVolume / 2;
+#endif
+
+   oldlen = len;
+
+   if (gpRixPlayer == NULL)
+   {
+      //
+      // Not initialized
+      //
+      return;
+   }
+
+   //
+   // fading in or fading out
+   //
+   if (gpRixPlayer->dwEndFadeTime > 0)
+   {
+      switch (gpRixPlayer->FadeType)
+      {
+      case RIXPLAYER::FADE_IN:
+         if (t >= gpRixPlayer->dwEndFadeTime)
+         {
+            gpRixPlayer->dwEndFadeTime = 0;
+         }
+         else
+         {
+            volume = (INT)(volume * (t - gpRixPlayer->dwStartFadeTime) /
+               (FLOAT)(gpRixPlayer->dwEndFadeTime - gpRixPlayer->dwStartFadeTime));
+         }
+         break;
+
+      case RIXPLAYER::FADE_OUT:
+         if (gpRixPlayer->iCurrentMusic == -1)
+         {
+            //
+            // There is no current playing music. Just start playing the next one.
+            //
+            gpRixPlayer->iCurrentMusic = gpRixPlayer->iNextMusic;
+            gpRixPlayer->fLoop = gpRixPlayer->fNextLoop;
+            gpRixPlayer->FadeType = RIXPLAYER::FADE_IN;
+            gpRixPlayer->dwEndFadeTime = t +
+               (gpRixPlayer->dwEndFadeTime - gpRixPlayer->dwStartFadeTime);
+            gpRixPlayer->dwStartFadeTime = t;
+            gpRixPlayer->rix->rewind(gpRixPlayer->iCurrentMusic);
+            return;
+         }
+         else if (t >= gpRixPlayer->dwEndFadeTime)
+         {
+            if (gpRixPlayer->iNextMusic <= 0)
+            {
+               gpRixPlayer->iCurrentMusic = -1;
+               gpRixPlayer->dwEndFadeTime = 0;
+            }
+            else
+            {
+               //
+               // Fade to the next music
+               //
+               gpRixPlayer->iCurrentMusic = gpRixPlayer->iNextMusic;
+               gpRixPlayer->fLoop = gpRixPlayer->fNextLoop;
+               gpRixPlayer->FadeType = RIXPLAYER::FADE_IN;
+               gpRixPlayer->dwEndFadeTime = t +
+                  (gpRixPlayer->dwEndFadeTime - gpRixPlayer->dwStartFadeTime);
+               gpRixPlayer->dwStartFadeTime = t;
+               gpRixPlayer->rix->rewind(gpRixPlayer->iCurrentMusic);
+            }
+            return;
+         }
+         volume = (INT)(volume * (1.0f - (t - gpRixPlayer->dwStartFadeTime) /
+            (FLOAT)(gpRixPlayer->dwEndFadeTime - gpRixPlayer->dwStartFadeTime)));
+         break;
+      }
+   }
+
+   if (gpRixPlayer->iCurrentMusic <= 0)
+   {
+      //
+      // No current playing music
+      //
+      return;
+   }
+
+   //
+   // Fill the buffer with sound data
+   //
+   while (len > 0)
+   {
+      if (gpRixPlayer->pos == NULL ||
+         gpRixPlayer->pos - gpRixPlayer->buf >= (int)sizeof(gpRixPlayer->buf))
+      {
+         gpRixPlayer->pos = gpRixPlayer->buf;
+         if (!gpRixPlayer->rix->update())
+         {
+            if (!gpRixPlayer->fLoop)
+            {
+               //
+               // Not loop, simply terminate the music
+               //
+               gpRixPlayer->iCurrentMusic = -1;
+               return;
+            }
+            gpRixPlayer->rix->rewind(gpRixPlayer->iCurrentMusic);
+            if (!gpRixPlayer->rix->update())
+            {
+               //
+               // Something must be wrong
+               //
+               gpRixPlayer->iCurrentMusic = -1;
+               return;
+            }
+         }
+         gpRixPlayer->opl->update((short *)(gpRixPlayer->buf), PAL_SAMPLE_RATE / 70);
+      }
+
+      l = sizeof(gpRixPlayer->buf) - (gpRixPlayer->pos - gpRixPlayer->buf);
+      if (len < l)
+      {
+         l = len;
+      }
+
+      //
+      // Put audio data into buffer and adjust volume
+      // WARNING: for signed 16-bit little-endian only
+      //
+      for (i = 0; i < (int)(l / sizeof(SHORT)); i++)
+      {
+         SHORT s = SWAP16((int)(*(SHORT *)(gpRixPlayer->pos)) * volume / SDL_MIX_MAXVOLUME);
+
+#if !USE_SURROUNDOPL
+         for (int j = 0; j < PAL_CHANNELS; j++)
+#endif
+         {
+            *(SHORT *)(stream) = s;
+            stream += sizeof(SHORT);
+         }
+
+         gpRixPlayer->pos += sizeof(SHORT);
+      }
+
+      len -= l;
+   }
+
+   stream -= oldlen;
+}
+
+INT
+RIX_Init(
+   LPCSTR     szFileName
+)
+/*++
+  Purpose:
+
+    Initialize the RIX player subsystem.
+
+  Parameters:
+
+    [IN]  szFileName - Filename of the mus.mkf file.
+
+  Return value:
+
+    0 if success, -1 if cannot allocate memory, -2 if file not found.
+
+--*/
+{
+   gpRixPlayer = new RIXPLAYER;
+   if (gpRixPlayer == NULL)
+   {
+      return -1;
+   }
+#if USE_SURROUNDOPL && (PAL_CHANNELS == 2)
+#	if USE_DEMUOPL
+   gpRixPlayer->opl = new CSurroundopl(new CDemuopl(PAL_SAMPLE_RATE, true, false),
+      new CDemuopl(PAL_SAMPLE_RATE, true, false), true);
+#	else
+   gpRixPlayer->opl = new CSurroundopl(new CEmuopl(PAL_SAMPLE_RATE, true, false),
+      new CEmuopl(PAL_SAMPLE_RATE, true, false), true);
+#	endif
+#else
+#	if USE_DEMUOPL
+   gpRixPlayer->opl = new CDemuopl(PAL_SAMPLE_RATE, true, false);
+#	else
+   gpRixPlayer->opl = new CEmuopl(PAL_SAMPLE_RATE, true, false);
+#	endif
+#endif
+
+   if (gpRixPlayer->opl == NULL)
+   {
+      delete gpRixPlayer;
+      return -1;
+   }
+
+   gpRixPlayer->rix = new CrixPlayer(gpRixPlayer->opl);
+   if (gpRixPlayer->rix == NULL)
+   {
+      delete gpRixPlayer->opl;
+      delete gpRixPlayer;
+      return -1;
+   }
+
+   //
+   // Load the MKF file.
+   //
+   if (!gpRixPlayer->rix->load(szFileName, CProvider_Filesystem()))
+   {
+      delete gpRixPlayer->rix;
+      delete gpRixPlayer->opl;
+      delete gpRixPlayer;
+      gpRixPlayer = NULL;
+      return -2;
+   }
+
+   //
+   // Success.
+   //
+   gpRixPlayer->iCurrentMusic = -1;
+   gpRixPlayer->dwEndFadeTime = 0;
+   gpRixPlayer->pos = NULL;
+   gpRixPlayer->fLoop = FALSE;
+   gpRixPlayer->fNextLoop = FALSE;
+
+   return 0;
+}
+
+VOID
+RIX_Shutdown(
+   VOID
+)
+/*++
+  Purpose:
+
+    Shutdown the RIX player subsystem.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   if (gpRixPlayer != NULL)
+   {
+      delete gpRixPlayer->rix;
+      delete gpRixPlayer->opl;
+      delete gpRixPlayer;
+
+      gpRixPlayer = NULL;
+   }
+}
+
+VOID
+RIX_Play(
+   INT       iNumRIX,
+   BOOL      fLoop,
+   FLOAT     flFadeTime
+)
+/*++
+  Purpose:
+
+    Start playing the specified music.
+
+  Parameters:
+
+    [IN]  iNumRIX - number of the music. 0 to stop playing current music.
+
+    [IN]  fLoop - Whether the music should be looped or not.
+
+    [IN]  flFadeTime - the fade in/out time when switching music.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   //
+   // Check for NULL pointer.
+   //
+   if (gpRixPlayer == NULL)
+   {
+      return;
+   }
+
+   //
+   // Stop the current CD music.
+   //
+   SOUND_PlayCDA(-1);
+
+   DWORD t = SDL_GetTicks();
+   gpRixPlayer->fNextLoop = fLoop;
+
+   if (iNumRIX == gpRixPlayer->iCurrentMusic && !g_fNoMusic)
+   {
+      return;
+   }
+
+   gpRixPlayer->iNextMusic = iNumRIX;
+   gpRixPlayer->dwStartFadeTime = t;
+   gpRixPlayer->dwEndFadeTime = t + (DWORD)(flFadeTime * 1000) / 2;
+   gpRixPlayer->FadeType = RIXPLAYER::FADE_OUT;
+}

+ 59 - 0
rixplay.h

@@ -0,0 +1,59 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef RIX_PLAY_H
+#define RIX_PLAY_H
+
+#include "common.h"
+
+#ifdef __cplusplus
+
+extern "C"
+{
+#endif
+
+VOID
+RIX_FillBuffer(
+   LPBYTE     stream,
+   INT        len
+);
+
+INT
+RIX_Init(
+   LPCSTR     szFileName
+);
+
+VOID
+RIX_Shutdown(
+   VOID
+);
+
+VOID
+RIX_Play(
+   INT       iNumRIX,
+   BOOL      fLoop,
+   FLOAT     flFadeTime
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 481 - 0
rngplay.c

@@ -0,0 +1,481 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// Portions based on PalLibrary by Lou Yihua <louyihua@21cn.com>.
+// Copyright (c) 2006-2007, Lou Yihua.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "main.h"
+
+static INT
+PAL_RNGReadFrame(
+   LPBYTE          lpBuffer,
+   UINT            uiBufferSize,
+   UINT            uiRngNum,
+   UINT            uiFrameNum,
+   FILE           *fpRngMKF
+)
+/*++
+  Purpose:
+
+    Read a frame from a RNG animation.
+
+  Parameters:
+
+    [OUT] lpBuffer - pointer to the destination buffer.
+
+    [IN]  uiBufferSize - size of the destination buffer.
+
+    [IN]  uiRngNum - the number of the RNG animation in the MKF archive.
+
+    [IN]  uiFrameNum - frame number in the RNG animation.
+
+    [IN]  fpRngMKF - pointer to the fopen'ed MKF file.
+
+  Return value:
+
+    Integer value which indicates the size of the chunk.
+    -1 if there are error in parameters.
+    -2 if buffer size is not enough.
+
+--*/
+{
+   UINT         uiOffset       = 0;
+   UINT         uiSubOffset    = 0;
+   UINT         uiNextOffset   = 0;
+   UINT         uiChunkCount   = 0;
+   INT          iChunkLen      = 0;
+
+   if (lpBuffer == NULL || fpRngMKF == NULL || uiBufferSize == 0)
+   {
+      return -1;
+   }
+
+   //
+   // Get the total number of chunks.
+   //
+   uiChunkCount = PAL_MKFGetChunkCount(fpRngMKF);
+   if (uiRngNum >= uiChunkCount)
+   {
+      return -1;
+   }
+
+   //
+   // Get the offset of the chunk.
+   //
+   fseek(fpRngMKF, 4 * uiRngNum, SEEK_SET);
+   fread(&uiOffset, sizeof(UINT), 1, fpRngMKF);
+   fread(&uiNextOffset, sizeof(UINT), 1, fpRngMKF);
+   uiOffset = SWAP32(uiOffset);
+   uiNextOffset = SWAP32(uiNextOffset);
+
+   //
+   // Get the length of the chunk.
+   //
+   iChunkLen = uiNextOffset - uiOffset;
+   if (iChunkLen != 0)
+   {
+      fseek(fpRngMKF, uiOffset, SEEK_SET);
+   }
+   else
+   {
+      return -1;
+   }
+
+   //
+   // Get the number of sub chunks.
+   //
+   fread(&uiChunkCount, sizeof(UINT), 1, fpRngMKF);
+   uiChunkCount = (SWAP32(uiChunkCount) - 4) / 4;
+   if (uiFrameNum >= uiChunkCount)
+   {
+      return -1;
+   }
+
+   //
+   // Get the offset of the sub chunk.
+   //
+   fseek(fpRngMKF, uiOffset + 4 * uiFrameNum, SEEK_SET);
+   fread(&uiSubOffset, sizeof(UINT), 1, fpRngMKF);
+   fread(&uiNextOffset, sizeof(UINT), 1, fpRngMKF);
+   uiSubOffset = SWAP32(uiSubOffset);
+   uiNextOffset = SWAP32(uiNextOffset);
+
+   //
+   // Get the length of the sub chunk.
+   //
+   iChunkLen = uiNextOffset - uiSubOffset;
+   if ((UINT)iChunkLen > uiBufferSize)
+   {
+      return -2;
+   }
+
+   if (iChunkLen != 0)
+   {
+      fseek(fpRngMKF, uiOffset + uiSubOffset, SEEK_SET);
+      fread(lpBuffer, iChunkLen, 1, fpRngMKF);
+   }
+   else
+   {
+      return -1;
+   }
+
+   return iChunkLen;
+}
+
+static INT
+PAL_RNGBlitToSurface(
+   INT                      iNumRNG,
+   INT                      iNumFrame,
+   SDL_Surface             *lpDstSurface,
+   FILE                    *fpRngMKF
+)
+/*++
+  Purpose:
+
+    Blit one frame in an RNG animation to an SDL surface.
+    The surface should contain the last frame of the RNG, or blank if it's the first
+    frame.
+
+    NOTE: Assume the surface is already locked, and the surface is a 320x200 8-bit one.
+
+  Parameters:
+
+    [IN]  iNumRNG - The number of the animation in the MKF archive.
+
+    [IN]  iNumFrame - The number of the frame in the animation.
+
+    [OUT] lpDstSurface - pointer to the destination SDL surface.
+
+    [IN]  fpRngMKF - Pointer to the fopen'ed rng.mkf file.
+
+  Return value:
+
+    0 = success, -1 = error.
+
+--*/
+{
+   INT                   ptr         = 0;
+   INT                   dst_ptr     = 0;
+   BYTE                  data        = 0;
+   WORD                  wdata       = 0;
+   INT                   x, y, i, n;
+   LPBYTE                rng         = NULL;
+   LPBYTE                buf         = NULL;
+
+   //
+   // Check for invalid parameters.
+   //
+   if (lpDstSurface == NULL || iNumRNG < 0 || iNumFrame < 0)
+   {
+      return -1;
+   }
+
+   buf = (LPBYTE)calloc(1, 65000);
+   if (buf == NULL)
+   {
+      return -1;
+   }
+
+   //
+   // Read the frame.
+   //
+   if (PAL_RNGReadFrame(buf, 65000, iNumRNG, iNumFrame, fpRngMKF) < 0)
+   {
+      free(buf);
+      return -1;
+   }
+
+   //
+   // Decompress the frame.
+   //
+   rng = (LPBYTE)calloc(1, 65000);
+   if (rng == NULL)
+   {
+      free(buf);
+      return -1;
+   }
+   Decompress(buf, rng, 65000);
+   free(buf);
+
+   //
+   // Draw the frame to the surface.
+   // FIXME: Dirty and ineffective code, needs to be cleaned up
+   //
+   while (TRUE)
+   {
+      data = rng[ptr++];
+      switch (data)
+      {
+      case 0x00:
+      case 0x13:
+         //
+         // End
+         //
+         goto end;
+
+      case 0x02:
+         dst_ptr += 2;
+         break;
+
+      case 0x03:
+         data = rng[ptr++];
+         dst_ptr += (data + 1) * 2;
+         break;
+
+      case 0x04:
+         wdata = rng[ptr] | (rng[ptr + 1] << 8);
+         ptr += 2;
+         dst_ptr += ((unsigned int)wdata + 1) * 2;
+         break;
+
+      case 0x0a:
+         x = dst_ptr % 320;
+         y = dst_ptr / 320;
+         ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr++];
+         if (++x >= 320)
+         {
+            x = 0;
+            ++y;
+         }
+         ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr++];
+         dst_ptr += 2;
+
+      case 0x09:
+         x = dst_ptr % 320;
+         y = dst_ptr / 320;
+         ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr++];
+         if (++x >= 320)
+         {
+            x = 0;
+            ++y;
+         }
+         ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr++];
+         dst_ptr += 2;
+
+      case 0x08:
+         x = dst_ptr % 320;
+         y = dst_ptr / 320;
+         ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr++];
+         if (++x >= 320)
+         {
+            x = 0;
+            ++y;
+         }
+         ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr++];
+         dst_ptr += 2;
+
+      case 0x07:
+         x = dst_ptr % 320;
+         y = dst_ptr / 320;
+         ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr++];
+         if (++x >= 320)
+         {
+            x = 0;
+            ++y;
+         }
+         ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr++];
+         dst_ptr += 2;
+
+      case 0x06:
+         x = dst_ptr % 320;
+         y = dst_ptr / 320;
+         ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr++];
+         if (++x >= 320)
+         {
+            x = 0;
+            ++y;
+         }
+         ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr++];
+         dst_ptr += 2;
+         break;
+
+      case 0x0b:
+         data = *(rng + ptr++);
+         for (i = 0; i <= data; i++)
+         {
+            x = dst_ptr % 320;
+            y = dst_ptr / 320;
+            ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr++];
+            if (++x >= 320)
+            {
+               x = 0;
+               ++y;
+            }
+            ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr++];
+            dst_ptr += 2;
+         }
+         break;
+
+      case 0x0c:
+         wdata = rng[ptr] | (rng[ptr + 1] << 8);
+         ptr += 2;
+         for (i = 0; i <= wdata; i++)
+         {
+            x = dst_ptr % 320;
+            y = dst_ptr / 320;
+            ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr++];
+            if (++x >= 320)
+            {
+               x = 0;
+               ++y;
+            }
+            ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr++];
+            dst_ptr += 2;
+         }
+         break;
+
+      case 0x0d:
+      case 0x0e:
+      case 0x0f:
+      case 0x10:
+         for (i = 0; i < data - (0x0d - 2); i++)
+         {
+            x = dst_ptr % 320;
+            y = dst_ptr / 320;
+            ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr];
+            if (++x >= 320)
+            {
+               x = 0;
+               ++y;
+            }
+            ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr + 1];
+            dst_ptr += 2;
+         }
+         ptr += 2;
+         break;
+
+      case 0x11:
+    	 data = *(rng + ptr++);
+         for (i = 0; i <= data; i++)
+         {
+            x = dst_ptr % 320;
+            y = dst_ptr / 320;
+            ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr];
+            if (++x >= 320)
+            {
+               x = 0;
+               ++y;
+            }
+            ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr + 1];
+            dst_ptr += 2;
+         }
+         ptr += 2;
+         break;
+
+      case 0x12:
+         n = (rng[ptr] | (rng[ptr + 1] << 8)) + 1;
+         ptr += 2;
+         for (i = 0; i < n; i++)
+         {
+            x = dst_ptr % 320;
+            y = dst_ptr / 320;
+            ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr];
+            if (++x >= 320)
+            {
+               x = 0;
+               ++y;
+            }
+            ((LPBYTE)(lpDstSurface->pixels))[y * lpDstSurface->pitch + x] = rng[ptr + 1];
+            dst_ptr += 2;
+         }
+         ptr += 2;
+         break;
+      }
+   }
+
+end:
+   free(rng);
+   return 0;
+}
+
+VOID
+PAL_RNGPlay(
+   INT           iNumRNG,
+   INT           iStartFrame,
+   INT           iEndFrame,
+   INT           iSpeed
+)
+/*++
+  Purpose:
+
+    Play a RNG movie.
+
+  Parameters:
+
+    [IN]  iNumRNG - number of the RNG movie.
+
+    [IN]  iStartFrame - start frame number.
+
+    [IN]  iEndFrame - end frame number.
+
+    [IN]  iSpeed - speed of playing.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   UINT            iTime;
+   int             iDelay = 800 / (iSpeed == 0 ? 16 : iSpeed);
+   FILE           *fp;
+
+   fp = UTIL_OpenRequiredFile("rng.mkf");
+
+   for (; iStartFrame <= iEndFrame; iStartFrame++)
+   {
+      iTime = SDL_GetTicks() + iDelay;
+
+      if (PAL_RNGBlitToSurface(iNumRNG, iStartFrame, gpScreen, fp) == -1)
+      {
+         //
+         // Failed to get the frame, don't go further
+         //
+         fclose(fp);
+         return;
+      }
+
+      //
+      // Update the screen
+      //
+      VIDEO_UpdateScreen(NULL);
+
+      //
+      // Fade in the screen if needed
+      //
+      if (gpGlobals->fNeedToFadeIn)
+      {
+         PAL_FadeIn(gpGlobals->wNumPalette, gpGlobals->fNightPalette, 1);
+         gpGlobals->fNeedToFadeIn = FALSE;
+      }
+
+      //
+      // Delay for a while
+      //
+      PAL_ProcessEvent();
+      while (SDL_GetTicks() <= iTime)
+      {
+         PAL_ProcessEvent();
+         SDL_Delay(1);
+      }
+   }
+
+   fclose(fp);
+}

+ 43 - 0
rngplay.h

@@ -0,0 +1,43 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef RNGPLAY_H
+#define RNGPLAY_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "common.h"
+
+VOID
+PAL_RNGPlay(
+   INT           iNumRNG,
+   INT           iStartFrame,
+   INT           iNumFrames,
+   INT           iSpeed
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 877 - 0
scene.c

@@ -0,0 +1,877 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// Portions based on PALx Project by palxex.
+// Copyright (c) 2006, Pal Lockheart <palxex@gmail.com>.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "main.h"
+
+#define MAX_SPRITE_TO_DRAW         2048
+
+typedef struct tagSPRITE_TO_DRAW
+{
+   LPCBITMAPRLE     lpSpriteFrame; // pointer to the frame bitmap
+   PAL_POS          pos;           // position on the scene
+   int              iLayer;        // logical layer
+} SPRITE_TO_DRAW;
+
+static SPRITE_TO_DRAW    g_rgSpriteToDraw[MAX_SPRITE_TO_DRAW];
+static int               g_nSpriteToDraw;
+
+static VOID
+PAL_AddSpriteToDraw(
+   LPCBITMAPRLE     lpSpriteFrame,
+   int              x,
+   int              y,
+   int              iLayer
+)
+/*++
+   Purpose:
+
+     Add a sprite to our list of drawing.
+
+   Parameters:
+
+     [IN]  lpSpriteFrame - the bitmap of the sprite frame.
+
+     [IN]  x - the X coordinate on the screen.
+
+     [IN]  y - the Y coordinate on the screen.
+
+     [IN]  iLayer - the layer of the sprite.
+
+   Return value:
+
+     None.
+
+--*/
+{
+   assert(g_nSpriteToDraw < MAX_SPRITE_TO_DRAW);
+
+   g_rgSpriteToDraw[g_nSpriteToDraw].lpSpriteFrame = lpSpriteFrame;
+   g_rgSpriteToDraw[g_nSpriteToDraw].pos = PAL_XY(x, y);
+   g_rgSpriteToDraw[g_nSpriteToDraw].iLayer = iLayer;
+
+   g_nSpriteToDraw++;
+}
+
+static VOID
+PAL_CalcCoverTiles(
+   SPRITE_TO_DRAW         *lpSpriteToDraw
+)
+/*++
+   Purpose:
+
+     Calculate all the tiles which may cover the specified sprite. Add the tiles
+     into our list as well.
+
+   Parameters:
+
+     [IN]  lpSpriteToDraw - pointer to SPRITE_TO_DRAW struct.
+
+   Return value:
+
+     None.
+
+--*/
+{
+   int             x, y, i, l, iTileHeight;
+   LPCBITMAPRLE    lpTile;
+
+   const int       sx = PAL_X(gpGlobals->viewport) + PAL_X(lpSpriteToDraw->pos);
+   const int       sy = PAL_Y(gpGlobals->viewport) + PAL_Y(lpSpriteToDraw->pos);
+   const int       sh = ((sx % 32) ? 1 : 0);
+
+   const int       width = PAL_RLEGetWidth(lpSpriteToDraw->lpSpriteFrame);
+   const int       height = PAL_RLEGetHeight(lpSpriteToDraw->lpSpriteFrame);
+
+   int             dx = 0;
+   int             dy = 0;
+   int             dh = 0;
+
+   //
+   // Loop through all the tiles in the area of the sprite.
+   //
+   for (y = (sy - height - 15) / 16; y <= sy / 16; y++)
+   {
+      for (x = (sx - width / 2) / 32; x <= (sx + width / 2) / 32; x++)
+      {
+         for (i = ((x == (sx - width / 2) / 32) ? 0 : 3); i < 5; i++)
+         {
+            //
+            // Scan tiles in the following form (* = to scan):
+            //
+            // . . . * * * . . .
+            //  . . . * * . . . .
+            //
+            switch (i)
+            {
+               case 0:
+                  dx = x;
+                  dy = y;
+                  dh = sh;
+                  break;
+
+               case 1:
+                  dx = x - 1;
+                  break;
+
+               case 2:
+                  dx = (sh ? x : (x - 1));
+                  dy = (sh ? (y + 1) : y);
+                  dh = 1 - sh;
+                  break;
+
+               case 3:
+                  dx = x + 1;
+                  dy = y;
+                  dh = sh;
+                  break;
+
+               case 4:
+                  dx = (sh ? (x + 1) : x);
+                  dy = (sh ? (y + 1) : y);
+                  dh = 1 - sh;
+                  break;
+            }
+
+            for (l = 0; l < 2; l++)
+            {
+               lpTile = PAL_MapGetTileBitmap(dx, dy, dh, l, PAL_GetCurrentMap());
+               iTileHeight = (signed char)PAL_MapGetTileHeight(dx, dy, dh, l, PAL_GetCurrentMap());
+
+               //
+               // Check if this tile may cover the sprites
+               //
+               if (lpTile != NULL && iTileHeight > 0 && (dy + iTileHeight) * 16 + dh * 8 >= sy)
+               {
+                  //
+                  // This tile may cover the sprite
+                  //
+                  PAL_AddSpriteToDraw(lpTile,
+                     dx * 32 + dh * 16 - 16 - PAL_X(gpGlobals->viewport),
+                     dy * 16 + dh * 8 + 7 + l + iTileHeight * 8 - PAL_Y(gpGlobals->viewport),
+                     iTileHeight * 8 + l);
+               }
+            }
+         }
+      }
+   }
+}
+
+static VOID
+PAL_SceneDrawSprites(
+   VOID
+)
+/*++
+   Purpose:
+
+     Draw all the sprites to scene.
+
+   Parameters:
+
+     None.
+
+   Return value:
+
+     None.
+
+--*/
+{
+   int i, x, y, vy;
+
+   g_nSpriteToDraw = 0;
+
+   //
+   // Put all the sprites to be drawn into our array.
+   //
+
+   //
+   // Players
+   //
+   for (i = 0; i <= (short)gpGlobals->wMaxPartyMemberIndex + gpGlobals->nFollower; i++)
+   {
+      LPCBITMAPRLE lpBitmap =
+         PAL_SpriteGetFrame(PAL_GetPlayerSprite((BYTE)i), gpGlobals->rgParty[i].wFrame);
+
+      if (lpBitmap == NULL)
+      {
+         continue;
+      }
+
+      //
+      // Add it to our array
+      //
+      PAL_AddSpriteToDraw(lpBitmap,
+         gpGlobals->rgParty[i].x - PAL_RLEGetWidth(lpBitmap) / 2,
+         gpGlobals->rgParty[i].y + gpGlobals->wLayer + 10,
+         gpGlobals->wLayer + 6);
+
+      //
+      // Calculate covering tiles on the map
+      //
+      PAL_CalcCoverTiles(&g_rgSpriteToDraw[g_nSpriteToDraw - 1]);
+   }
+
+   //
+   // Event Objects (Monsters/NPCs/others)
+   //
+   for (i = gpGlobals->g.rgScene[gpGlobals->wNumScene - 1].wEventObjectIndex;
+      i < gpGlobals->g.rgScene[gpGlobals->wNumScene].wEventObjectIndex; i++)
+   {
+      LPCBITMAPRLE     lpFrame;
+      LPCSPRITE        lpSprite;
+
+      LPEVENTOBJECT    lpEvtObj = &(gpGlobals->g.lprgEventObject[i]);
+
+      int              iFrame;
+
+      if (lpEvtObj->sState == kObjStateHidden || lpEvtObj->sVanishTime > 0 ||
+         lpEvtObj->sState < 0)
+      {
+         continue;
+      }
+
+      //
+      // Get the sprite
+      //
+      lpSprite = PAL_GetEventObjectSprite((WORD)i + 1);
+      if (lpSprite == NULL)
+      {
+         continue;
+      }
+
+      iFrame = lpEvtObj->wCurrentFrameNum;
+      if (lpEvtObj->nSpriteFrames == 3)
+      {
+         //
+         // walking character
+         //
+         if (iFrame == 2)
+         {
+            iFrame = 0;
+         }
+
+         if (iFrame == 3)
+         {
+            iFrame = 2;
+         }
+      }
+
+      lpFrame = PAL_SpriteGetFrame(lpSprite,
+         lpEvtObj->wDirection * lpEvtObj->nSpriteFrames + iFrame);
+
+      if (lpFrame == NULL)
+      {
+         continue;
+      }
+
+      //
+      // Calculate the coordinate and check if outside the screen
+      //
+      x = (SHORT)lpEvtObj->x - PAL_X(gpGlobals->viewport);
+      x -= PAL_RLEGetWidth(lpFrame) / 2;
+
+      if (x >= 320 || x < -(int)PAL_RLEGetWidth(lpFrame))
+      {
+         //
+         // outside the screen; skip it
+         //
+         continue;
+      }
+
+      y = (SHORT)lpEvtObj->y - PAL_Y(gpGlobals->viewport);
+      y += lpEvtObj->sLayer * 8 + 9;
+
+      vy = y - PAL_RLEGetHeight(lpFrame) - lpEvtObj->sLayer * 8 + 2;
+      if (vy >= 200 || vy < -(int)PAL_RLEGetHeight(lpFrame))
+      {
+         //
+         // outside the screen; skip it
+         //
+         continue;
+      }
+
+      //
+      // Add it into the array
+      //
+      PAL_AddSpriteToDraw(lpFrame, x, y, lpEvtObj->sLayer * 8 + 2);
+
+      //
+      // Calculate covering map tiles
+      //
+      PAL_CalcCoverTiles(&g_rgSpriteToDraw[g_nSpriteToDraw - 1]);
+   }
+
+   //
+   // All sprites are now in our array; sort them by their vertical positions.
+   //
+   for (x = 0; x < g_nSpriteToDraw - 1; x++)
+   {
+      SPRITE_TO_DRAW           tmp;
+      BOOL                     fSwap = FALSE;
+
+      for (y = 0; y < g_nSpriteToDraw - 1 - x; y++)
+      {
+         if (PAL_Y(g_rgSpriteToDraw[y].pos) > PAL_Y(g_rgSpriteToDraw[y + 1].pos))
+         {
+            fSwap = TRUE;
+
+            tmp = g_rgSpriteToDraw[y];
+            g_rgSpriteToDraw[y] = g_rgSpriteToDraw[y + 1];
+            g_rgSpriteToDraw[y + 1] = tmp;
+         }
+      }
+
+      if (!fSwap)
+      {
+         break;
+      }
+   }
+
+   //
+   // Draw all the sprites to the screen.
+   //
+   for (i = 0; i < g_nSpriteToDraw; i++)
+   {
+      SPRITE_TO_DRAW *p = &g_rgSpriteToDraw[i];
+
+      x = PAL_X(p->pos);
+      y = PAL_Y(p->pos) - PAL_RLEGetHeight(p->lpSpriteFrame) - p->iLayer;
+
+      PAL_RLEBlitToSurface(p->lpSpriteFrame, gpScreen, PAL_XY(x, y));
+   }
+}
+
+VOID
+PAL_ApplyWave(
+   SDL_Surface         *lpSurface
+)
+/*++
+   Purpose:
+
+     Apply screen waving effect when needed.
+
+   Parameters:
+
+     [OUT] lpSurface - the surface to be proceed.
+
+   Return value:
+
+     None.
+
+--*/
+{
+   int                  wave[32];
+   int                  i, a, b;
+   static int           index = 0;
+   LPBYTE               p;
+   BYTE                 buf[320];
+
+   gpGlobals->wScreenWave += gpGlobals->sWaveProgression;
+
+   if (gpGlobals->wScreenWave == 0 || gpGlobals->wScreenWave >= 256)
+   {
+      //
+      // No need to wave the screen
+      //
+      gpGlobals->wScreenWave = 0;
+      gpGlobals->sWaveProgression = 0;
+      return;
+   }
+
+   //
+   // Calculate the waving offsets.
+   //
+   a = 0;
+   b = 60 + 8;
+
+   for (i = 0; i < 16; i++)
+   {
+      b -= 8;
+      a += b;
+
+      //
+      // WARNING: assuming the screen width is 320
+      //
+      wave[i] = a * gpGlobals->wScreenWave / 256;
+      wave[i + 16] = 320 - wave[i];
+   }
+
+   //
+   // Apply the effect.
+   // WARNING: only works with 320x200 8-bit surface.
+   //
+   a = index;
+   p = (LPBYTE)(lpSurface->pixels);
+
+   //
+   // Loop through all lines in the screen buffer.
+   //
+   for (i = 0; i < 200; i++)
+   {
+      b = wave[a];
+
+      if (b > 0)
+      {
+         //
+         // Do a shift on the current line with the calculated offset.
+         //
+         memcpy(buf, p, b);
+         //memmove(p, p + b, 320 - b);
+         memmove(p, &p[b], 320 - b);
+         //memcpy(p + 320 - b, buf, b);
+         memcpy(&p[320 - b], buf, b);
+      }
+
+      a = (a + 1) % 32;
+      p += lpSurface->pitch;
+   }
+
+   index = (index + 1) % 32;
+}
+
+VOID
+PAL_MakeScene(
+   VOID
+)
+/*++
+   Purpose:
+
+     Draw the scene of the current frame to the screen. Both the map and
+     the sprites are handled here.
+
+   Parameters:
+
+     None.
+
+   Return value:
+
+     None.
+
+--*/
+{
+   static SDL_Rect         rect = {0, 0, 320, 200};
+
+   //
+   // Step 1: Draw the complete map, for both of the layers.
+   //
+   rect.x = PAL_X(gpGlobals->viewport);
+   rect.y = PAL_Y(gpGlobals->viewport);
+
+   PAL_MapBlitToSurface(PAL_GetCurrentMap(), gpScreen, &rect, 0);
+   PAL_MapBlitToSurface(PAL_GetCurrentMap(), gpScreen, &rect, 1);
+
+   //
+   // Step 2: Apply screen waving effects.
+   //
+   PAL_ApplyWave(gpScreen);
+
+   //
+   // Step 3: Draw all the sprites.
+   //
+   PAL_SceneDrawSprites();
+
+   //
+   // Check if we need to fade in.
+   //
+   if (gpGlobals->fNeedToFadeIn)
+   {
+      VIDEO_UpdateScreen(NULL);
+      PAL_FadeIn(gpGlobals->wNumPalette, gpGlobals->fNightPalette, 1);
+      gpGlobals->fNeedToFadeIn = FALSE;
+   }
+}
+
+BOOL
+PAL_CheckObstacle(
+   PAL_POS         pos,
+   BOOL            fCheckEventObjects,
+   WORD            wSelfObject
+)
+/*++
+   Purpose:
+
+     Check if the specified location has obstacle or not.
+
+   Parameters:
+
+     [IN]  pos - the position to check.
+
+     [IN]  fCheckEventObjects - TRUE if check for event objects, FALSE if only
+           check for the map.
+
+     [IN]  wSelfObject - the event object which will be skipped.
+
+   Return value:
+
+     TRUE if the location is obstacle, FALSE if not.
+
+--*/
+{
+   int x, y, h, xr, yr;
+
+   if (PAL_X(pos) < 0 || PAL_X(pos) >= 2048 || PAL_Y(pos) < 0 || PAL_Y(pos) >= 2048)
+   {
+      return TRUE;
+   }
+
+   //
+   // Check if the map tile at the specified position is blocking
+   //
+   x = PAL_X(pos) / 32;
+   y = PAL_Y(pos) / 16;
+   h = 0;
+
+   xr = PAL_X(pos) % 32;
+   yr = PAL_Y(pos) % 16;
+
+   if (xr + yr * 2 >= 16)
+   {
+      if (xr + yr * 2 >= 48)
+      {
+         x++;
+         y++;
+      }
+      else if (32 - xr + yr * 2 < 16)
+      {
+         x++;
+      }
+      else if (32 - xr + yr * 2 < 48)
+      {
+         h = 1;
+      }
+      else
+      {
+         y++;
+      }
+   }
+
+   if (PAL_MapTileIsBlocked(x, y, h, PAL_GetCurrentMap()))
+   {
+      return TRUE;
+   }
+
+   if (fCheckEventObjects)
+   {
+      //
+      // Loop through all event objects in the current scene
+      //
+      int i;
+      for (i = gpGlobals->g.rgScene[gpGlobals->wNumScene - 1].wEventObjectIndex;
+         i < gpGlobals->g.rgScene[gpGlobals->wNumScene].wEventObjectIndex; i++)
+      {
+         LPEVENTOBJECT p = &(gpGlobals->g.lprgEventObject[i]);
+         if (i == wSelfObject - 1)
+         {
+            //
+            // Skip myself
+            //
+            continue;
+         }
+
+         //
+         // Is this object a blocking one?
+         //
+         if (p->sState >= kObjStateBlocker)
+         {
+            //
+            // Check for collision
+            //
+            if (abs(p->x - PAL_X(pos)) + abs(p->y - PAL_Y(pos)) * 2 < 16)
+            {
+               return TRUE;
+            }
+         }
+      }
+   }
+
+   return FALSE;
+}
+
+VOID
+PAL_UpdatePartyGestures(
+   BOOL             fWalking
+)
+/*++
+   Purpose:
+
+     Update the gestures of all the party members.
+
+   Parameters:
+
+     [IN]  fWalking - whether the party is walking or not.
+
+   Return value:
+
+     None.
+
+--*/
+{
+   static int       s_iThisStepFrame = 0;
+   int              iStepFrameFollower = 0, iStepFrameLeader = 0;
+   int              i;
+
+   if (fWalking)
+   {
+      //
+      // Update the gesture for party leader
+      //
+      s_iThisStepFrame = (s_iThisStepFrame + 1) % 4;
+      if (s_iThisStepFrame & 1)
+      {
+         iStepFrameLeader = (s_iThisStepFrame + 1) / 2;
+         iStepFrameFollower = 3 - iStepFrameLeader;
+      }
+      else
+      {
+         iStepFrameLeader = 0;
+         iStepFrameFollower = 0;
+      }
+
+      gpGlobals->rgParty[0].x = PAL_X(gpGlobals->partyoffset);
+      gpGlobals->rgParty[0].y = PAL_Y(gpGlobals->partyoffset);
+
+      if (gpGlobals->g.PlayerRoles.rgwWalkFrames[gpGlobals->rgParty[0].wPlayerRole] == 4)
+      {
+         gpGlobals->rgParty[0].wFrame = gpGlobals->wPartyDirection * 4 + s_iThisStepFrame;
+      }
+      else
+      {
+         gpGlobals->rgParty[0].wFrame = gpGlobals->wPartyDirection * 3 + iStepFrameLeader;
+      }
+
+      //
+      // Update the gestures and positions for other party members
+      //
+      for (i = 1; i <= (short)gpGlobals->wMaxPartyMemberIndex; i++)
+      {
+         gpGlobals->rgParty[i].x = gpGlobals->rgTrail[1].x - PAL_X(gpGlobals->viewport);
+         gpGlobals->rgParty[i].y = gpGlobals->rgTrail[1].y - PAL_Y(gpGlobals->viewport);
+
+         if (i == 2)
+         {
+            gpGlobals->rgParty[i].x +=
+               (gpGlobals->rgTrail[1].wDirection == kDirEast || gpGlobals->rgTrail[1].wDirection == kDirWest) ? -16 : 16;
+            gpGlobals->rgParty[i].y += 8;
+         }
+         else
+         {
+            gpGlobals->rgParty[i].x +=
+               ((gpGlobals->rgTrail[1].wDirection == kDirWest || gpGlobals->rgTrail[1].wDirection == kDirSouth) ? 16 : -16);
+            gpGlobals->rgParty[i].y +=
+               ((gpGlobals->rgTrail[1].wDirection == kDirWest || gpGlobals->rgTrail[1].wDirection == kDirNorth) ? 8 : -8);
+         }
+
+         //
+         // Adjust the position if there is obstacle
+         //
+         if (PAL_CheckObstacle(PAL_XY(gpGlobals->rgParty[i].x + PAL_X(gpGlobals->viewport),
+            gpGlobals->rgParty[i].y + PAL_Y(gpGlobals->viewport)), TRUE, 0))
+         {
+            gpGlobals->rgParty[i].x = gpGlobals->rgTrail[1].x - PAL_X(gpGlobals->viewport);
+            gpGlobals->rgParty[i].y = gpGlobals->rgTrail[1].y - PAL_Y(gpGlobals->viewport);
+         }
+
+         //
+         // Update gesture for this party member
+         //
+         if (gpGlobals->g.PlayerRoles.rgwWalkFrames[gpGlobals->rgParty[i].wPlayerRole] == 4)
+         {
+            gpGlobals->rgParty[i].wFrame = gpGlobals->rgTrail[2].wDirection * 4 + s_iThisStepFrame;
+         }
+         else
+         {
+            gpGlobals->rgParty[i].wFrame = gpGlobals->rgTrail[2].wDirection * 3 + iStepFrameLeader;
+         }
+      }
+
+      if (gpGlobals->nFollower > 0)
+      {
+         //
+         // Update the position and gesture for the follower
+         //
+         gpGlobals->rgParty[gpGlobals->wMaxPartyMemberIndex + 1].x =
+            gpGlobals->rgTrail[3].x - PAL_X(gpGlobals->viewport);
+         gpGlobals->rgParty[gpGlobals->wMaxPartyMemberIndex + 1].y =
+            gpGlobals->rgTrail[3].y - PAL_Y(gpGlobals->viewport);
+         gpGlobals->rgParty[gpGlobals->wMaxPartyMemberIndex + 1].wFrame =
+            gpGlobals->rgTrail[3].wDirection * 3 + iStepFrameFollower;
+      }
+   }
+   else
+   {
+      //
+      // Player is not moved. Use the "standing" gesture instead of "walking" one.
+      //
+      i = gpGlobals->g.PlayerRoles.rgwWalkFrames[gpGlobals->rgParty[0].wPlayerRole];
+      if (i == 0)
+      {
+         i = 3;
+      }
+      gpGlobals->rgParty[0].wFrame = gpGlobals->wPartyDirection * i;
+
+      for (i = 1; i <= (short)gpGlobals->wMaxPartyMemberIndex; i++)
+      {
+         int f = gpGlobals->g.PlayerRoles.rgwWalkFrames[gpGlobals->rgParty[i].wPlayerRole];
+         if (f == 0)
+         {
+            f = 3;
+         }
+         gpGlobals->rgParty[i].wFrame = gpGlobals->rgTrail[2].wDirection * f;
+      }
+
+      if (gpGlobals->nFollower > 0)
+      {
+         gpGlobals->rgParty[gpGlobals->wMaxPartyMemberIndex + 1].wFrame =
+            gpGlobals->rgTrail[3].wDirection * 3;
+      }
+
+      s_iThisStepFrame &= 2;
+      s_iThisStepFrame ^= 2;
+   }
+}
+
+VOID
+PAL_UpdateParty(
+   VOID
+)
+/*++
+   Purpose:
+
+     Update the location and walking gesture of all the party members.
+
+   Parameters:
+
+     None.
+
+   Return value:
+
+     None.
+
+--*/
+{
+   int              xSource, ySource, xTarget, yTarget, xOffset, yOffset, i;
+
+   //
+   // Has user pressed one of the arrow keys?
+   //
+   if (g_InputState.dir != kDirUnknown)
+   {
+      xOffset = ((g_InputState.dir == kDirWest || g_InputState.dir == kDirSouth) ? -16 : 16);
+      yOffset = ((g_InputState.dir == kDirWest || g_InputState.dir == kDirNorth) ? -8 : 8);
+
+      xSource = PAL_X(gpGlobals->viewport) + PAL_X(gpGlobals->partyoffset);
+      ySource = PAL_Y(gpGlobals->viewport) + PAL_Y(gpGlobals->partyoffset);
+
+      xTarget = xSource + xOffset;
+      yTarget = ySource + yOffset;
+
+      gpGlobals->wPartyDirection = g_InputState.dir;
+
+      //
+      // Check for obstacles on the destination location
+      //
+      if (!PAL_CheckObstacle(PAL_XY(xTarget, yTarget), TRUE, 0))
+      {
+         //
+         // Player will actually be moved. Store trail.
+         //
+         for (i = 3; i >= 0; i--)
+         {
+            gpGlobals->rgTrail[i + 1] = gpGlobals->rgTrail[i];
+         }
+
+         gpGlobals->rgTrail[0].wDirection = g_InputState.dir;
+         gpGlobals->rgTrail[0].x = xSource;
+         gpGlobals->rgTrail[0].y = ySource;
+
+         //
+         // Move the viewport
+         //
+         gpGlobals->viewport =
+            PAL_XY(PAL_X(gpGlobals->viewport) + xOffset, PAL_Y(gpGlobals->viewport) + yOffset);
+
+         //
+         // Update gestures
+         //
+         PAL_UpdatePartyGestures(TRUE);
+
+         return; // don't go further
+      }
+   }
+
+   PAL_UpdatePartyGestures(FALSE);
+}
+
+VOID
+PAL_NPCWalkOneStep(
+   WORD          wEventObjectID,
+   INT           iSpeed
+)
+/*++
+  Purpose:
+
+    Move and animate the specified event object (NPC).
+
+  Parameters:
+
+    [IN]  wEventObjectID - the event object to move.
+
+    [IN]  iSpeed - speed of the movement.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   LPEVENTOBJECT        p;
+
+   //
+   // Check for invalid parameters
+   //
+   if (wEventObjectID == 0 || wEventObjectID > gpGlobals->g.nEventObject)
+   {
+      return;
+   }
+
+   p = &(gpGlobals->g.lprgEventObject[wEventObjectID - 1]);
+
+   //
+   // Move the event object by the specified direction
+   //
+   p->x += ((p->wDirection == kDirWest || p->wDirection == kDirSouth) ? -2 : 2) * iSpeed;
+   p->y += ((p->wDirection == kDirWest || p->wDirection == kDirNorth) ? -1 : 1) * iSpeed;
+
+   //
+   // Update the gesture
+   //
+   if (p->nSpriteFrames > 0)
+   {
+      p->wCurrentFrameNum++;
+      p->wCurrentFrameNum %= (p->nSpriteFrames == 3 ? 4 : p->nSpriteFrames);
+   }
+   else if (p->nSpriteFramesAuto > 0)
+   {
+      p->wCurrentFrameNum++;
+      p->wCurrentFrameNum %= p->nSpriteFramesAuto;
+   }
+}

+ 66 - 0
scene.h

@@ -0,0 +1,66 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef _SCENE_H
+#define	_SCENE_H
+
+#ifdef	__cplusplus
+extern "C"
+{
+#endif
+
+VOID
+PAL_ApplyWave(
+   SDL_Surface    *lpSurface
+);
+
+VOID
+PAL_MakeScene(
+   VOID
+);
+
+BOOL
+PAL_CheckObstacle(
+   PAL_POS         pos,
+   BOOL            fCheckEventObjects,
+   WORD            wSelfObject
+);
+
+VOID
+PAL_UpdatePartyGestures(
+   BOOL             fWalking
+);
+
+VOID
+PAL_UpdateParty(
+   VOID
+);
+
+VOID
+PAL_NPCWalkOneStep(
+   WORD          wEventObjectID,
+   INT           iSpeed
+);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif

File diff suppressed because it is too large
+ 3425 - 0
script.c


+ 47 - 0
script.h

@@ -0,0 +1,47 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef SCRIPT_H
+#define SCRIPT_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+WORD
+PAL_RunTriggerScript(
+   WORD           wScriptEntry,
+   WORD           wEventObjectID
+);
+
+WORD
+PAL_RunAutoScript(
+   WORD           wScriptEntry,
+   WORD           wEventObjectID
+);
+
+extern BOOL       g_fScriptSuccess;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 154 - 0
sdlpal.cproj

@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>8.0.50727</ProductVersion>
+    <ProjectGuid>{97ADEBD0-5333-4E9D-A9F7-6F3F226ECA5C}</ProjectGuid>
+    <Compiler>
+      <Compiler ctype="GccCompiler" />
+    </Compiler>
+    <Language>C</Language>
+    <Target>Bin</Target>
+    <SchemaVersion>2.0</SchemaVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>bin\Debug</OutputPath>
+    <DefineSymbols>DEBUG MONODEVELOP</DefineSymbols>
+    <SourceDirectory>.</SourceDirectory>
+    <OutputName>sdlpal</OutputName>
+    <CompileTarget>Bin</CompileTarget>
+    <ExtraLinkerArguments>`sdl-config --libs` -lm</ExtraLinkerArguments>
+    <ExtraCompilerArguments>`sdl-config --cflags`</ExtraCompilerArguments>
+    <Libs>
+      <Libs>
+        <Lib>stdc++</Lib>
+      </Libs>
+    </Libs>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <OutputPath>bin\Release</OutputPath>
+    <DefineSymbols>MONODEVELOP</DefineSymbols>
+    <SourceDirectory>.</SourceDirectory>
+    <OptimizationLevel>3</OptimizationLevel>
+    <OutputName>sdlpal</OutputName>
+    <CompileTarget>Bin</CompileTarget>
+    <ExtraLinkerArguments>`sdl-config --libs` -lm</ExtraLinkerArguments>
+    <ExtraCompilerArguments>`sdl-config --cflags`</ExtraCompilerArguments>
+    <Libs>
+      <Libs>
+        <Lib>stdc++</Lib>
+      </Libs>
+    </Libs>
+  </PropertyGroup>
+  <ItemGroup>
+    <None Include="ascii.h" />
+    <None Include="battle.h" />
+    <None Include="common.h" />
+    <None Include="ending.h" />
+    <None Include="fight.h" />
+    <None Include="font.h" />
+    <None Include="game.h" />
+    <None Include="getopt.h" />
+    <None Include="global.h" />
+    <None Include="input.h" />
+    <None Include="itemmenu.h" />
+    <None Include="magicmenu.h" />
+    <None Include="main.h" />
+    <None Include="map.h" />
+    <None Include="palcommon.h" />
+    <None Include="palette.h" />
+    <None Include="play.h" />
+    <None Include="res.h" />
+    <None Include="rixplay.h" />
+    <None Include="rngplay.h" />
+    <None Include="scene.h" />
+    <None Include="script.h" />
+    <None Include="sound.h" />
+    <None Include="text.h" />
+    <None Include="ui.h" />
+    <None Include="uibattle.h" />
+    <None Include="uigame.h" />
+    <None Include="util.h" />
+    <None Include="video.h" />
+    <None Include="adplug\binfile.h" />
+    <None Include="adplug\binio.h" />
+    <None Include="adplug\demuopl.h" />
+    <None Include="adplug\dosbox_opl.h" />
+    <None Include="adplug\emuopl.h" />
+    <None Include="adplug\fmopl.h" />
+    <None Include="adplug\fprovide.h" />
+    <None Include="adplug\opl.h" />
+    <None Include="adplug\player.h" />
+    <None Include="adplug\rix.h" />
+    <None Include="libmad\bit.h" />
+    <None Include="libmad\D.dat" />
+    <None Include="libmad\decoder.h" />
+    <None Include="libmad\fixed.h" />
+    <None Include="libmad\frame.h" />
+    <None Include="libmad\huffman.h" />
+    <None Include="libmad\imdct_s.dat" />
+    <None Include="libmad\layer3.h" />
+    <None Include="libmad\layer12.h" />
+    <None Include="libmad\libmad_config.h" />
+    <None Include="libmad\libmad_global.h" />
+    <None Include="libmad\mad.h" />
+    <None Include="libmad\qc_table.dat" />
+    <None Include="libmad\rq_table.dat" />
+    <None Include="libmad\sf_table.dat" />
+    <None Include="libmad\stream.h" />
+    <None Include="libmad\synth.h" />
+    <None Include="libmad\timer.h" />
+    <None Include="libmad\music_mad.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="battle.c" />
+    <Compile Include="ending.c" />
+    <Compile Include="fight.c" />
+    <Compile Include="font.c" />
+    <Compile Include="game.c" />
+    <Compile Include="getopt.c" />
+    <Compile Include="global.c" />
+    <Compile Include="input.c" />
+    <Compile Include="itemmenu.c" />
+    <Compile Include="magicmenu.c" />
+    <Compile Include="main.c" />
+    <Compile Include="map.c" />
+    <Compile Include="palcommon.c" />
+    <Compile Include="palette.c" />
+    <Compile Include="play.c" />
+    <Compile Include="res.c" />
+    <Compile Include="rngplay.c" />
+    <Compile Include="scene.c" />
+    <Compile Include="script.c" />
+    <Compile Include="sound.c" />
+    <Compile Include="text.c" />
+    <Compile Include="ui.c" />
+    <Compile Include="uibattle.c" />
+    <Compile Include="uigame.c" />
+    <Compile Include="util.c" />
+    <Compile Include="video.c" />
+    <Compile Include="yj1.c" />
+    <Compile Include="rixplay.cpp" />
+    <Compile Include="adplug\binfile.cpp" />
+    <Compile Include="adplug\binio.cpp" />
+    <Compile Include="adplug\dosbox_opl.cpp" />
+    <Compile Include="adplug\emuopl.cpp" />
+    <Compile Include="adplug\fmopl.c" />
+    <Compile Include="adplug\fprovide.cpp" />
+    <Compile Include="adplug\player.cpp" />
+    <Compile Include="adplug\rix.cpp" />
+    <Compile Include="libmad\bit.c" />
+    <Compile Include="libmad\decoder.c" />
+    <Compile Include="libmad\fixed.c" />
+    <Compile Include="libmad\frame.c" />
+    <Compile Include="libmad\huffman.c" />
+    <Compile Include="libmad\layer3.c" />
+    <Compile Include="libmad\layer12.c" />
+    <Compile Include="libmad\stream.c" />
+    <Compile Include="libmad\synth.c" />
+    <Compile Include="libmad\timer.c" />
+    <Compile Include="libmad\music_mad.c" />
+  </ItemGroup>
+</Project>

File diff suppressed because it is too large
+ 1203 - 0
sdlpal.dev


+ 576 - 0
sdlpal.dsp

@@ -0,0 +1,576 @@
+# Microsoft Developer Studio Project File - Name="sdlpal" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=sdlpal - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "sdlpal.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "sdlpal.mak" CFG="sdlpal - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "sdlpal - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "sdlpal - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "sdlpal - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /Op /I "d:\sdl\include" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x804 /d "NDEBUG"
+# ADD RSC /l 0x804 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib sdl.lib sdlmain.lib /nologo /subsystem:windows /machine:I386 /libpath:"d:\sdl\lib"
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF  "$(CFG)" == "sdlpal - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "d:\sdl\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x804 /d "_DEBUG"
+# ADD RSC /l 0x804 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib sdl.lib sdlmain.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept /libpath:"d:\sdl\lib"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "sdlpal - Win32 Release"
+# Name "sdlpal - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\battle.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ending.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\fight.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\font.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\game.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\getopt.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\global.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\input.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\itemmenu.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\magicmenu.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\main.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\map.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\midi.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\palcommon.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\palette.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\play.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\res.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\rixplay.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\rngplay.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\scene.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\script.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\sound.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\text.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ui.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\uibattle.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\uigame.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\util.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\video.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\yj1.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\ascii.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\battle.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\common.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ending.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\fight.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\font.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\game.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\getopt.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\global.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\input.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\itemmenu.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\magicmenu.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\main.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\map.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\midi.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\palcommon.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\palette.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\play.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\res.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\rixplay.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\rngplay.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\scene.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\script.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\sound.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\text.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ui.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\uibattle.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\uigame.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\util.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\video.h
+# End Source File
+# End Group
+# Begin Group "adplug"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\adplug\binfile.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\adplug\binfile.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\adplug\binio.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\adplug\binio.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\adplug\demuopl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\adplug\dosbox_opl.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\adplug\dosbox_opl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\adplug\emuopl.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\adplug\emuopl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\adplug\fmopl.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\adplug\fmopl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\adplug\fprovide.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\adplug\fprovide.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\adplug\opl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\adplug\player.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\adplug\player.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\adplug\rix.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\adplug\rix.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\adplug\surroundopl.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\adplug\surroundopl.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\sdlpal.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\sdlpal.rc
+# End Source File
+# End Group
+# Begin Group "native_midi"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\native_midi\native_midi.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\native_midi\native_midi_common.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\native_midi\native_midi_common.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\native_midi\native_midi_win32.c
+# End Source File
+# End Group
+# Begin Group "libmad"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\libmad\bit.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\bit.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\D.dat
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\decoder.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\decoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\fixed.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\fixed.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\frame.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\frame.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\huffman.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\huffman.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\imdct_s.dat
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\layer12.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\layer12.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\layer3.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\layer3.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\libmad_config.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\libmad_global.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\mad.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\music_mad.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\music_mad.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\qc_table.dat
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\rq_table.dat
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\sf_table.dat
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\stream.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\stream.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\synth.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\synth.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\timer.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\libmad\timer.h
+# End Source File
+# End Group
+# End Target
+# End Project

BIN
sdlpal.icns


BIN
sdlpal.ico


+ 1 - 0
sdlpal.rc

@@ -0,0 +1 @@
+101 ICON "sdlpal.ico"

File diff suppressed because it is too large
+ 2808 - 0
sdlpal.tgt


+ 43 - 0
sdlpal.wpj

@@ -0,0 +1,43 @@
+40
+projectIdent
+0
+VpeMain
+1
+WRect
+832
+273
+7680
+9147
+2
+MProject
+3
+MCommand
+0
+4
+MCommand
+0
+1
+5
+WFileName
+10
+sdlpal.tgt
+6
+WVList
+1
+7
+VComponent
+8
+WRect
+192
+22
+5632
+4141
+0
+0
+9
+WFileName
+10
+sdlpal.tgt
+75
+89
+7

+ 64 - 0
sdlpal.xpm

@@ -0,0 +1,64 @@
+static char *sdlpal_xpm[] = {
+"32 32 30 1",
+" 	c #000000",
+".	c #000040",
+"+	c #0000E7",
+"@	c #000087",
+"#	c #000048",
+"$	c #000030",
+"%	c #0000FF",
+"&	c #0000DF",
+"*	c #00007F",
+"=	c #00008F",
+"-	c #000028",
+";	c #000090",
+">	c #DFDFFF",
+",	c #FFFFFF",
+"'	c #4040D7",
+")	c #000078",
+"!	c #000080",
+"~	c #CFCFFF",
+"{	c #8888FF",
+"]	c #3030FF",
+"^	c #7878FF",
+"/	c #000088",
+"(	c #000070",
+"_	c #8080FF",
+":	c #0000CF",
+"<	c #2828FF",
+"[	c #D7D7FF",
+"}	c #0000D7",
+"|	c #F7F7FF",
+"1	c #00009F",
+"                                ",
+"                                ",
+"                                ",
+"                            .+@#",
+"                            $%& ",
+"       $@******************=-+; ",
+"      $%%%%%%%%%%>,,,,,,,,,'.   ",
+"      )%%%%%%%%%%,,,,,,,,,,!    ",
+"      %%%%%%%%%%>,,,,,,,,,~$    ",
+"     !%%%%%%%%%%,,,,,,,,,,{     ",
+"     %%%%%%%%%%],,,,,,,,,,!     ",
+"     %%%%%%%%%%{,,,,,,,,,^      ",
+"    $%%%%%%%%%%^,,,,,,,,,]      ",
+"    /%%%%%%%%%%~,,,,,,,,,(      ",
+"    !%%%%%%%%%%,,,,,,,,,,!      ",
+"    !%%%%%%%%%],,,,,,,,,,/      ",
+"    !%%%%%%%%%{,,,,,,,,,~$      ",
+"    (%%%%%%%%%_,,,,,,,,,^       ",
+"    %%%%%%%%%%_,,,,,,,,,_       ",
+"    %%%%%%%%%%_,,,,,,,,,_       ",
+"   $%%%%%%%%%%^,,,,,,,,,{       ",
+"   /%%%%%%%%%%~,,,,,,,,,]       ",
+"   )%%%%%%%%%%,,,,,,,,,,:       ",
+"   :%%%%%%%%%%,,,,,,,,,~/       ",
+"   %%%%%%%%%%<,,,,,,,,,{        ",
+"  -%%%%%%%%%%[,,,,,,,,,)        ",
+"  }%%%%%%%%%<,,,,,,,,,>$        ",
+" ;%%%%%%%%%%|,,,,,,,,,!         ",
+" 1*******************=          ",
+"                                ",
+"                                ",
+"                                "};

+ 729 - 0
sound.c

@@ -0,0 +1,729 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "palcommon.h"
+#include "common.h"
+#include "sound.h"
+#include "rixplay.h"
+#include "util.h"
+
+#ifdef PAL_HAS_NATIVEMIDI
+#include "midi.h"
+#endif
+
+#ifdef PAL_HAS_MP3
+#include "libmad/music_mad.h"
+#endif
+
+static BOOL  gSndOpened = FALSE;
+
+BOOL         g_fNoSound = FALSE;
+BOOL         g_fNoMusic = FALSE;
+
+#ifdef PAL_HAS_NATIVEMIDI
+BOOL         g_fUseMidi = FALSE;
+#endif
+
+static BOOL  g_fUseWav = FALSE;
+
+#ifdef __SYMBIAN32__
+INT          g_iVolume  = SDL_MIX_MAXVOLUME * 0.1;
+#endif
+
+typedef struct tagSNDPLAYER
+{
+   FILE                     *mkf;
+   SDL_AudioSpec             spec;
+   LPBYTE                    buf[2], pos[2];
+   INT                       audio_len[2];
+#ifdef PAL_HAS_CD
+   SDL_CD                   *pCD;
+#endif
+#ifdef PAL_HAS_MP3
+   mad_data                 *pMP3;
+   BOOL                      fMP3Loop;
+   INT                       iCurrentMP3;
+   SDL_mutex                *lock;
+#endif
+} SNDPLAYER;
+
+static SNDPLAYER gSndPlayer;
+
+static SDL_AudioSpec *
+SOUND_LoadVOCFromBuffer(
+   LPCBYTE                lpVOC,
+   DWORD                  dwLen,
+   SDL_AudioSpec         *lpSpec,
+   LPBYTE                *lppBuffer
+)
+/*++
+  Purpose:
+
+    Load a VOC file in a buffer. Currently supports type 01 block only.
+
+  Parameters:
+
+    [IN]  lpVOC - pointer to the buffer of the VOC file.
+
+    [IN]  dwLen - length of the buffer of the VOC file.
+
+    [OUT] lpSpec - pointer to the SDL_AudioSpec structure, which contains
+                   some basic information about the VOC file.
+
+    [OUT] lppBuffer - the output buffer.
+
+  Return value:
+
+    Pointer to the SDL_AudioSpec structure, NULL if failed.
+
+--*/
+{
+   INT freq, len, x, i, l;
+   SDL_RWops *rw;
+
+   if (g_fUseWav)
+   {
+      rw = SDL_RWFromConstMem(lpVOC, dwLen);
+      if (rw == NULL) return NULL;
+
+      len = dwLen;
+
+      SDL_LoadWAV_RW(rw, 1, lpSpec, lppBuffer, (Uint32 *)&len);
+      lpSpec->size = len;
+
+      return lpSpec;
+   }
+   else
+   {
+      //
+      // Skip header
+      //
+      lpVOC += 0x1B;
+
+      //
+      // Length is 3 bytes long
+      //
+      len = (lpVOC[0] | (lpVOC[1] << 8) | (lpVOC[2] << 16)) - 2;
+      lpVOC += 3;
+
+      //
+      // One byte for frequency
+      //
+      freq = 1000000 / (256 - *lpVOC);
+
+#if 1
+
+      lpVOC += 2;
+
+      //
+      // Convert the sample manually, as SDL doesn't like "strange" sample rates.
+      //
+      x = (INT)(len * ((FLOAT)PAL_SAMPLE_RATE / freq));
+
+      *lppBuffer = (LPBYTE)calloc(1, x);
+      if (*lppBuffer == NULL)
+      {
+         return NULL;
+      }
+      for (i = 0; i < x; i++)
+      {
+         l = (INT)(i * (freq / (FLOAT)PAL_SAMPLE_RATE));
+         if (l >= len)
+         {
+            l = len - 1;
+         }
+         (*lppBuffer)[i] = lpVOC[l];
+      }
+
+      lpSpec->channels = 1;
+      lpSpec->format = AUDIO_U8;
+      lpSpec->freq = PAL_SAMPLE_RATE;
+      lpSpec->size = x;
+
+#else
+
+      *lppBuffer = (unsigned char *)malloc(len);
+      if (*lppBuffer == NULL)
+      {
+         return NULL;
+      }
+
+      lpSpec->channels = 1;
+      lpSpec->format = AUDIO_U8;
+      lpSpec->freq = freq;
+      lpSpec->size = len;
+
+      lpVOC += 2;
+      memcpy(*lppBuffer, lpVOC, len);
+
+#endif
+
+      return lpSpec;
+   }
+}
+
+static VOID SDLCALL
+SOUND_FillAudio(
+   LPVOID          udata,
+   LPBYTE          stream,
+   INT             len
+)
+/*++
+  Purpose:
+
+    SDL sound callback function.
+
+  Parameters:
+
+    [IN]  udata - pointer to user-defined parameters (Not used).
+
+    [OUT] stream - pointer to the stream buffer.
+
+    [IN]  len - Length of the buffer.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   int        i;
+
+   //
+   // Play music
+   //
+   if (!g_fNoMusic)
+   {
+#ifdef PAL_HAS_MP3
+      if (gSndPlayer.pMP3 != NULL)
+      {
+         SDL_mutexP(gSndPlayer.lock);
+
+         mad_getSamples(gSndPlayer.pMP3, stream, len);
+
+         if (!mad_isPlaying(gSndPlayer.pMP3) && gSndPlayer.fMP3Loop)
+         {
+            mad_seek(gSndPlayer.pMP3, 0);
+            mad_start(gSndPlayer.pMP3);
+
+            mad_getSamples(gSndPlayer.pMP3, stream, len);
+         }
+
+         SDL_mutexV(gSndPlayer.lock);
+      }
+#endif
+      RIX_FillBuffer(stream, len);
+   }
+
+   //
+   // No current playing sound
+   //
+   if (g_fNoSound)
+   {
+      return;
+   }
+
+   for (i = 0; i < 2; i++)
+   {
+      //
+      // Only play if we have data left
+      //
+      if (gSndPlayer.buf[i] == NULL)
+      {
+         continue;
+      }
+
+      if (gSndPlayer.audio_len[i] == 0)
+      {
+         //
+         // Delete the audio buffer from memory
+         //
+         free(gSndPlayer.buf[i]);
+         gSndPlayer.buf[i] = NULL;
+         continue;
+      }
+
+      //
+      // Mix as much data as possible
+      //
+      len = (len > gSndPlayer.audio_len[i]) ? gSndPlayer.audio_len[i] : len;
+#ifdef __SYMBIAN32__
+      SDL_MixAudio(stream, gSndPlayer.pos[i], len, g_iVolume);
+#else
+      SDL_MixAudio(stream, gSndPlayer.pos[i], len, SDL_MIX_MAXVOLUME);
+#endif
+      gSndPlayer.pos[i] += len;
+      gSndPlayer.audio_len[i] -= len;
+   }
+}
+
+INT
+SOUND_OpenAudio(
+   VOID
+)
+/*++
+  Purpose:
+
+    Initialize the audio subsystem.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    0 if succeed, others if failed.
+
+--*/
+{
+   SDL_AudioSpec spec;
+
+   if (gSndOpened)
+   {
+      //
+      // Already opened
+      //
+      return -1;
+   }
+
+   gSndOpened = FALSE;
+
+   //
+   // Load the MKF file.
+   //
+   gSndPlayer.mkf = fopen(va("%s%s", PAL_PREFIX, "voc.mkf"), "rb");
+   if (gSndPlayer.mkf == NULL)
+   {
+      gSndPlayer.mkf = fopen(va("%s%s", PAL_PREFIX, "sounds.mkf"), "rb");
+      if (gSndPlayer.mkf == NULL)
+      {
+         return -2;
+      }
+      g_fUseWav = TRUE;
+   }
+   else
+   {
+      g_fUseWav = FALSE;
+   }
+
+   //
+   // Open the sound subsystem.
+   //
+   gSndPlayer.spec.freq = PAL_SAMPLE_RATE;
+   gSndPlayer.spec.format = AUDIO_S16;
+   gSndPlayer.spec.channels = PAL_CHANNELS;
+   gSndPlayer.spec.samples = 1024;
+   gSndPlayer.spec.callback = SOUND_FillAudio;
+
+   if (SDL_OpenAudio(&gSndPlayer.spec, &spec) < 0)
+   {
+      //
+      // Failed
+      //
+      return -3;
+   }
+
+   memcpy(&gSndPlayer.spec, &spec, sizeof(SDL_AudioSpec));
+
+   gSndPlayer.buf[0] = NULL;
+   gSndPlayer.pos[0] = NULL;
+   gSndPlayer.audio_len[0] = 0;
+
+   gSndPlayer.buf[1] = NULL;
+   gSndPlayer.pos[1] = NULL;
+   gSndPlayer.audio_len[1] = 0;
+
+   gSndOpened = TRUE;
+
+   //
+   // Initialize the music subsystem.
+   //
+   RIX_Init(va("%s%s", PAL_PREFIX, "mus.mkf"));
+
+#ifdef PAL_HAS_CD
+   //
+   // Initialize the CD audio.
+   //
+   {
+      int i;
+      gSndPlayer.pCD = NULL;
+
+      for (i = 0; i < SDL_CDNumDrives(); i++)
+      {
+         gSndPlayer.pCD = SDL_CDOpen(i);
+         if (gSndPlayer.pCD != NULL)
+         {
+            if (!CD_INDRIVE(SDL_CDStatus(gSndPlayer.pCD)))
+            {
+               SDL_CDClose(gSndPlayer.pCD);
+               gSndPlayer.pCD = NULL;
+            }
+            else
+            {
+               break;
+            }
+         }
+      }
+   }
+#endif
+
+#ifdef PAL_HAS_MP3
+   gSndPlayer.iCurrentMP3 = -1;
+   gSndPlayer.lock = SDL_CreateMutex();
+#endif
+
+   //
+   // Let the callback function run so that musics will be played.
+   //
+   SDL_PauseAudio(0);
+
+   return 0;
+}
+
+#ifdef PSP
+void
+SOUND_ReloadVOC(
+	void
+)
+{
+   gSndPlayer.mkf = fopen(va("%s%s", PAL_PREFIX, "voc.mkf"), "rb");
+   g_fUseWav = FALSE;
+}
+#endif
+
+VOID
+SOUND_CloseAudio(
+   VOID
+)
+/*++
+  Purpose:
+
+    Close the audio subsystem.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   SDL_CloseAudio();
+
+   if (gSndPlayer.buf[0] != NULL)
+   {
+      free(gSndPlayer.buf[0]);
+      gSndPlayer.buf[0] = NULL;
+   }
+
+   if (gSndPlayer.buf[1] != NULL)
+   {
+      free(gSndPlayer.buf[1]);
+      gSndPlayer.buf[1] = NULL;
+   }
+
+   if (gSndPlayer.mkf != NULL)
+   {
+      fclose(gSndPlayer.mkf);
+      gSndPlayer.mkf = NULL;
+   }
+
+#ifdef PAL_HAS_MP3
+   SDL_mutexP(gSndPlayer.lock);
+
+   if (gSndPlayer.pMP3 != NULL)
+   {
+      mad_stop(gSndPlayer.pMP3);
+      mad_closeFile(gSndPlayer.pMP3);
+      gSndPlayer.pMP3 = NULL;
+   }
+
+   SDL_DestroyMutex(gSndPlayer.lock);
+#endif
+
+   RIX_Shutdown();
+
+#ifdef PAL_HAS_CD
+   if (gSndPlayer.pCD != NULL)
+   {
+      SOUND_PlayCDA(-1);
+      SDL_CDClose(gSndPlayer.pCD);
+   }
+#endif
+
+#ifdef PAL_HAS_NATIVEMIDI
+   MIDI_Play(0, FALSE);
+#endif
+}
+
+#ifdef __SYMBIAN32__
+
+VOID
+SOUND_AdjustVolume(
+   INT    iDirectory
+)
+/*++
+  Purpose:
+
+    SDL sound volume adjust function.
+
+  Parameters:
+
+    [IN]  iDirectory - value, Increase (>0) or decrease (<=0) 3% volume.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   if (iDirectory > 0)
+   {
+      if (g_iVolume <= SDL_MIX_MAXVOLUME)
+      {
+         g_iVolume += SDL_MIX_MAXVOLUME * 0.03;
+      }
+      else
+      {
+         g_iVolume = SDL_MIX_MAXVOLUME;
+      }
+   }
+   else
+   {
+      if (g_iVolume > 0)
+      {
+         g_iVolume -= SDL_MIX_MAXVOLUME * 0.03;
+      }
+      else
+      {
+         g_iVolume = 0;
+      }
+   }
+}
+
+#endif
+
+VOID
+SOUND_PlayChannel(
+   INT    iSoundNum,
+   INT    iChannel
+)
+/*++
+  Purpose:
+
+    Play a sound in voc.mkf file.
+
+  Parameters:
+
+    [IN]  iSoundNum - number of the sound.
+
+    [IN]  iChannel - the number of channel (0 or 1).
+
+  Return value:
+
+    None.
+
+--*/
+{
+   SDL_AudioCVT    wavecvt;
+   SDL_AudioSpec   wavespec;
+   LPBYTE          buf, bufdec;
+   UINT            samplesize;
+   int             len;
+
+   if (!gSndOpened || g_fNoSound)
+   {
+      return;
+   }
+
+   //
+   // Stop playing current sound.
+   //
+   if (gSndPlayer.buf[iChannel] != NULL)
+   {
+      LPBYTE p = gSndPlayer.buf[iChannel];
+      gSndPlayer.buf[iChannel] = NULL;
+      free(p);
+   }
+
+   if (iSoundNum < 0)
+   {
+      return;
+   }
+
+   //
+   // Get the length of the sound file.
+   //
+   len = PAL_MKFGetChunkSize(iSoundNum, gSndPlayer.mkf);
+   if (len <= 0)
+   {
+      return;
+   }
+
+   buf = (LPBYTE)calloc(len, 1);
+   if (buf == NULL)
+   {
+      return;
+   }
+
+   //
+   // Read the sound file from the MKF archive.
+   //
+   PAL_MKFReadChunk(buf, len, iSoundNum, gSndPlayer.mkf);
+
+   SOUND_LoadVOCFromBuffer(buf, len, &wavespec, &bufdec);
+   free(buf);
+
+   //
+   // Build the audio converter and create conversion buffers
+   //
+   if (SDL_BuildAudioCVT(&wavecvt, wavespec.format, wavespec.channels, wavespec.freq,
+      gSndPlayer.spec.format, gSndPlayer.spec.channels, gSndPlayer.spec.freq) < 0)
+   {
+      free(bufdec);
+      return;
+   }
+
+   samplesize = ((wavespec.format & 0xFF) / 8) * wavespec.channels;
+   wavecvt.len = wavespec.size & ~(samplesize - 1);
+   wavecvt.buf = (LPBYTE)malloc(wavecvt.len * wavecvt.len_mult);
+   if (wavecvt.buf == NULL)
+   {
+      free(bufdec);
+      return;
+   }
+   memcpy(wavecvt.buf, bufdec, wavespec.size);
+   free(bufdec);
+
+   //
+   // Run the audio converter
+   //
+   if (SDL_ConvertAudio(&wavecvt) < 0)
+   {
+      return;
+   }
+
+   gSndPlayer.buf[iChannel] = wavecvt.buf;
+   gSndPlayer.audio_len[iChannel] = wavecvt.len * wavecvt.len_mult;
+   gSndPlayer.pos[iChannel] = wavecvt.buf;
+}
+
+VOID
+PAL_PlayMUS(
+   INT       iNumRIX,
+   BOOL      fLoop,
+   FLOAT     flFadeTime
+)
+{
+#ifdef PAL_HAS_NATIVEMIDI
+   if (g_fUseMidi)
+   {
+      MIDI_Play(iNumRIX, fLoop);
+      return;
+   }
+#endif
+
+#ifdef PAL_HAS_MP3
+   if (gSndPlayer.pMP3 != NULL)
+   {
+      if (iNumRIX == gSndPlayer.iCurrentMP3 && !g_fNoMusic)
+      {
+         return;
+      }
+
+      SDL_mutexP(gSndPlayer.lock);
+
+      mad_stop(gSndPlayer.pMP3);
+      mad_closeFile(gSndPlayer.pMP3);
+
+      gSndPlayer.pMP3 = NULL;
+
+      SDL_mutexV(gSndPlayer.lock);
+   }
+
+   gSndPlayer.iCurrentMP3 = -1;
+
+   if (iNumRIX > 0)
+   {
+      SDL_mutexP(gSndPlayer.lock);
+
+      gSndPlayer.pMP3 = mad_openFile(va("%s/mp3/%.2d.mp3", PAL_PREFIX, iNumRIX), &gSndPlayer.spec);
+      if (gSndPlayer.pMP3 != NULL)
+      {
+         RIX_Play(0, FALSE, flFadeTime);
+
+         mad_start(gSndPlayer.pMP3);
+         gSndPlayer.fMP3Loop = fLoop;
+         gSndPlayer.iCurrentMP3 = iNumRIX;
+         SDL_mutexV(gSndPlayer.lock);
+
+         return;
+      }
+
+      SDL_mutexV(gSndPlayer.lock);
+   }
+#endif
+
+   RIX_Play(iNumRIX, fLoop, flFadeTime);
+}
+
+BOOL
+SOUND_PlayCDA(
+   INT    iNumTrack
+)
+/*++
+  Purpose:
+
+    Play a CD Audio Track.
+
+  Parameters:
+
+    [IN]  iNumTrack - number of the CD Audio Track.
+
+  Return value:
+
+    TRUE if the track can be played, FALSE if not.
+
+--*/
+{
+#ifdef PAL_HAS_CD
+   if (gSndPlayer.pCD != NULL)
+   {
+      if (CD_INDRIVE(SDL_CDStatus(gSndPlayer.pCD)))
+      {
+         SDL_CDStop(gSndPlayer.pCD);
+
+         if (iNumTrack != -1)
+         {
+            PAL_PlayMUS(-1, FALSE, 0);
+
+            if (SDL_CDPlayTracks(gSndPlayer.pCD, iNumTrack - 1, 0, 1, 0) == 0)
+            {
+               return TRUE;
+            }
+         }
+      }
+   }
+#endif
+
+   return FALSE;
+}

+ 86 - 0
sound.h

@@ -0,0 +1,86 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef SOUND_H
+#define SOUND_H
+
+#include "common.h"
+
+#ifndef PAL_SAMPLE_RATE
+#define PAL_SAMPLE_RATE     49716
+#endif
+
+#ifndef PAL_CHANNELS
+#define PAL_CHANNELS        1
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+INT
+SOUND_OpenAudio(
+   VOID
+);
+
+VOID
+SOUND_CloseAudio(
+   VOID
+);
+
+VOID
+SOUND_PlayChannel(
+   INT    iSoundNum,
+   INT    iChannel
+);
+
+#ifdef __SYMBIAN32__
+VOID
+SOUND_AdjustVolume(
+   INT    iDirectory
+);
+#endif
+
+VOID
+PAL_PlayMUS(
+   INT       iNumRIX,
+   BOOL      fLoop,
+   FLOAT     flFadeTime
+);
+
+BOOL
+SOUND_PlayCDA(
+   INT    iNumTrack
+);
+
+#define SOUND_Play(i) SOUND_PlayChannel((i), 0)
+
+extern BOOL       g_fNoSound;
+extern BOOL       g_fNoMusic;
+#ifdef PAL_HAS_NATIVEMIDI
+extern BOOL       g_fUseMidi;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 988 - 0
text.c

@@ -0,0 +1,988 @@
+//
+// Copyright (c) 2008, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// Portions based on PALx Project by palxex.
+// Copyright (c) 2006-2008, Pal Lockheart <palxex@gmail.com>.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "main.h"
+
+#define WORD_LENGTH      10
+
+#define   FONT_COLOR_DEFAULT        0x4F
+#define   FONT_COLOR_YELLOW         0x2D
+#define   FONT_COLOR_RED            0x1A
+#define   FONT_COLOR_CYAN           0x8D
+#define   FONT_COLOR_CYAN_ALT       0x8C
+
+BOOL      g_fUpdatedInBattle      = FALSE;
+
+static const char g_rgszAdditionalWords[][WORD_LENGTH + 1] = {
+   {0xBE, 0xD4, 0xB0, 0xAB, 0xB3, 0x74, 0xAB, 0xD7, 0x00, 0x00, 0x00}, // Battle Speed
+   {0xA4, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 1
+   {0xA4, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 2
+   {0xA4, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 3
+   {0xA5, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 4
+   {0xA4, 0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 5
+};
+
+typedef struct tagTEXTLIB
+{
+   LPBYTE          lpWordBuf;
+   LPBYTE          lpMsgBuf;
+   LPDWORD         lpMsgOffset;
+
+   int             nWords;
+   int             nMsgs;
+
+   int             nCurrentDialogLine;
+   BYTE            bCurrentFontColor;
+   PAL_POS         posIcon;
+   PAL_POS         posDialogTitle;
+   PAL_POS         posDialogText;
+   BYTE            bDialogPosition;
+   BYTE            bIcon;
+   int             iDelayTime;
+   BOOL            fUserSkip;
+   BOOL            fPlayingRNG;
+
+   BYTE            bufDialogIcons[282];
+} TEXTLIB, *LPTEXTLIB;
+
+static TEXTLIB         g_TextLib;
+
+INT
+PAL_InitText(
+   VOID
+)
+/*++
+  Purpose:
+
+    Initialize the in-game texts.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    0 = success.
+    -1 = memory allocation error.
+
+--*/
+{
+   FILE       *fpMsg, *fpWord;
+   int         i;
+
+   //
+   // Open the message and word data files.
+   //
+   fpMsg = UTIL_OpenRequiredFile("m.msg");
+   fpWord = UTIL_OpenRequiredFile("word.dat");
+
+   //
+   // See how many words we have
+   //
+   fseek(fpWord, 0, SEEK_END);
+   i = ftell(fpWord);
+
+   //
+   // Each word has 10 bytes
+   //
+   g_TextLib.nWords = (i + (WORD_LENGTH - 1)) / WORD_LENGTH;
+
+   //
+   // Read the words
+   //
+   g_TextLib.lpWordBuf = (LPBYTE)malloc(i);
+   if (g_TextLib.lpWordBuf == NULL)
+   {
+      fclose(fpWord);
+      fclose(fpMsg);
+      return -1;
+   }
+   fseek(fpWord, 0, SEEK_SET);
+   fread(g_TextLib.lpWordBuf, i, 1, fpWord);
+
+   //
+   // Close the words file
+   //
+   fclose(fpWord);
+
+   //
+   // Read the message offsets. The message offsets are in SSS.MKF #3
+   //
+   i = PAL_MKFGetChunkSize(3, gpGlobals->f.fpSSS) / sizeof(DWORD);
+   g_TextLib.nMsgs = i - 1;
+
+   g_TextLib.lpMsgOffset = (LPDWORD)malloc(i * sizeof(DWORD));
+   if (g_TextLib.lpMsgOffset == NULL)
+   {
+      free(g_TextLib.lpWordBuf);
+      fclose(fpMsg);
+      return -1;
+   }
+
+   PAL_MKFReadChunk((LPBYTE)(g_TextLib.lpMsgOffset), i * sizeof(DWORD), 3,
+      gpGlobals->f.fpSSS);
+
+   //
+   // Read the messages.
+   //
+   fseek(fpMsg, 0, SEEK_END);
+   i = ftell(fpMsg);
+
+   g_TextLib.lpMsgBuf = (LPBYTE)malloc(i);
+   if (g_TextLib.lpMsgBuf == NULL)
+   {
+      free(g_TextLib.lpMsgOffset);
+      free(g_TextLib.lpWordBuf);
+      fclose(fpMsg);
+      return -1;
+   }
+
+   fseek(fpMsg, 0, SEEK_SET);
+   fread(g_TextLib.lpMsgBuf, 1, i, fpMsg);
+
+   fclose(fpMsg);
+
+   g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT;
+   g_TextLib.bIcon = 0;
+   g_TextLib.posIcon = 0;
+   g_TextLib.nCurrentDialogLine = 0;
+   g_TextLib.iDelayTime = 3;
+   g_TextLib.posDialogTitle = PAL_XY(12, 8);
+   g_TextLib.posDialogText = PAL_XY(44, 26);
+   g_TextLib.bDialogPosition = kDialogUpper;
+   g_TextLib.fUserSkip = FALSE;
+
+   PAL_MKFReadChunk(g_TextLib.bufDialogIcons, 282, 12, gpGlobals->f.fpDATA);
+
+   return 0;
+}
+
+VOID
+PAL_FreeText(
+   VOID
+)
+/*++
+  Purpose:
+
+    Free the memory used by the texts.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   if (g_TextLib.lpMsgBuf != NULL)
+   {
+      free(g_TextLib.lpMsgBuf);
+      g_TextLib.lpMsgBuf = NULL;
+   }
+
+   if (g_TextLib.lpMsgOffset != NULL)
+   {
+      free(g_TextLib.lpMsgOffset);
+      g_TextLib.lpMsgOffset = NULL;
+   }
+
+   if (g_TextLib.lpWordBuf != NULL)
+   {
+      free(g_TextLib.lpWordBuf);
+      g_TextLib.lpWordBuf = NULL;
+   }
+}
+
+LPCSTR
+PAL_GetWord(
+   WORD       wNumWord
+)
+/*++
+  Purpose:
+
+    Get the specified word.
+
+  Parameters:
+
+    [IN]  wNumWord - the number of the requested word.
+
+  Return value:
+
+    Pointer to the requested word. NULL if not found.
+
+--*/
+{
+   static char buf[WORD_LENGTH + 1];
+
+   if (wNumWord >= PAL_ADDITIONAL_WORD_FIRST)
+   {
+      return g_rgszAdditionalWords[wNumWord - PAL_ADDITIONAL_WORD_FIRST];
+   }
+
+   if (wNumWord >= g_TextLib.nWords)
+   {
+      return NULL;
+   }
+
+   memcpy(buf, &g_TextLib.lpWordBuf[wNumWord * WORD_LENGTH], WORD_LENGTH);
+   buf[WORD_LENGTH] = '\0';
+
+   //
+   // Remove the trailing spaces
+   //
+   trim(buf);
+
+   if ((strlen(buf) & 1) != 0 && buf[strlen(buf) - 1] == '1')
+   {
+      buf[strlen(buf) - 1] = '\0';
+   }
+
+   return buf;
+}
+
+LPCSTR
+PAL_GetMsg(
+   WORD       wNumMsg
+)
+/*++
+  Purpose:
+
+    Get the specified message.
+
+  Parameters:
+
+    [IN]  wNumMsg - the number of the requested message.
+
+  Return value:
+
+    Pointer to the requested message. NULL if not found.
+
+--*/
+{
+   static char    buf[256];
+   DWORD          dwOffset, dwSize;
+
+   if (wNumMsg >= g_TextLib.nMsgs)
+   {
+      return NULL;
+   }
+
+   dwOffset = SWAP32(g_TextLib.lpMsgOffset[wNumMsg]);
+   dwSize = SWAP32(g_TextLib.lpMsgOffset[wNumMsg + 1]) - dwOffset;
+   assert(dwSize < 255);
+
+   memcpy(buf, &g_TextLib.lpMsgBuf[dwOffset], dwSize);
+   buf[dwSize] = '\0';
+
+   return buf;
+}
+
+VOID
+PAL_DrawText(
+   LPCSTR     lpszText,
+   PAL_POS    pos,
+   BYTE       bColor,
+   BOOL       fShadow,
+   BOOL       fUpdate
+)
+/*++
+  Purpose:
+
+    Draw text on the screen.
+
+  Parameters:
+
+    [IN]  lpszText - the text to be drawn.
+
+    [IN]  pos - Position of the text.
+
+    [IN]  bColor - Color of the text.
+
+    [IN]  fShadow - TRUE if the text is shadowed or not.
+
+    [IN]  fUpdate - TRUE if update the screen area.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   SDL_Rect   rect, urect;
+   WORD       wChar;
+
+   rect.x = PAL_X(pos);
+   rect.y = PAL_Y(pos);
+
+   urect.x = rect.x;
+   urect.y = rect.y;
+   urect.h = 16;
+   urect.w = 0;
+
+   while (*lpszText)
+   {
+      //
+      // Draw the character
+      //
+      if (*lpszText & 0x80)
+      {
+         //
+         // BIG-5 Chinese Character
+         //
+         wChar = SWAP16(((LPBYTE)lpszText)[0] | (((LPBYTE)lpszText)[1] << 8));
+         if (fShadow)
+         {
+            PAL_DrawCharOnSurface(wChar, gpScreen, PAL_XY(rect.x + 1, rect.y + 1), 0);
+            PAL_DrawCharOnSurface(wChar, gpScreen, PAL_XY(rect.x + 1, rect.y), 0);
+         }
+         PAL_DrawCharOnSurface(wChar, gpScreen, PAL_XY(rect.x, rect.y), bColor);
+         lpszText += 2;
+         rect.x += 16;
+         urect.w += 16;
+      }
+      else
+      {
+         //
+         // ASCII character
+         //
+         if (fShadow)
+         {
+            PAL_DrawASCIICharOnSurface(*lpszText, gpScreen, PAL_XY(rect.x + 1, rect.y + 1), 0);
+            PAL_DrawASCIICharOnSurface(*lpszText, gpScreen, PAL_XY(rect.x + 1, rect.y), 0);
+         }
+         PAL_DrawASCIICharOnSurface(*lpszText, gpScreen, PAL_XY(rect.x, rect.y), bColor);
+         lpszText++;
+         rect.x += 8;
+         urect.w += 8;
+      }
+   }
+
+   //
+   // Update the screen area
+   //
+   if (fUpdate && urect.w > 0)
+   {
+      VIDEO_UpdateScreen(&urect);
+   }
+}
+
+VOID
+PAL_DialogSetDelayTime(
+   INT          iDelayTime
+)
+/*++
+  Purpose:
+
+    Set the delay time for dialog.
+
+  Parameters:
+
+    [IN]  iDelayTime - the delay time to be set.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   g_TextLib.iDelayTime = iDelayTime;
+}
+
+VOID
+PAL_StartDialog(
+   BYTE         bDialogLocation,
+   BYTE         bFontColor,
+   INT          iNumCharFace,
+   BOOL         fPlayingRNG
+)
+/*++
+  Purpose:
+
+    Start a new dialog.
+
+  Parameters:
+
+    [IN]  bDialogLocation - the location of the text on the screen.
+
+    [IN]  bFontColor - the font color of the text.
+
+    [IN]  iNumCharFace - number of the character face in RGM.MKF.
+
+    [IN]  fPlayingRNG - whether we are playing a RNG video or not.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   PAL_LARGE BYTE buf[16384];
+   SDL_Rect       rect;
+
+   if (gpGlobals->fInBattle && !g_fUpdatedInBattle)
+   {
+      //
+      // Update the screen in battle, or the graphics may seem messed up
+      //
+      VIDEO_UpdateScreen(NULL);
+      g_fUpdatedInBattle = TRUE;
+   }
+
+   g_TextLib.bIcon = 0;
+   g_TextLib.posIcon = 0;
+   g_TextLib.nCurrentDialogLine = 0;
+   g_TextLib.posDialogTitle = PAL_XY(12, 8);
+   g_TextLib.fUserSkip = FALSE;
+
+   if (bFontColor != 0)
+   {
+      g_TextLib.bCurrentFontColor = bFontColor;
+   }
+
+   if (fPlayingRNG && iNumCharFace)
+   {
+      VIDEO_BackupScreen();
+      g_TextLib.fPlayingRNG = TRUE;
+   }
+
+   switch (bDialogLocation)
+   {
+   case kDialogUpper:
+      if (iNumCharFace > 0)
+      {
+         //
+         // Display the character face at the upper part of the screen
+         //
+         if (PAL_MKFReadChunk(buf, 16384, iNumCharFace, gpGlobals->f.fpRGM) > 0)
+         {
+            rect.w = PAL_RLEGetWidth((LPCBITMAPRLE)buf);
+            rect.h = PAL_RLEGetHeight((LPCBITMAPRLE)buf);
+            rect.x = 48 - rect.w / 2;
+            rect.y = 55 - rect.h / 2;
+
+            if (rect.x < 0)
+            {
+               rect.x = 0;
+            }
+
+            if (rect.y < 0)
+            {
+               rect.y = 0;
+            }
+
+            PAL_RLEBlitToSurface((LPCBITMAPRLE)buf, gpScreen, PAL_XY(rect.x, rect.y));
+
+            if (rect.x < 0)
+            {
+               rect.x = 0;
+            }
+            if (rect.y < 0)
+            {
+               rect.y = 0;
+            }
+
+            VIDEO_UpdateScreen(&rect);
+         }
+      }
+      g_TextLib.posDialogTitle = PAL_XY(iNumCharFace > 0 ? 80 : 12, 8);
+      g_TextLib.posDialogText = PAL_XY(iNumCharFace > 0 ? 96 : 44, 26);
+      break;
+
+   case kDialogCenter:
+      g_TextLib.posDialogText = PAL_XY(80, 40);
+      break;
+
+   case kDialogLower:
+      if (iNumCharFace > 0)
+      {
+         //
+         // Display the character face at the lower part of the screen
+         //
+         if (PAL_MKFReadChunk(buf, 16384, iNumCharFace, gpGlobals->f.fpRGM) > 0)
+         {
+            rect.x = 270 - PAL_RLEGetWidth((LPCBITMAPRLE)buf) / 2;
+            rect.y = 144 - PAL_RLEGetHeight((LPCBITMAPRLE)buf) / 2;
+
+            PAL_RLEBlitToSurface((LPCBITMAPRLE)buf, gpScreen, PAL_XY(rect.x, rect.y));
+
+            VIDEO_UpdateScreen(NULL);
+         }
+      }
+      g_TextLib.posDialogTitle = PAL_XY(iNumCharFace > 0 ? 4 : 12, 108);
+      g_TextLib.posDialogText = PAL_XY(iNumCharFace > 0 ? 20 : 44, 126);
+      break;
+
+   case kDialogCenterWindow:
+      g_TextLib.posDialogText = PAL_XY(160, 40);
+      break;
+   }
+
+   g_TextLib.bDialogPosition = bDialogLocation;
+}
+
+static VOID
+PAL_DialogWaitForKey(
+   VOID
+)
+/*++
+  Purpose:
+
+    Wait for player to press a key after showing a dialog.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   PAL_LARGE SDL_Color   palette[256];
+   SDL_Color   *pCurrentPalette, t;
+   int         i;
+
+   //
+   // get the current palette
+   //
+   pCurrentPalette = PAL_GetPalette(gpGlobals->wNumPalette, gpGlobals->fNightPalette);
+   memcpy(palette, pCurrentPalette, sizeof(palette));
+
+   if (g_TextLib.bDialogPosition != kDialogCenterWindow &&
+      g_TextLib.bDialogPosition != kDialogCenter)
+   {
+      //
+      // show the icon
+      //
+      LPCBITMAPRLE p = PAL_SpriteGetFrame(g_TextLib.bufDialogIcons, g_TextLib.bIcon);
+      if (p != NULL)
+      {
+         SDL_Rect rect;
+
+         rect.x = PAL_X(g_TextLib.posIcon);
+         rect.y = PAL_Y(g_TextLib.posIcon);
+         rect.w = 16;
+         rect.h = 16;
+
+         PAL_RLEBlitToSurface(p, gpScreen, g_TextLib.posIcon);
+         VIDEO_UpdateScreen(&rect);
+      }
+   }
+
+   PAL_ClearKeyState();
+
+   while (TRUE)
+   {
+      UTIL_Delay(100);
+
+      if (g_TextLib.bDialogPosition != kDialogCenterWindow &&
+         g_TextLib.bDialogPosition != kDialogCenter)
+      {
+         //
+         // palette shift
+         //
+         t = palette[0xF9];
+         for (i = 0xF9; i < 0xFE; i++)
+         {
+            palette[i] = palette[i + 1];
+         }
+         palette[0xFE] = t;
+
+         VIDEO_SetPalette(palette);
+      }
+
+      if (g_InputState.dwKeyPress != 0)
+      {
+         break;
+      }
+   }
+
+   if (g_TextLib.bDialogPosition != kDialogCenterWindow &&
+      g_TextLib.bDialogPosition != kDialogCenter)
+   {
+      PAL_SetPalette(gpGlobals->wNumPalette, gpGlobals->fNightPalette);
+   }
+
+   PAL_ClearKeyState();
+
+   g_TextLib.fUserSkip = FALSE;
+}
+
+VOID
+PAL_ShowDialogText(
+   LPCSTR       lpszText
+)
+/*++
+  Purpose:
+
+    Show one line of the dialog text.
+
+  Parameters:
+
+    [IN]  lpszText - the text to be shown.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   SDL_Rect        rect;
+   int             x, y, len = strlen(lpszText);
+
+   PAL_ClearKeyState();
+   g_TextLib.bIcon = 0;
+
+   if (gpGlobals->fInBattle && !g_fUpdatedInBattle)
+   {
+      //
+      // Update the screen in battle, or the graphics may seem messed up
+      //
+      VIDEO_UpdateScreen(NULL);
+      g_fUpdatedInBattle = TRUE;
+   }
+
+   if (g_TextLib.nCurrentDialogLine > 3)
+   {
+      //
+      // The rest dialogs should be shown in the next page.
+      //
+      PAL_DialogWaitForKey();
+      g_TextLib.nCurrentDialogLine = 0;
+      VIDEO_RestoreScreen();
+      VIDEO_UpdateScreen(NULL);
+   }
+
+   x = PAL_X(g_TextLib.posDialogText);
+   y = PAL_Y(g_TextLib.posDialogText) + g_TextLib.nCurrentDialogLine * 18;
+
+   if (g_TextLib.bDialogPosition == kDialogCenterWindow)
+   {
+      //
+      // The text should be shown in a small window at the center of the screen
+      //
+#ifndef PAL_CLASSIC
+      if (gpGlobals->fInBattle && g_Battle.BattleResult == kBattleResultOnGoing)
+      {
+         PAL_BattleUIShowText(lpszText, 1400);
+      }
+      else
+#endif
+      {
+         PAL_POS    pos;
+         LPBOX      lpBox;
+
+         //
+         // Create the window box
+         //
+         pos = PAL_XY(PAL_X(g_TextLib.posDialogText) - len * 4, PAL_Y(g_TextLib.posDialogText));
+         lpBox = PAL_CreateSingleLineBox(pos, (len + 1) / 2, TRUE);
+
+         rect.x = PAL_X(pos);
+         rect.y = PAL_Y(pos);
+         rect.w = 320 - rect.x * 2 + 32;
+         rect.h = 64;
+
+         //
+         // Show the text on the screen
+         //
+         pos = PAL_XY(PAL_X(pos) + 8 + ((len & 1) << 2), PAL_Y(pos) + 10);
+         PAL_DrawText(lpszText, pos, 0, FALSE, FALSE);
+         VIDEO_UpdateScreen(&rect);
+
+         PAL_DialogWaitForKey();
+
+         //
+         // Delete the box
+         //
+         PAL_DeleteBox(lpBox);
+         VIDEO_UpdateScreen(&rect);
+
+         PAL_EndDialog();
+      }
+   }
+   else
+   {
+      if (g_TextLib.nCurrentDialogLine == 0 &&
+         g_TextLib.bDialogPosition != kDialogCenter &&
+         (BYTE)lpszText[len - 1] == 0x47 && (BYTE)lpszText[len - 2] == 0xA1)
+      {
+         //
+         // name of character
+         //
+         PAL_DrawText(lpszText, g_TextLib.posDialogTitle, FONT_COLOR_CYAN_ALT, TRUE, TRUE);
+      }
+      else
+      {
+         //
+         // normal texts
+         //
+         char text[3];
+
+         if (!g_TextLib.fPlayingRNG && g_TextLib.nCurrentDialogLine == 0)
+         {
+            //
+            // Save the screen before we show the first line of dialog
+            //
+            VIDEO_BackupScreen();
+         }
+
+         while (lpszText != NULL && *lpszText != '\0')
+         {
+            switch (*lpszText)
+            {
+            case '-':
+               //
+               // Set the font color to Cyan
+               //
+               if (g_TextLib.bCurrentFontColor == FONT_COLOR_CYAN)
+               {
+                  g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT;
+               }
+               else
+               {
+                  g_TextLib.bCurrentFontColor = FONT_COLOR_CYAN;
+               }
+               lpszText++;
+               break;
+
+            case '\'':
+               //
+               // Set the font color to Red
+               //
+               if (g_TextLib.bCurrentFontColor == FONT_COLOR_RED)
+               {
+                  g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT;
+               }
+               else
+               {
+                  g_TextLib.bCurrentFontColor = FONT_COLOR_RED;
+               }
+               lpszText++;
+               break;
+
+            case '\"':
+               //
+               // Set the font color to Yellow
+               //
+               if (g_TextLib.bCurrentFontColor == FONT_COLOR_YELLOW)
+               {
+                  g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT;
+               }
+               else
+               {
+                  g_TextLib.bCurrentFontColor = FONT_COLOR_YELLOW;
+               }
+               lpszText++;
+               break;
+
+            case '$':
+               //
+               // Set the delay time of text-displaying
+               //
+               g_TextLib.iDelayTime = atoi(lpszText + 1) * 10 / 7;
+               lpszText += 3;
+               break;
+
+            case '~':
+               //
+               // Delay for a period and quit
+               //
+               UTIL_Delay(atoi(lpszText + 1) * 80 / 7);
+               g_TextLib.nCurrentDialogLine = 0;
+               g_TextLib.fUserSkip = FALSE;
+               return; // don't go further
+
+            case ')':
+               //
+               // Set the waiting icon
+               //
+               g_TextLib.bIcon = 1;
+               lpszText++;
+               break;
+
+            case '(':
+               //
+               // Set the waiting icon
+               //
+               g_TextLib.bIcon = 2;
+               lpszText++;
+               break;
+
+            case '\\':
+               lpszText++;
+
+            default:
+               if (*lpszText & 0x80)
+               {
+                  text[0] = lpszText[0];
+                  text[1] = lpszText[1];
+                  text[2] = '\0';
+                  lpszText += 2;
+               }
+               else
+               {
+                  text[0] = *lpszText;
+                  text[1] = '\0';
+                  lpszText++;
+               }
+
+               PAL_DrawText(text, PAL_XY(x, y), g_TextLib.bCurrentFontColor, TRUE, TRUE);
+               x += ((text[0] & 0x80) ? 16 : 8);
+
+               if (!g_TextLib.fUserSkip)
+               {
+                  PAL_ClearKeyState();
+                  UTIL_Delay(g_TextLib.iDelayTime * 8);
+
+                  if (g_InputState.dwKeyPress & (kKeySearch | kKeyMenu))
+                  {
+                     //
+                     // User pressed a key to skip the dialog
+                     //
+                     g_TextLib.fUserSkip = TRUE;
+                  }
+               }
+            }
+         }
+
+         g_TextLib.posIcon = PAL_XY(x, y);
+         g_TextLib.nCurrentDialogLine++;
+      }
+   }
+}
+
+VOID
+PAL_ClearDialog(
+   BOOL       fWaitForKey
+)
+/*++
+  Purpose:
+
+    Clear the state of the dialog.
+
+  Parameters:
+
+    [IN]  fWaitForKey - whether wait for any key or not.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   if (g_TextLib.nCurrentDialogLine > 0 && fWaitForKey)
+   {
+      PAL_DialogWaitForKey();
+   }
+
+   g_TextLib.nCurrentDialogLine = 0;
+
+   if (g_TextLib.bDialogPosition == kDialogCenter)
+   {
+      g_TextLib.posDialogTitle = PAL_XY(12, 8);
+      g_TextLib.posDialogText = PAL_XY(44, 26);
+      g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT;
+      g_TextLib.bDialogPosition = kDialogUpper;
+   }
+}
+
+VOID
+PAL_EndDialog(
+   VOID
+)
+/*++
+  Purpose:
+
+    Ends a dialog.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   PAL_ClearDialog(TRUE);
+
+   //
+   // Set some default parameters, as there are some parts of script
+   // which doesn't have a "start dialog" instruction before showing the dialog.
+   //
+   g_TextLib.posDialogTitle = PAL_XY(12, 8);
+   g_TextLib.posDialogText = PAL_XY(44, 26);
+   g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT;
+   g_TextLib.bDialogPosition = kDialogUpper;
+   g_TextLib.fUserSkip = FALSE;
+   g_TextLib.fPlayingRNG = FALSE;
+}
+
+BOOL
+PAL_IsInDialog(
+   VOID
+)
+/*++
+  Purpose:
+
+    Check if there are dialog texts on the screen.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    TRUE if there are dialog texts on the screen, FALSE if not.
+
+--*/
+{
+   return (g_TextLib.nCurrentDialogLine != 0);
+}
+
+BOOL
+PAL_DialogIsPlayingRNG(
+   VOID
+)
+/*++
+  Purpose:
+
+    Check if the script used the RNG playing parameter when displaying texts.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    TRUE if the script used the RNG playing parameter, FALSE if not.
+
+--*/
+{
+   return g_TextLib.fPlayingRNG;
+}

+ 101 - 0
text.h

@@ -0,0 +1,101 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef _TEXT_H
+#define _TEXT_H
+
+typedef enum tagDIALOGPOSITION
+{
+   kDialogUpper       = 0,
+   kDialogCenter,
+   kDialogLower,
+   kDialogCenterWindow
+} DIALOGLOCATION;
+
+#define PAL_ADDITIONAL_WORD_FIRST           10000
+
+INT
+PAL_InitText(
+   VOID
+);
+
+VOID
+PAL_FreeText(
+   VOID
+);
+
+LPCSTR
+PAL_GetWord(
+   WORD       wNumWord
+);
+
+LPCSTR
+PAL_GetMsg(
+   WORD       wNumMsg
+);
+
+VOID
+PAL_DrawText(
+   LPCSTR     lpszText,
+   PAL_POS    pos,
+   BYTE       bColor,
+   BOOL       fShadow,
+   BOOL       fUpdate
+);
+
+VOID
+PAL_DialogSetDelayTime(
+   INT          iDelayTime
+);
+
+VOID
+PAL_StartDialog(
+   BYTE         bDialogLocation,
+   BYTE         bFontColor,
+   INT          iNumCharFace,
+   BOOL         fPlayingRNG
+);
+
+VOID
+PAL_ShowDialogText(
+   LPCSTR       szText
+);
+
+VOID
+PAL_ClearDialog(
+   BOOL         fWaitForKey
+);
+
+VOID
+PAL_EndDialog(
+   VOID
+);
+
+BOOL
+PAL_IsInDialog(
+   VOID
+);
+
+BOOL
+PAL_DialogIsPlayingRNG(
+   VOID
+);
+
+#endif

+ 797 - 0
ui.c

@@ -0,0 +1,797 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "main.h"
+
+LPSPRITE      gpSpriteUI = NULL;
+
+INT
+PAL_InitUI(
+   VOID
+)
+/*++
+  Purpose:
+
+    Initialze the UI subsystem.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    0 = success, -1 = fail.
+
+--*/
+{
+   int        iSize;
+
+   //
+   // Load the UI sprite.
+   //
+   iSize = PAL_MKFGetChunkSize(CHUNKNUM_SPRITEUI, gpGlobals->f.fpDATA);
+   if (iSize < 0)
+   {
+      return -1;
+   }
+
+   gpSpriteUI = (LPSPRITE)calloc(1, iSize);
+   if (gpSpriteUI == NULL)
+   {
+      return -1;
+   }
+
+   PAL_MKFReadChunk(gpSpriteUI, iSize, CHUNKNUM_SPRITEUI, gpGlobals->f.fpDATA);
+
+   return 0;
+}
+
+VOID
+PAL_FreeUI(
+   VOID
+)
+/*++
+  Purpose:
+
+    Shutdown the UI subsystem.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   if (gpSpriteUI != NULL)
+   {
+      free(gpSpriteUI);
+      gpSpriteUI = NULL;
+   }
+}
+
+LPBOX
+PAL_CreateBox(
+   PAL_POS        pos,
+   INT            nRows,
+   INT            nColumns,
+   INT            iStyle,
+   BOOL           fSaveScreen
+)
+/*++
+  Purpose:
+
+    Create a box on the screen.
+
+  Parameters:
+
+    [IN]  pos - position of the box.
+
+    [IN]  nRows - number of rows of the box.
+
+    [IN]  nColumns - number of columns of the box.
+
+    [IN]  iStyle - style of the box (0 or 1).
+
+    [IN]  fSaveScreen - whether save the used screen area or not.
+
+  Return value:
+
+    Pointer to a BOX structure. NULL if failed.
+    If fSaveScreen is false, then always returns NULL.
+
+--*/
+{
+   int              i, j, x, m, n;
+   LPCBITMAPRLE     rglpBorderBitmap[3][3];
+   LPBOX            lpBox = NULL;
+   SDL_Surface     *save;
+   SDL_Rect         rect;
+
+   //
+   // Get the bitmaps
+   //
+   for (i = 0; i < 3; i++)
+   {
+      for (j = 0; j < 3; j++)
+      {
+         rglpBorderBitmap[i][j] = PAL_SpriteGetFrame(gpSpriteUI, i * 3 + j + iStyle * 9);
+      }
+   }
+
+   rect.x = PAL_X(pos);
+   rect.y = PAL_Y(pos);
+   rect.w = 0;
+   rect.h = 0;
+
+   //
+   // Get the total width and total height of the box
+   //
+   for (i = 0; i < 3; i++)
+   {
+      if (i == 1)
+      {
+         rect.w += PAL_RLEGetWidth(rglpBorderBitmap[0][i]) * nColumns;
+         rect.h += PAL_RLEGetHeight(rglpBorderBitmap[i][0]) * nRows;
+      }
+      else
+      {
+         rect.w += PAL_RLEGetWidth(rglpBorderBitmap[0][i]);
+         rect.h += PAL_RLEGetHeight(rglpBorderBitmap[i][0]);
+      }
+   }
+
+   if (fSaveScreen)
+   {
+      //
+      // Save the used part of the screen
+      //
+      save = SDL_CreateRGBSurface(gpScreen->flags, rect.w, rect.h, 8,
+         gpScreen->format->Rmask, gpScreen->format->Gmask,
+         gpScreen->format->Bmask, gpScreen->format->Amask);
+
+      if (save == NULL)
+      {
+         return NULL;
+      }
+
+      lpBox = (LPBOX)calloc(1, sizeof(BOX));
+      if (lpBox == NULL)
+      {
+         SDL_FreeSurface(save);
+         return NULL;
+      }
+
+      SDL_BlitSurface(gpScreen, &rect, save, NULL);
+
+      lpBox->lpSavedArea = save;
+      lpBox->pos = pos;
+      lpBox->wWidth = rect.w;
+      lpBox->wHeight = rect.h;
+   }
+
+   //
+   // Border takes 2 additional rows and columns...
+   //
+   nRows += 2;
+   nColumns += 2;
+
+   //
+   // Draw the box
+   //
+   for (i = 0; i < nRows; i++)
+   {
+      x = rect.x;
+      m = (i == 0) ? 0 : ((i == nRows - 1) ? 2 : 1);
+
+      for (j = 0; j < nColumns; j++)
+      {
+         n = (j == 0) ? 0 : ((j == nColumns - 1) ? 2 : 1);
+         PAL_RLEBlitToSurface(rglpBorderBitmap[m][n], gpScreen, PAL_XY(x, rect.y));
+         x += PAL_RLEGetWidth(rglpBorderBitmap[m][n]);
+      }
+
+      rect.y += PAL_RLEGetHeight(rglpBorderBitmap[m][0]);
+   }
+
+   return lpBox;
+}
+
+LPBOX
+PAL_CreateSingleLineBox(
+   PAL_POS        pos,
+   INT            nLen,
+   BOOL           fSaveScreen
+)
+/*++
+  Purpose:
+
+    Create a single-line box on the screen.
+
+  Parameters:
+
+    [IN]  pos - position of the box.
+
+    [IN]  nLen - length of the box.
+
+    [IN]  fSaveScreen - whether save the used screen area or not.
+
+  Return value:
+
+    Pointer to a BOX structure. NULL if failed.
+    If fSaveScreen is false, then always returns NULL.
+
+--*/
+{
+   static const int      iNumLeftSprite   = 44;
+   static const int      iNumMidSprite    = 45;
+   static const int      iNumRightSprite  = 46;
+
+   LPCBITMAPRLE          lpBitmapLeft;
+   LPCBITMAPRLE          lpBitmapMid;
+   LPCBITMAPRLE          lpBitmapRight;
+   SDL_Surface          *save;
+   SDL_Rect              rect;
+   LPBOX                 lpBox = NULL;
+   int                   i;
+
+   //
+   // Get the bitmaps
+   //
+   lpBitmapLeft = PAL_SpriteGetFrame(gpSpriteUI, iNumLeftSprite);
+   lpBitmapMid = PAL_SpriteGetFrame(gpSpriteUI, iNumMidSprite);
+   lpBitmapRight = PAL_SpriteGetFrame(gpSpriteUI, iNumRightSprite);
+
+   rect.x = PAL_X(pos);
+   rect.y = PAL_Y(pos);
+
+   //
+   // Get the total width and total height of the box
+   //
+   rect.w = PAL_RLEGetWidth(lpBitmapLeft) + PAL_RLEGetWidth(lpBitmapRight);
+   rect.w += PAL_RLEGetWidth(lpBitmapMid) * nLen;
+   rect.h = PAL_RLEGetHeight(lpBitmapLeft);
+
+   if (fSaveScreen)
+   {
+      //
+      // Save the used part of the screen
+      //
+      save = SDL_CreateRGBSurface(gpScreen->flags, rect.w, rect.h, 8,
+         gpScreen->format->Rmask, gpScreen->format->Gmask,
+         gpScreen->format->Bmask, gpScreen->format->Amask);
+
+      if (save == NULL)
+      {
+         return NULL;
+      }
+
+      lpBox = (LPBOX)calloc(1, sizeof(BOX));
+      if (lpBox == NULL)
+      {
+         SDL_FreeSurface(gpScreen);
+         return NULL;
+      }
+
+      SDL_BlitSurface(gpScreen, &rect, save, NULL);
+
+      lpBox->pos = pos;
+      lpBox->lpSavedArea = save;
+      lpBox->wHeight = (WORD)rect.w;
+      lpBox->wWidth = (WORD)rect.h;
+   }
+
+   //
+   // Draw the box
+   //
+   PAL_RLEBlitToSurface(lpBitmapLeft, gpScreen, pos);
+
+   rect.x += PAL_RLEGetWidth(lpBitmapLeft);
+
+   for (i = 0; i < nLen; i++)
+   {
+      PAL_RLEBlitToSurface(lpBitmapMid, gpScreen, PAL_XY(rect.x, rect.y));
+      rect.x += PAL_RLEGetWidth(lpBitmapMid);
+   }
+
+   PAL_RLEBlitToSurface(lpBitmapRight, gpScreen, PAL_XY(rect.x, rect.y));
+
+   return lpBox;
+}
+
+VOID
+PAL_DeleteBox(
+   LPBOX          lpBox
+)
+/*++
+  Purpose:
+
+    Delete a box and restore the saved part of the screen.
+
+  Parameters:
+
+    [IN]  lpBox - pointer to the BOX struct.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   SDL_Rect        rect;
+
+   //
+   // Check for NULL pointer.
+   //
+   if (lpBox == NULL)
+   {
+      return;
+   }
+
+   //
+   // Restore the saved screen part
+   //
+   rect.x = PAL_X(lpBox->pos);
+   rect.y = PAL_Y(lpBox->pos);
+   rect.w = lpBox->wWidth;
+   rect.h = lpBox->wHeight;
+
+   SDL_BlitSurface(lpBox->lpSavedArea, NULL, gpScreen, &rect);
+
+   //
+   // Free the memory used by the box
+   //
+   SDL_FreeSurface(lpBox->lpSavedArea);
+   free(lpBox);
+}
+
+WORD
+PAL_ReadMenu(
+   LPITEMCHANGED_CALLBACK    lpfnMenuItemChanged,
+   LPMENUITEM                rgMenuItem,
+   INT                       nMenuItem,
+   WORD                      wDefaultItem,
+   BYTE                      bLabelColor
+)
+/*++
+  Purpose:
+
+    Execute a menu.
+
+  Parameters:
+
+    [IN]  lpfnMenuItemChanged - Callback function which is called when user
+                                changed the current menu item.
+
+    [IN]  rgMenuItem - Array of the menu items.
+
+    [IN]  nMenuItem - Number of menu items.
+
+    [IN]  wDefaultItem - default item index.
+
+    [IN]  bLabelColor - color of the labels.
+
+  Return value:
+
+    Return value of the selected menu item. MENUITEM_VALUE_CANCELLED if cancelled.
+
+--*/
+{
+   int               i;
+   WORD              wCurrentItem    = (wDefaultItem < nMenuItem) ? wDefaultItem : 0;
+
+   //
+   // Draw all the menu texts.
+   //
+   for (i = 0; i < nMenuItem; i++)
+   {
+      BYTE bColor = bLabelColor;
+
+      if (!rgMenuItem[i].fEnabled)
+      {
+         if (i == wCurrentItem)
+         {
+            bColor = MENUITEM_COLOR_SELECTED_INACTIVE;
+         }
+         else
+         {
+            bColor = MENUITEM_COLOR_INACTIVE;
+         }
+      }
+
+      PAL_DrawText(PAL_GetWord(rgMenuItem[i].wNumWord), rgMenuItem[i].pos,
+         bColor, TRUE, TRUE);
+   }
+
+   if (lpfnMenuItemChanged != NULL)
+   {
+      (*lpfnMenuItemChanged)(rgMenuItem[wDefaultItem].wValue);
+   }
+
+   while (TRUE)
+   {
+      PAL_ClearKeyState();
+
+      //
+      // Redraw the selected item if needed.
+      //
+      if (rgMenuItem[wCurrentItem].fEnabled)
+      {
+         PAL_DrawText(PAL_GetWord(rgMenuItem[wCurrentItem].wNumWord),
+            rgMenuItem[wCurrentItem].pos, MENUITEM_COLOR_SELECTED, FALSE, TRUE);
+      }
+
+      PAL_ProcessEvent();
+
+      if (g_InputState.dwKeyPress & (kKeyDown | kKeyRight))
+      {
+         //
+         // User pressed the down or right arrow key
+         //
+         if (rgMenuItem[wCurrentItem].fEnabled)
+         {
+            //
+            // Dehighlight the unselected item.
+            //
+            PAL_DrawText(PAL_GetWord(rgMenuItem[wCurrentItem].wNumWord),
+               rgMenuItem[wCurrentItem].pos, bLabelColor, FALSE, TRUE);
+         }
+         else
+         {
+            PAL_DrawText(PAL_GetWord(rgMenuItem[wCurrentItem].wNumWord),
+               rgMenuItem[wCurrentItem].pos, MENUITEM_COLOR_INACTIVE, FALSE, TRUE);
+         }
+
+         wCurrentItem++;
+
+         if (wCurrentItem >= nMenuItem)
+         {
+            wCurrentItem = 0;
+         }
+
+         //
+         // Highlight the selected item.
+         //
+         if (rgMenuItem[wCurrentItem].fEnabled)
+         {
+            PAL_DrawText(PAL_GetWord(rgMenuItem[wCurrentItem].wNumWord),
+               rgMenuItem[wCurrentItem].pos, MENUITEM_COLOR_SELECTED, FALSE, TRUE);
+         }
+         else
+         {
+            PAL_DrawText(PAL_GetWord(rgMenuItem[wCurrentItem].wNumWord),
+               rgMenuItem[wCurrentItem].pos, MENUITEM_COLOR_SELECTED_INACTIVE, FALSE, TRUE);
+         }
+
+         if (lpfnMenuItemChanged != NULL)
+         {
+            (*lpfnMenuItemChanged)(rgMenuItem[wCurrentItem].wValue);
+         }
+      }
+      else if (g_InputState.dwKeyPress & (kKeyUp | kKeyLeft))
+      {
+         //
+         // User pressed the up or left arrow key
+         //
+         if (rgMenuItem[wCurrentItem].fEnabled)
+         {
+            //
+            // Dehighlight the unselected item.
+            //
+            PAL_DrawText(PAL_GetWord(rgMenuItem[wCurrentItem].wNumWord),
+               rgMenuItem[wCurrentItem].pos, bLabelColor, FALSE, TRUE);
+         }
+         else
+         {
+            PAL_DrawText(PAL_GetWord(rgMenuItem[wCurrentItem].wNumWord),
+               rgMenuItem[wCurrentItem].pos, MENUITEM_COLOR_INACTIVE, FALSE, TRUE);
+         }
+
+         if (wCurrentItem > 0)
+         {
+            wCurrentItem--;
+         }
+         else
+         {
+            wCurrentItem = nMenuItem - 1;
+         }
+
+         //
+         // Highlight the selected item.
+         //
+         if (rgMenuItem[wCurrentItem].fEnabled)
+         {
+            PAL_DrawText(PAL_GetWord(rgMenuItem[wCurrentItem].wNumWord),
+               rgMenuItem[wCurrentItem].pos, MENUITEM_COLOR_SELECTED, FALSE, TRUE);
+         }
+         else
+         {
+            PAL_DrawText(PAL_GetWord(rgMenuItem[wCurrentItem].wNumWord),
+               rgMenuItem[wCurrentItem].pos, MENUITEM_COLOR_SELECTED_INACTIVE, FALSE, TRUE);
+         }
+
+         if (lpfnMenuItemChanged != NULL)
+         {
+            (*lpfnMenuItemChanged)(rgMenuItem[wCurrentItem].wValue);
+         }
+      }
+      else if (g_InputState.dwKeyPress & kKeyMenu)
+      {
+         //
+         // User cancelled
+         //
+         if (rgMenuItem[wCurrentItem].fEnabled)
+         {
+            PAL_DrawText(PAL_GetWord(rgMenuItem[wCurrentItem].wNumWord),
+               rgMenuItem[wCurrentItem].pos, bLabelColor, FALSE, TRUE);
+         }
+         else
+         {
+            PAL_DrawText(PAL_GetWord(rgMenuItem[wCurrentItem].wNumWord),
+               rgMenuItem[wCurrentItem].pos, MENUITEM_COLOR_INACTIVE, FALSE, TRUE);
+         }
+
+         break;
+      }
+      else if (g_InputState.dwKeyPress & kKeySearch)
+      {
+         //
+         // User pressed Enter
+         //
+         if (rgMenuItem[wCurrentItem].fEnabled)
+         {
+            PAL_DrawText(PAL_GetWord(rgMenuItem[wCurrentItem].wNumWord),
+               rgMenuItem[wCurrentItem].pos, MENUITEM_COLOR_CONFIRMED, FALSE, TRUE);
+
+            return rgMenuItem[wCurrentItem].wValue;
+         }
+      }
+
+      //
+      // Use delay function to avoid high CPU usage.
+      //
+      SDL_Delay(50);
+   }
+
+   return MENUITEM_VALUE_CANCELLED;
+}
+
+VOID
+PAL_DrawNumber(
+   UINT            iNum,
+   UINT            nLength,
+   PAL_POS         pos,
+   NUMCOLOR        color,
+   NUMALIGN        align
+)
+/*++
+  Purpose:
+
+    Draw the specified number with the bitmaps in the UI sprite.
+
+  Parameters:
+
+    [IN]  iNum - the number to be drawn.
+
+    [IN]  nLength - max. length of the number.
+
+    [IN]  pos - position on the screen.
+
+    [IN]  color - color of the number (yellow or blue).
+
+    [IN]  align - align mode of the number.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   UINT          nActualLength, i;
+   int           x, y;
+   LPCBITMAPRLE  rglpBitmap[10];
+
+   //
+   // Get the bitmaps. Blue starts from 29, Cyan from 56, Yellow from 19.
+   //
+   x = (color == kNumColorBlue) ? 29 : ((color == kNumColorCyan) ? 56 : 19);
+
+   for (i = 0; i < 10; i++)
+   {
+      rglpBitmap[i] = PAL_SpriteGetFrame(gpSpriteUI, (UINT)x + i);
+   }
+
+   i = iNum;
+   nActualLength = 0;
+
+   //
+   // Calculate the actual length of the number.
+   //
+   while (i > 0)
+   {
+      i /= 10;
+      nActualLength++;
+   }
+
+   if (nActualLength > nLength)
+   {
+      nActualLength = nLength;
+   }
+   else if (nActualLength == 0)
+   {
+      nActualLength = 1;
+   }
+
+   x = PAL_X(pos) - 6;
+   y = PAL_Y(pos);
+
+   switch (align)
+   {
+   case kNumAlignLeft:
+      x += 6 * nActualLength;
+      break;
+
+   case kNumAlignMid:
+      x += 3 * (nLength + nActualLength);
+      break;
+
+   case kNumAlignRight:
+      x += 6 * nLength;
+      break;
+   }
+
+   //
+   // Draw the number.
+   //
+   while (nActualLength-- > 0)
+   {
+      PAL_RLEBlitToSurface(rglpBitmap[iNum % 10], gpScreen, PAL_XY(x, y));
+      x -= 6;
+      iNum /= 10;
+   }
+}
+
+LPOBJECTDESC
+PAL_LoadObjectDesc(
+   LPCSTR         lpszFileName
+)
+/*++
+  Purpose:
+
+    Load the object description strings from file.
+
+  Parameters:
+
+    [IN]  lpszFileName - the filename to be loaded.
+
+  Return value:
+
+    Pointer to loaded data, in linked list form. NULL if unable to load.
+
+--*/
+{
+   FILE                      *fp;
+   PAL_LARGE char             buf[512];
+   char                      *p;
+   LPOBJECTDESC               lpDesc = NULL, pNew = NULL;
+   unsigned int               i;
+
+   fp = fopen(lpszFileName, "r");
+
+   if (fp == NULL)
+   {
+      return NULL;
+   }
+
+   //
+   // Load the description data
+   //
+   while (fgets(buf, 512, fp) != NULL)
+   {
+      p = strchr(buf, '=');
+      if (p == NULL)
+      {
+         continue;
+      }
+
+      *p = '\0';
+      p++;
+
+      pNew = UTIL_calloc(1, sizeof(OBJECTDESC));
+
+      sscanf(buf, "%x", &i);
+      pNew->wObjectID = i;
+      pNew->lpDesc = strdup(p);
+
+      pNew->next = lpDesc;
+      lpDesc = pNew;
+   }
+
+   fclose(fp);
+   return lpDesc;
+}
+
+VOID
+PAL_FreeObjectDesc(
+   LPOBJECTDESC   lpObjectDesc
+)
+/*++
+  Purpose:
+
+    Free the object description data.
+
+  Parameters:
+
+    [IN]  lpObjectDesc - the description data to be freed.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   LPOBJECTDESC    p;
+
+   while (lpObjectDesc != NULL)
+   {
+      p = lpObjectDesc->next;
+      free(lpObjectDesc->lpDesc);
+      free(lpObjectDesc);
+      lpObjectDesc = p;
+   }
+}
+
+LPCSTR
+PAL_GetObjectDesc(
+   LPOBJECTDESC   lpObjectDesc,
+   WORD           wObjectID
+)
+/*++
+  Purpose:
+
+    Get the object description string from the linked list.
+
+  Parameters:
+
+    [IN]  lpObjectDesc - the description data linked list.
+
+    [IN]  wObjectID - the object ID.
+
+  Return value:
+
+    The description string. NULL if the specified object ID
+    is not found.
+
+--*/
+{
+   while (lpObjectDesc != NULL)
+   {
+      if (lpObjectDesc->wObjectID == wObjectID)
+      {
+         return lpObjectDesc->lpDesc;
+      }
+
+      lpObjectDesc = lpObjectDesc->next;
+   }
+
+   return NULL;
+}

+ 232 - 0
ui.h

@@ -0,0 +1,232 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef UI_H
+#define UI_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "common.h"
+
+#define CHUNKNUM_SPRITEUI                  9
+
+#define MENUITEM_COLOR                     0x4F
+#define MENUITEM_COLOR_INACTIVE            0x1C
+#define MENUITEM_COLOR_CONFIRMED           0x2C
+#define MENUITEM_COLOR_SELECTED_INACTIVE   0x1F
+#define MENUITEM_COLOR_SELECTED_FIRST      0xF9
+#define MENUITEM_COLOR_SELECTED_TOTALNUM   6
+
+#define MENUITEM_COLOR_SELECTED                                    \
+   (MENUITEM_COLOR_SELECTED_FIRST +                                \
+      SDL_GetTicks() / (600 / MENUITEM_COLOR_SELECTED_TOTALNUM)    \
+      % MENUITEM_COLOR_SELECTED_TOTALNUM)
+
+#define MENUITEM_COLOR_EQUIPPEDITEM        0xC8
+
+#define DESCTEXT_COLOR                     0x2E
+
+#define MAINMENU_BACKGROUND_FBPNUM         60
+#define RIX_NUM_OPENINGMENU                4
+#define MAINMENU_LABEL_NEWGAME             7
+#define MAINMENU_LABEL_LOADGAME            8
+
+#define LOADMENU_LABEL_SLOT_FIRST          43
+
+#define CONFIRMMENU_LABEL_NO               19
+#define CONFIRMMENU_LABEL_YES              20
+
+#define CASH_LABEL                         21
+
+#define SWITCHMENU_LABEL_DISABLE           17
+#define SWITCHMENU_LABEL_ENABLE            18
+
+#define GAMEMENU_LABEL_STATUS              3
+#define GAMEMENU_LABEL_MAGIC               4
+#define GAMEMENU_LABEL_INVENTORY           5
+#define GAMEMENU_LABEL_SYSTEM              6
+
+#define SYSMENU_LABEL_SAVE                 11
+#define SYSMENU_LABEL_LOAD                 12
+#define SYSMENU_LABEL_MUSIC                13
+#define SYSMENU_LABEL_SOUND                14
+#define SYSMENU_LABEL_QUIT                 15
+#define SYSMENU_LABEL_BATTLEMODE           (PAL_ADDITIONAL_WORD_FIRST)
+
+#define BATTLESPEEDMENU_LABEL_1            (PAL_ADDITIONAL_WORD_FIRST + 1)
+#define BATTLESPEEDMENU_LABEL_2            (PAL_ADDITIONAL_WORD_FIRST + 2)
+#define BATTLESPEEDMENU_LABEL_3            (PAL_ADDITIONAL_WORD_FIRST + 3)
+#define BATTLESPEEDMENU_LABEL_4            (PAL_ADDITIONAL_WORD_FIRST + 4)
+#define BATTLESPEEDMENU_LABEL_5            (PAL_ADDITIONAL_WORD_FIRST + 5)
+
+#define INVMENU_LABEL_USE                  23
+#define INVMENU_LABEL_EQUIP                22
+
+#define STATUS_BACKGROUND_FBPNUM           0
+#define STATUS_LABEL_EXP                   2
+#define STATUS_LABEL_LEVEL                 48
+#define STATUS_LABEL_HP                    49
+#define STATUS_LABEL_MP                    50
+#define STATUS_LABEL_ATTACKPOWER           51
+#define STATUS_LABEL_MAGICPOWER            52
+#define STATUS_LABEL_RESISTANCE            53
+#define STATUS_LABEL_DEXTERITY             54
+#define STATUS_LABEL_FLEERATE              55
+#define STATUS_COLOR_EQUIPMENT             0xBE
+
+#define BUYMENU_LABEL_CURRENT              35
+#define SELLMENU_LABEL_PRICE               25
+
+#define SPRITENUM_SLASH                    39
+#define SPRITENUM_ITEMBOX                  70
+#define SPRITENUM_CURSOR_YELLOW            68
+#define SPRITENUM_CURSOR                   69
+#define SPRITENUM_PLAYERINFOBOX            18
+#define SPRITENUM_PLAYERFACE_FIRST         48
+
+#define EQUIPMENU_BACKGROUND_FBPNUM        1
+
+#define ITEMUSEMENU_COLOR_STATLABEL        0xBB
+
+#define BATTLEWIN_GETEXP_LABEL             30
+#define BATTLEWIN_BEATENEMY_LABEL          9
+#define BATTLEWIN_DOLLAR_LABEL             10
+#define BATTLEWIN_LEVELUP_LABEL            32
+#define BATTLEWIN_ADDMAGIC_LABEL           33
+#define BATTLEWIN_LEVELUP_LABEL_COLOR      0x39
+#define SPRITENUM_ARROW                    47
+
+#define BATTLE_LABEL_ESCAPEFAIL            31
+
+typedef struct tagBOX
+{
+   PAL_POS        pos;
+   WORD           wWidth, wHeight;
+   SDL_Surface   *lpSavedArea;
+} BOX, *LPBOX;
+
+typedef struct tagMENUITEM
+{
+   WORD          wValue;
+   WORD          wNumWord;
+   BOOL          fEnabled;
+   PAL_POS       pos;
+} MENUITEM, *LPMENUITEM;
+
+typedef struct tagOBJECTDESC
+{
+   WORD                        wObjectID;
+   LPSTR                       lpDesc;
+   struct tagOBJECTDESC       *next;
+} OBJECTDESC, *LPOBJECTDESC;
+
+typedef VOID (*LPITEMCHANGED_CALLBACK)(WORD);
+
+#define MENUITEM_VALUE_CANCELLED      0xFFFF
+
+typedef enum tagNUMCOLOR
+{
+   kNumColorYellow,
+   kNumColorBlue,
+   kNumColorCyan
+} NUMCOLOR;
+
+typedef enum tagNUMALIGN
+{
+   kNumAlignLeft,
+   kNumAlignMid,
+   kNumAlignRight
+} NUMALIGN;
+
+INT
+PAL_InitUI(
+   VOID
+);
+
+VOID
+PAL_FreeUI(
+   VOID
+);
+
+LPBOX
+PAL_CreateBox(
+   PAL_POS        pos,
+   INT            nRows,
+   INT            nColumns,
+   INT            iStyle,
+   BOOL           fSaveScreen
+);
+
+LPBOX
+PAL_CreateSingleLineBox(
+   PAL_POS        pos,
+   INT            nLen,
+   BOOL           fSaveScreen
+);
+
+VOID
+PAL_DeleteBox(
+   LPBOX          lpBox
+);
+
+WORD
+PAL_ReadMenu(
+   LPITEMCHANGED_CALLBACK    lpfnMenuItemChanged,
+   LPMENUITEM                rgMenuItem,
+   INT                       nMenuItem,
+   WORD                      wDefaultItem,
+   BYTE                      bLabelColor
+);
+
+VOID
+PAL_DrawNumber(
+   UINT            iNum,
+   UINT            nLength,
+   PAL_POS         pos,
+   NUMCOLOR        color,
+   NUMALIGN        align
+);
+
+LPOBJECTDESC
+PAL_LoadObjectDesc(
+   LPCSTR          lpszFileName
+);
+
+VOID
+PAL_FreeObjectDesc(
+   LPOBJECTDESC    lpObjectDesc
+);
+
+LPCSTR
+PAL_GetObjectDesc(
+   LPOBJECTDESC   lpObjectDesc,
+   WORD           wObjectID
+);
+
+extern LPSPRITE gpSpriteUI;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

File diff suppressed because it is too large
+ 1777 - 0
uibattle.c


+ 152 - 0
uibattle.h

@@ -0,0 +1,152 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef UIBATTLE_H
+#define UIBATTLE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "ui.h"
+
+typedef enum tagBATTLEUISTATE
+{
+   kBattleUIWait,
+   kBattleUISelectMove,
+   kBattleUISelectTargetEnemy,
+   kBattleUISelectTargetPlayer,
+   kBattleUISelectTargetEnemyAll,
+   kBattleUISelectTargetPlayerAll,
+} BATTLEUISTATE;
+
+typedef enum tagBATTLEMENUSTATE
+{
+   kBattleMenuMain,
+   kBattleMenuMagicSelect,
+   kBattleMenuUseItemSelect,
+   kBattleMenuThrowItemSelect,
+   kBattleMenuMisc,
+   kBattleMenuMiscItemSubMenu,
+} BATTLEMENUSTATE;
+
+typedef enum tagBATTLEUIACTION
+{
+   kBattleUIActionAttack,
+   kBattleUIActionMagic,
+   kBattleUIActionCoopMagic,
+   kBattleUIActionMisc,
+} BATTLEUIACTION;
+
+#define SPRITENUM_BATTLEICON_ATTACK      40
+#define SPRITENUM_BATTLEICON_MAGIC       41
+#define SPRITENUM_BATTLEICON_COOPMAGIC   42
+#define SPRITENUM_BATTLEICON_MISCMENU    43
+
+#define SPRITENUM_BATTLE_ARROW_CURRENTPLAYER           69
+#define SPRITENUM_BATTLE_ARROW_CURRENTPLAYER_RED       68
+
+#define SPRITENUM_BATTLE_ARROW_SELECTEDPLAYER          67
+#define SPRITENUM_BATTLE_ARROW_SELECTEDPLAYER_RED      66
+
+#define BATTLEUI_LABEL_ITEM              5
+#define BATTLEUI_LABEL_DEFEND            58
+#define BATTLEUI_LABEL_AUTO              56
+#define BATTLEUI_LABEL_INVENTORY         57
+#define BATTLEUI_LABEL_FLEE              59
+#define BATTLEUI_LABEL_STATUS            60
+
+#define BATTLEUI_LABEL_USEITEM           23
+#define BATTLEUI_LABEL_THROWITEM         24
+
+#define TIMEMETER_COLOR_DEFAULT          0x1B
+#define TIMEMETER_COLOR_SLOW             0x5B
+#define TIMEMETER_COLOR_HASTE            0x2A
+
+#define BATTLEUI_MAX_SHOWNUM             16
+
+typedef struct tagSHOWNUM
+{
+   WORD             wNum;
+   PAL_POS          pos;
+   DWORD            dwTime;
+   NUMCOLOR         color;
+} SHOWNUM;
+
+typedef struct tagBATTLEUI
+{
+   BATTLEUISTATE    state;
+   BATTLEMENUSTATE  MenuState;
+
+   CHAR             szMsg[256];           // message to be shown on the screen
+   CHAR             szNextMsg[256];       // next message to be shown on the screen
+   DWORD            dwMsgShowTime;        // the end time of showing the message
+   WORD             wNextMsgDuration;     // duration of the next message
+
+   WORD             wCurPlayerIndex;      // index of the current player
+   WORD             wSelectedAction;      // current selected action
+   WORD             wSelectedIndex;       // current selected index of player or enemy
+   WORD             wPrevEnemyTarget;     // previous enemy target
+
+   WORD             wActionType;          // type of action to be performed
+   WORD             wObjectID;            // object ID of the item or magic to use
+
+   BOOL             fAutoAttack;          // TRUE if auto attack
+
+   SHOWNUM          rgShowNum[BATTLEUI_MAX_SHOWNUM];
+} BATTLEUI;
+
+VOID
+PAL_PlayerInfoBox(
+   PAL_POS         pos,
+   WORD            wPlayerRole,
+   INT             iTimeMeter,
+   BYTE            bTimeMeterColor,
+   BOOL            fUpdate
+);
+
+VOID
+PAL_BattleUIShowText(
+   LPCSTR        lpszText,
+   WORD          wDuration
+);
+
+VOID
+PAL_BattleUIPlayerReady(
+   WORD          wPlayerIndex
+);
+
+VOID
+PAL_BattleUIUpdate(
+   VOID
+);
+
+VOID
+PAL_BattleUIShowNum(
+   WORD           wNum,
+   PAL_POS        pos,
+   NUMCOLOR       color
+);
+
+#ifdef __cplusplus
+}
+#endif
+#endif

File diff suppressed because it is too large
+ 1918 - 0
uigame.c


+ 94 - 0
uigame.h

@@ -0,0 +1,94 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef UIGAME_H
+#define UIGAME_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "ui.h"
+
+VOID
+PAL_DrawOpeningMenuBackground(
+   VOID
+);
+
+INT
+PAL_OpeningMenu(
+   VOID
+);
+
+INT
+PAL_SaveSlotMenu(
+   WORD        wDefaultSlot
+);
+
+BOOL
+PAL_ConfirmMenu(
+   VOID
+);
+
+BOOL
+PAL_SwitchMenu(
+   BOOL      fEnabled
+);
+
+VOID
+PAL_InGameMagicMenu(
+   VOID
+);
+
+VOID
+PAL_InGameMenu(
+   VOID
+);
+
+VOID
+PAL_PlayerStatus(
+   VOID
+);
+
+WORD
+PAL_ItemUseMenu(
+   WORD           wItemToUse
+);
+
+VOID
+PAL_BuyMenu(
+   WORD           wStoreNum
+);
+
+VOID
+PAL_SellMenu(
+   VOID
+);
+
+VOID
+PAL_EquipItemMenu(
+   WORD           wItem
+);
+
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 488 - 0
util.c

@@ -0,0 +1,488 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// Portions Copyright (c) 2004, Pierre-Marie Baty.
+// Portions Copyright (c) 2009, netwan.
+//
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "util.h"
+
+#ifdef PAL_HAS_NATIVEMIDI
+#include "midi.h"
+#endif
+
+void
+trim(
+   char *str
+)
+/*++
+  Purpose:
+
+    Remove the leading and trailing spaces in a string.
+
+  Parameters:
+
+    str - the string to proceed.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   int pos = 0;
+   char *dest = str;
+
+   //
+   // skip leading blanks
+   //
+   while (str[pos] <= ' ' && str[pos] > 0)
+      pos++;
+
+   while (str[pos])
+   {
+      *(dest++) = str[pos];
+      pos++;
+   }
+
+   *(dest--) = '\0'; // store the null
+
+   //
+   // remove trailing blanks
+   //
+   while (dest >= str && *dest <= ' ' && *dest > 0)
+      *(dest--) = '\0';
+}
+
+char *
+va(
+   const char *format,
+   ...
+)
+/*++
+  Purpose:
+
+    Does a varargs printf into a temp buffer, so we don't need to have
+    varargs versions of all text functions.
+
+  Parameters:
+
+    format - the format string.
+
+  Return value:
+
+    Pointer to the result string.
+
+--*/
+{
+   static char string[256];
+   va_list     argptr;
+
+   va_start(argptr, format);
+   vsnprintf(string, 256, format, argptr);
+   va_end(argptr);
+
+   return string;
+}
+
+/*
+ * RNG code based on RACC by Pierre-Marie Baty.
+ * http://racc.bots-united.com
+ *
+ * Copyright (c) 2004, Pierre-Marie Baty
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the RACC nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+//
+// Our random number generator's seed.
+//
+static int glSeed = 0;
+
+static void
+lsrand(
+   unsigned int iInitialSeed
+)
+/*++
+  Purpose:
+
+    This function initializes the random seed based on the initial seed value passed in the
+    iInitialSeed parameter.
+
+  Parameters:
+
+    [IN]  iInitialSeed - The initial random seed.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   //
+   // fill in the initial seed of the random number generator
+   //
+   glSeed = 1664525L * iInitialSeed + 1013904223L;
+}
+
+static int
+lrand(
+   void
+)
+/*++
+  Purpose:
+
+    This function is the equivalent of the rand() standard C library function, except that
+    whereas rand() works only with short integers (i.e. not above 32767), this function is
+    able to generate 32-bit random numbers.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    The generated random number.
+
+--*/
+{
+   if (glSeed == 0) // if the random seed isn't initialized...
+      lsrand((unsigned int)time(NULL)); // initialize it first
+   glSeed = 1664525L * glSeed + 1013904223L; // do some twisted math (infinite suite)
+   return ((glSeed >> 1) + 1073741824L); // and return the result.
+}
+
+int
+RandomLong(
+   int from,
+   int to
+)
+/*++
+  Purpose:
+
+    This function returns a random integer number between (and including) the starting and
+    ending values passed by parameters from and to.
+
+  Parameters:
+
+    from - the starting value.
+
+    to - the ending value.
+
+  Return value:
+
+    The generated random number.
+
+--*/
+{
+   if (to <= from)
+      return from;
+
+   return from + lrand() / (INT_MAX / (to - from + 1));
+}
+
+float
+RandomFloat(
+   float from,
+   float to
+)
+/*++
+  Purpose:
+
+    This function returns a random floating-point number between (and including) the starting
+    and ending values passed by parameters from and to.
+
+  Parameters:
+
+    from - the starting value.
+
+    to - the ending value.
+
+  Return value:
+
+    The generated random number.
+
+--*/
+{
+   if (to <= from)
+      return from;
+
+   return from + (float)lrand() / (INT_MAX / (to - from));
+}
+
+void
+UTIL_Delay(
+   unsigned int ms
+)
+{
+   unsigned int t = SDL_GetTicks() + ms;
+
+   while (SDL_PollEvent(NULL));
+
+   while (SDL_GetTicks() < t)
+   {
+      SDL_Delay(1);
+      while (SDL_PollEvent(NULL));
+   }
+
+#ifdef PAL_HAS_NATIVEMIDI
+   MIDI_CheckLoop();
+#endif
+}
+
+void
+TerminateOnError(
+   const char *fmt,
+   ...
+)
+// This function terminates the game because of an error and
+// prints the message string pointed to by fmt both in the
+// console and in a messagebox.
+{
+   va_list argptr;
+   char string[256];
+   extern VOID PAL_Shutdown(VOID);
+
+   // concatenate all the arguments in one string
+   va_start(argptr, fmt);
+   vsnprintf(string, sizeof(string), fmt, argptr);
+   va_end(argptr);
+
+
+   fprintf(stderr, "\nFATAL ERROR: %s\n", string);
+
+#ifdef _WIN32
+   MessageBoxA(0, string, "FATAL ERROR", MB_ICONERROR);
+#endif
+
+#ifdef __linux__
+   system(va("beep; xmessage -center \"FATAL ERROR: %s\"", string));
+#endif
+
+#if defined(__SYMBIAN32__)
+   UTIL_WriteLog(LOG_DEBUG,"[0x%08x][%s][%s] - %s",(long)TerminateOnError,"TerminateOnError",__FILE__, string);
+   SDL_Delay(3000);
+#endif
+
+#ifdef _DEBUG
+   assert(!"TerminateOnError()"); // allows jumping to debugger
+#endif
+
+
+PAL_Shutdown();
+
+#if defined (NDS)
+   while (1);
+#else
+   exit(255);
+#endif
+}
+
+void *
+UTIL_malloc(
+   size_t               buffer_size
+)
+{
+   // handy wrapper for operations we always forget, like checking malloc's returned pointer.
+
+   void *buffer;
+
+   // first off, check if buffer size is valid
+   if (buffer_size == 0)
+      TerminateOnError("UTIL_malloc() called with invalid buffer size: %d\n", buffer_size);
+
+   buffer = malloc(buffer_size); // allocate real memory space
+
+   // last check, check if malloc call succeeded
+   if (buffer == NULL)
+      TerminateOnError("UTIL_malloc() failure for %d bytes (out of memory?)\n", buffer_size);
+
+   return buffer; // nothing went wrong, so return buffer pointer
+}
+
+void *
+UTIL_calloc(
+   size_t               n,
+   size_t               size
+)
+{
+   // handy wrapper for operations we always forget, like checking calloc's returned pointer.
+
+   void *buffer;
+
+   // first off, check if buffer size is valid
+   if (n == 0 || size == 0)
+      TerminateOnError ("UTIL_calloc() called with invalid parameters\n");
+
+   buffer = calloc(n, size); // allocate real memory space
+
+   // last check, check if malloc call succeeded
+   if (buffer == NULL)
+      TerminateOnError("UTIL_calloc() failure for %d bytes (out of memory?)\n", size * n);
+
+   return buffer; // nothing went wrong, so return buffer pointer
+}
+
+FILE *
+UTIL_OpenRequiredFile(
+   LPCSTR            lpszFileName
+)
+/*++
+  Purpose:
+
+    Open a required file. If fails, quit the program.
+
+  Parameters:
+
+    [IN]  lpszFileName - file name to open.
+
+  Return value:
+
+    Pointer to the file.
+
+--*/
+{
+   FILE         *fp;
+
+   fp = fopen(va("%s%s", PAL_PREFIX, lpszFileName), "rb");
+
+   if (fp == NULL)
+   {
+      TerminateOnError("File not found: %s!\n", lpszFileName);
+   }
+
+   return fp;
+}
+
+VOID
+UTIL_CloseFile(
+   FILE             *fp
+)
+/*++
+  Purpose:
+
+    Close a file.
+
+  Parameters:
+
+    [IN]  fp - file handle to be closed.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   if (fp != NULL)
+   {
+      fclose(fp);
+   }
+}
+
+#ifdef ENABLE_LOG
+
+static FILE *pLogFile = NULL;
+
+FILE *
+UTIL_OpenLog(
+   VOID
+)
+{
+   if ((pLogFile = fopen(_PATH_LOG, "a+")) == NULL)
+   {
+      return NULL;
+   }
+
+   return pLogFile;
+}
+
+VOID
+UTIL_CloseLog(
+   VOID
+)
+{
+   if (pLogFile != NULL)
+   {
+      fclose(pLogFile);
+   }
+}
+
+VOID
+UTIL_WriteLog(
+   int             Priority,
+   const char     *Fmt,
+   ...
+)
+{
+   va_list       vaa;
+   time_t        lTime;
+   struct tm    *curTime;
+   char          szDateBuf[260];
+
+   time(&lTime);
+
+   if ((Priority < LOG_EMERG) || (Priority >= LOG_LAST_PRIORITY))
+   {
+      return;
+   }
+
+   curTime = localtime(&lTime);
+   strftime(szDateBuf, 128, "%Y-%m-%d   %H:%M:%S", curTime);
+   szDateBuf[strlen(szDateBuf) - 1] = '\0'; //remove the
+
+   va_start(vaa,Fmt);
+
+   fprintf(pLogFile, "[%s]", szDateBuf);
+   vfprintf(pLogFile, Fmt, vaa);
+   fprintf(pLogFile, "\n");
+   fflush(pLogFile);
+
+   va_end(vaa);
+}
+
+#endif

+ 133 - 0
util.h

@@ -0,0 +1,133 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#include "common.h"
+
+//#define ENABLE_LOG 1
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+void
+trim(
+   char *str
+);
+
+char *va(
+   const char *format,
+   ...
+);
+
+int
+RandomLong(
+   int from,
+   int to
+);
+
+float
+RandomFloat(
+   float from,
+   float to
+);
+
+void
+UTIL_Delay(
+   unsigned int ms
+);
+
+void
+TerminateOnError(
+   const char *fmt,
+   ...
+);
+
+void *
+UTIL_malloc(
+   size_t               buffer_size
+);
+
+void *
+UTIL_calloc(
+   size_t               n,
+   size_t               size
+);
+
+FILE *
+UTIL_OpenRequiredFile(
+   LPCSTR               lpszFileName
+);
+
+VOID
+UTIL_CloseFile(
+   FILE                *fp
+);
+
+#define _PATH_LOG           PAL_PREFIX "log.txt"
+#define LOG_EMERG           0 /* system is unusable */
+#define LOG_ALERT           1 /* action must be taken immediately */
+#define LOG_CRIT            2 /* critical conditions */
+#define LOG_ERR             3 /* error conditions */
+#define LOG_WARNING         4 /* warning conditions */
+#define LOG_NOTICE          5 /* normal but significant condition */
+#define LOG_INFO            6 /* informational */
+#define LOG_DEBUG           7 /* debug-level messages */
+#define LOG_LAST_PRIORITY   8 /* last level */
+
+#ifdef ENABLE_LOG
+
+FILE *
+UTIL_OpenLog(
+   VOID
+);
+
+VOID
+UTIL_CloseLog(
+   VOID
+);
+
+VOID
+UTIL_WriteLog(
+   int             Priority,
+   const char     *Fmt,
+   ...
+);
+
+#else
+
+#define UTIL_OpenLog()       ((void)(0))
+#define UTIL_CloseLog()      ((void)(0))
+#ifdef _MSC_VER
+__forceinline VOID UTIL_WriteLog(int i, const char *p, ...) {}
+#else
+#define UTIL_WriteLog(...)   ((void)(0))
+#endif
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 869 - 0
video.c

@@ -0,0 +1,869 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#include "main.h"
+
+// Screen buffer
+SDL_Surface              *gpScreen           = NULL;
+
+// Backup screen buffer
+SDL_Surface              *gpScreenBak        = NULL;
+
+// The real screen surface
+static SDL_Surface       *gpScreenReal       = NULL;
+
+#if (defined (__SYMBIAN32__) && !defined (__S60_5X__)) || defined (PSP) || defined (GEKKO)
+   static BOOL bScaleScreen = FALSE;
+#else
+   static BOOL bScaleScreen = TRUE;
+#endif
+
+// Initial screen size
+static WORD               g_wInitialWidth    = 640;
+static WORD               g_wInitialHeight   = 400;
+
+// Shake times and level
+static WORD               g_wShakeTime       = 0;
+static WORD               g_wShakeLevel      = 0;
+
+INT
+#ifdef GEKKO // Rikku2000: Crash on compile, allready define on WIISDK
+VIDEO_Init_GEKKO(
+#else
+VIDEO_Init(
+#endif
+   WORD             wScreenWidth,
+   WORD             wScreenHeight,
+   BOOL             fFullScreen
+)
+/*++
+  Purpose:
+
+    Initialze the video subsystem.
+
+  Parameters:
+
+    [IN]  wScreenWidth - width of the screen.
+
+    [IN]  wScreenHeight - height of the screen.
+
+    [IN]  fFullScreen - TRUE to use full screen mode, FALSE to use windowed mode.
+
+  Return value:
+
+    0 = success, -1 = fail to create the screen surface,
+    -2 = fail to create screen buffer.
+
+--*/
+{
+   g_wInitialWidth = wScreenWidth;
+   g_wInitialHeight = wScreenHeight;
+
+   //
+   // Create the screen surface.
+   //
+#if defined (NDS)
+   gpScreenReal = SDL_SetVideoMode(293, 196, 8, SDL_SWSURFACE | SDL_FULLSCREEN);
+#elif defined (__SYMBIAN32__)
+#ifdef __S60_5X__
+   gpScreenReal = SDL_SetVideoMode(640, 360, 8,
+      SDL_SWSURFACE | (fFullScreen ? SDL_FULLSCREEN : 0));
+#else
+   gpScreenReal = SDL_SetVideoMode(320, 240, 8,
+      SDL_SWSURFACE | (fFullScreen ? SDL_FULLSCREEN : 0));
+#endif
+#elif defined (GEKKO)
+   gpScreenReal = SDL_SetVideoMode(640, 480, 8,
+      SDL_SWSURFACE | (fFullScreen ? SDL_FULLSCREEN : 0));
+#elif defined (PSP)
+   gpScreenReal = SDL_SetVideoMode(320, 240, 8, SDL_SWSURFACE | SDL_FULLSCREEN);
+#else
+   gpScreenReal = SDL_SetVideoMode(wScreenWidth, wScreenHeight, 8,
+      SDL_HWSURFACE | SDL_RESIZABLE | (fFullScreen ? SDL_FULLSCREEN : 0));
+#endif
+
+   if (gpScreenReal == NULL)
+   {
+      //
+      // Fall back to 640x480 software mode.
+      //
+      gpScreenReal = SDL_SetVideoMode(640, 480, 8,
+         SDL_SWSURFACE | (fFullScreen ? SDL_FULLSCREEN : 0));
+   }
+
+   //
+   // Still fail?
+   //
+   if (gpScreenReal == NULL)
+   {
+      return -1;
+   }
+
+   //
+   // Create the screen buffer and the backup screen buffer.
+   //
+   gpScreen = SDL_CreateRGBSurface(gpScreenReal->flags & ~SDL_HWSURFACE, 320, 200, 8,
+      gpScreenReal->format->Rmask, gpScreenReal->format->Gmask,
+      gpScreenReal->format->Bmask, gpScreenReal->format->Amask);
+
+   gpScreenBak = SDL_CreateRGBSurface(gpScreenReal->flags & ~SDL_HWSURFACE, 320, 200, 8,
+      gpScreenReal->format->Rmask, gpScreenReal->format->Gmask,
+      gpScreenReal->format->Bmask, gpScreenReal->format->Amask);
+
+   //
+   // Failed?
+   //
+   if (gpScreen == NULL || gpScreenBak == NULL)
+   {
+      if (gpScreen != NULL)
+      {
+         SDL_FreeSurface(gpScreen);
+      }
+
+      if (gpScreenBak != NULL)
+      {
+         SDL_FreeSurface(gpScreenBak);
+      }
+
+      SDL_FreeSurface(gpScreenReal);
+      return -2;
+   }
+
+   if (fFullScreen)
+   {
+      SDL_ShowCursor(FALSE);
+   }
+
+   return 0;
+}
+
+VOID
+VIDEO_Shutdown(
+   VOID
+)
+/*++
+  Purpose:
+
+    Shutdown the video subsystem.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   if (gpScreen != NULL)
+   {
+      SDL_FreeSurface(gpScreen);
+   }
+   gpScreen = NULL;
+
+   if (gpScreenBak != NULL)
+   {
+      SDL_FreeSurface(gpScreenBak);
+   }
+   gpScreenBak = NULL;
+
+   if (gpScreenReal != NULL)
+   {
+      SDL_FreeSurface(gpScreenReal);
+   }
+   gpScreenReal = NULL;
+}
+
+VOID
+VIDEO_UpdateScreen(
+   const SDL_Rect  *lpRect
+)
+/*++
+  Purpose:
+
+    Update the screen area specified by lpRect.
+
+  Parameters:
+
+    [IN]  lpRect - Screen area to update.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   SDL_Rect        srcrect, dstrect;
+   short           offset = 240 - 200;
+   short           screenRealHeight = gpScreenReal->h;
+   short           screenRealY = 0;
+
+   //
+   // Lock surface if needed
+   //
+   if (SDL_MUSTLOCK(gpScreenReal))
+   {
+      if (SDL_LockSurface(gpScreenReal) < 0)
+         return;
+   }
+
+   if (!bScaleScreen)
+   {
+      screenRealHeight -= offset;
+      screenRealY = offset / 2;
+   }
+
+   if (lpRect != NULL)
+   {
+      dstrect.x = (SHORT)((INT)(lpRect->x) * gpScreenReal->w / gpScreen->w);
+      dstrect.y = (SHORT)((INT)(screenRealY + lpRect->y) * screenRealHeight / gpScreen->h);
+      dstrect.w = (WORD)((DWORD)(lpRect->w) * gpScreenReal->w / gpScreen->w);
+      dstrect.h = (WORD)((DWORD)(lpRect->h) * screenRealHeight / gpScreen->h);
+
+      SDL_SoftStretch(gpScreen, (SDL_Rect *)lpRect, gpScreenReal, &dstrect);
+
+      if (SDL_MUSTLOCK(gpScreenReal))
+      {
+         SDL_UnlockSurface(gpScreenReal);
+      }
+
+      SDL_UpdateRect(gpScreenReal, dstrect.x, dstrect.y, dstrect.w, dstrect.h);
+   }
+   else if (g_wShakeTime != 0)
+   {
+      //
+      // Shake the screen
+      //
+      srcrect.x = 0;
+      srcrect.y = 0;
+      srcrect.w = 320;
+      srcrect.h = 200 - g_wShakeLevel;
+
+      dstrect.x = 0;
+      dstrect.y = screenRealY;
+      dstrect.w = 320 * gpScreenReal->w / gpScreen->w;
+      dstrect.h = (200 - g_wShakeLevel) * screenRealHeight / gpScreen->h;
+
+      if (g_wShakeTime & 1)
+      {
+         srcrect.y = g_wShakeLevel;
+      }
+      else
+      {
+         dstrect.y = (screenRealY + g_wShakeLevel) * screenRealHeight / gpScreen->h;
+      }
+
+      SDL_SoftStretch(gpScreen, &srcrect, gpScreenReal, &dstrect);
+
+      if (g_wShakeTime & 1)
+      {
+         dstrect.y = (screenRealY + screenRealHeight - g_wShakeLevel) * screenRealHeight / gpScreen->h;
+      }
+      else
+      {
+         dstrect.y = screenRealY;
+      }
+
+      dstrect.h = g_wShakeLevel * screenRealHeight / gpScreen->h;
+
+      SDL_FillRect(gpScreenReal, &dstrect, 0);
+
+      if (SDL_MUSTLOCK(gpScreenReal))
+      {
+         SDL_UnlockSurface(gpScreenReal);
+      }
+
+      SDL_UpdateRect(gpScreenReal, 0, 0, gpScreenReal->w, gpScreenReal->h);
+
+      g_wShakeTime--;
+   }
+   else
+   {
+      dstrect.x = 0;
+      dstrect.y = screenRealY;
+      dstrect.w = gpScreenReal->w;
+      dstrect.h = screenRealHeight;
+
+      SDL_SoftStretch(gpScreen, NULL, gpScreenReal, &dstrect);
+
+      if (SDL_MUSTLOCK(gpScreenReal))
+      {
+         SDL_UnlockSurface(gpScreenReal);
+      }
+
+      SDL_UpdateRect(gpScreenReal, 0, 0, gpScreenReal->w, gpScreenReal->h);
+   }
+}
+
+VOID
+VIDEO_SetPalette(
+   SDL_Color        rgPalette[256]
+)
+/*++
+  Purpose:
+
+    Set the palette of the screen.
+
+  Parameters:
+
+    [IN]  rgPalette - array of 256 colors.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   SDL_SetPalette(gpScreenReal, SDL_LOGPAL | SDL_PHYSPAL, rgPalette, 0, 256);
+#if (defined (__SYMBIAN32__))
+   {
+      static UINT32 time = 0;
+      if (SDL_GetTicks() - time > 50)
+      {
+	      SDL_UpdateRect(gpScreenReal, 0, 0, gpScreenReal->w, gpScreenReal->h);
+	      time = SDL_GetTicks();
+      }
+   }
+#endif
+}
+
+VOID
+VIDEO_Resize(
+   INT             w,
+   INT             h
+)
+/*++
+  Purpose:
+
+    This function is called when user resized the window.
+
+  Parameters:
+
+    [IN]  w - width of the window after resizing.
+
+    [IN]  h - height of the window after resizing.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   DWORD                    flags;
+   PAL_LARGE SDL_Color      palette[256];
+   int                      i;
+
+   //
+   // Get the original palette.
+   //
+   for (i = 0; i < gpScreenReal->format->palette->ncolors; i++)
+   {
+      palette[i] = gpScreenReal->format->palette->colors[i];
+   }
+
+   //
+   // Create the screen surface.
+   //
+   flags = gpScreenReal->flags;
+
+   SDL_FreeSurface(gpScreenReal);
+   gpScreenReal = SDL_SetVideoMode(w, h, 8, flags);
+
+   if (gpScreenReal == NULL)
+   {
+#ifdef __SYMBIAN32__
+#ifdef __S60_5X__
+      gpScreenReal = SDL_SetVideoMode(640, 360, 8, SDL_SWSURFACE);
+#else
+      gpScreenReal = SDL_SetVideoMode(320, 240, 8, SDL_SWSURFACE);
+#endif
+#else
+      //
+      // Fall back to 640x480 software windowed mode.
+      //
+      gpScreenReal = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE);
+#endif
+   }
+
+   SDL_SetPalette(gpScreenReal, SDL_PHYSPAL | SDL_LOGPAL, palette, 0, i);
+   VIDEO_UpdateScreen(NULL);
+}
+
+SDL_Color *
+VIDEO_GetPalette(
+   VOID
+)
+/*++
+  Purpose:
+
+    Get the current palette of the screen.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    Pointer to the current palette.
+
+--*/
+{
+   return gpScreenReal->format->palette->colors;
+}
+
+VOID
+VIDEO_ToggleScaleScreen(
+   VOID
+)
+/*++
+  Purpose:
+
+    Toggle scalescreen mode.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+#ifdef __SYMBIAN32__
+   bScaleScreen = !bScaleScreen;
+   VIDEO_Resize(320, 240);
+   VIDEO_UpdateScreen(NULL);
+#endif
+}
+
+VOID
+VIDEO_ToggleFullscreen(
+   VOID
+)
+/*++
+  Purpose:
+
+    Toggle fullscreen mode.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   DWORD                    flags;
+   PAL_LARGE SDL_Color      palette[256];
+   int                      i;
+
+   //
+   // Get the original palette.
+   //
+   for (i = 0; i < gpScreenReal->format->palette->ncolors; i++)
+   {
+      palette[i] = gpScreenReal->format->palette->colors[i];
+   }
+
+   //
+   // Get the flags of the original screen surface
+   //
+   flags = gpScreenReal->flags;
+
+   if (flags & SDL_FULLSCREEN)
+   {
+      //
+      // Already in fullscreen mode. Remove the fullscreen flag.
+      //
+      flags &= ~SDL_FULLSCREEN;
+      flags |= SDL_RESIZABLE;
+      SDL_ShowCursor(TRUE);
+   }
+   else
+   {
+      //
+      // Not in fullscreen mode. Set the fullscreen flag.
+      //
+      flags |= SDL_FULLSCREEN;
+      SDL_ShowCursor(FALSE);
+   }
+
+   //
+   // Free the original screen surface
+   //
+   SDL_FreeSurface(gpScreenReal);
+
+   //
+   // ... and create a new one
+   //
+   if (g_wInitialWidth == 640 && g_wInitialHeight == 400 && (flags & SDL_FULLSCREEN))
+   {
+      gpScreenReal = SDL_SetVideoMode(640, 480, 8, flags);
+   }
+   else if (g_wInitialWidth == 640 && g_wInitialHeight == 480 && !(flags & SDL_FULLSCREEN))
+   {
+      gpScreenReal = SDL_SetVideoMode(640, 400, 8, flags);
+   }
+   else
+   {
+      gpScreenReal = SDL_SetVideoMode(g_wInitialWidth, g_wInitialHeight, 8, flags);
+   }
+
+   VIDEO_SetPalette(palette);
+
+   //
+   // Update the screen
+   //
+   VIDEO_UpdateScreen(NULL);
+}
+
+VOID
+VIDEO_SaveScreenshot(
+   VOID
+)
+/*++
+  Purpose:
+
+    Save the screenshot of current screen to a BMP file.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   int      iNumBMP = 0;
+   FILE    *fp;
+
+   //
+   // Find a usable BMP filename.
+   //
+   for (iNumBMP = 0; iNumBMP <= 9999; iNumBMP++)
+   {
+      fp = fopen(va("%sscrn%.4d.bmp", PAL_PREFIX, iNumBMP), "rb");
+      if (fp == NULL)
+      {
+         break;
+      }
+      fclose(fp);
+   }
+
+   if (iNumBMP > 9999)
+   {
+      return;
+   }
+
+   //
+   // Save the screenshot.
+   //
+   SDL_SaveBMP(gpScreenReal, va("%sscrn%.4d.bmp", PAL_PREFIX, iNumBMP));
+}
+
+VOID
+VIDEO_BackupScreen(
+   VOID
+)
+/*++
+  Purpose:
+
+    Backup the screen buffer.
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   SDL_BlitSurface(gpScreen, NULL, gpScreenBak, NULL);
+}
+
+VOID
+VIDEO_RestoreScreen(
+   VOID
+)
+/*++
+  Purpose:
+
+    Restore the screen buffer which has been saved with VIDEO_BackupScreen().
+
+  Parameters:
+
+    None.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   SDL_BlitSurface(gpScreenBak, NULL, gpScreen, NULL);
+}
+
+VOID
+VIDEO_ShakeScreen(
+   WORD           wShakeTime,
+   WORD           wShakeLevel
+)
+/*++
+  Purpose:
+
+    Set the screen shake time and level.
+
+  Parameters:
+
+    [IN]  wShakeTime - how many times should we shake the screen.
+
+    [IN]  wShakeLevel - level of shaking.
+
+  Return value:
+
+    None.
+
+--*/
+{
+   g_wShakeTime = wShakeTime;
+   g_wShakeLevel = wShakeLevel;
+}
+
+VOID
+VIDEO_SwitchScreen(
+   WORD           wSpeed
+)
+/*++
+  Purpose:
+
+    Switch the screen from the backup screen buffer to the current screen buffer.
+    NOTE: This will destroy the backup buffer.
+
+  Parameters:
+
+    [IN]  wSpeed - speed of fading (the larger value, the slower).
+
+  Return value:
+
+    None.
+
+--*/
+{
+   int               i, j;
+   const int         rgIndex[6] = {0, 3, 1, 5, 2, 4};
+   SDL_Rect          dstrect;
+
+   short             offset = 240 - 200;
+   short             screenRealHeight = gpScreenReal->h;
+   short             screenRealY = 0;
+
+   if (!bScaleScreen)
+   {
+      screenRealHeight -= offset;
+      screenRealY = offset / 2;
+   }
+
+   wSpeed++;
+   wSpeed *= 10;
+
+   for (i = 0; i < 6; i++)
+   {
+      for (j = rgIndex[i]; j < gpScreen->pitch * gpScreen->h; j += 6)
+      {
+         ((LPBYTE)(gpScreenBak->pixels))[j] = ((LPBYTE)(gpScreen->pixels))[j];
+      }
+
+      //
+      // Draw the backup buffer to the screen
+      //
+      dstrect.x = 0;
+      dstrect.y = screenRealY;
+      dstrect.w = gpScreenReal->w;
+      dstrect.h = screenRealHeight;
+
+      SDL_SoftStretch(gpScreenBak, NULL, gpScreenReal, &dstrect);
+      SDL_UpdateRect(gpScreenReal, 0, 0, gpScreenReal->w, gpScreenReal->h);
+
+      UTIL_Delay(wSpeed);
+   }
+}
+
+VOID
+VIDEO_FadeScreen(
+   WORD           wSpeed
+)
+/*++
+  Purpose:
+
+    Fade from the backup screen buffer to the current screen buffer.
+    NOTE: This will destroy the backup buffer.
+
+  Parameters:
+
+    [IN]  wSpeed - speed of fading (the larger value, the slower).
+
+  Return value:
+
+    None.
+
+--*/
+{
+   int               i, j, k;
+   DWORD             time;
+   BYTE              a, b;
+   const int         rgIndex[6] = {0, 3, 1, 5, 2, 4};
+   SDL_Rect          dstrect;
+   short             offset = 240 - 200;
+   short             screenRealHeight = gpScreenReal->h;
+   short             screenRealY = 0;
+
+   //
+   // Lock surface if needed
+   //
+   if (SDL_MUSTLOCK(gpScreenReal))
+   {
+      if (SDL_LockSurface(gpScreenReal) < 0)
+         return;
+   }
+
+   if (!bScaleScreen)
+   {
+      screenRealHeight -= offset;
+      screenRealY = offset / 2;
+   }
+
+   time = SDL_GetTicks();
+
+   wSpeed++;
+   wSpeed *= 10;
+
+   for (i = 0; i < 12; i++)
+   {
+      for (j = 0; j < 6; j++)
+      {
+         PAL_ProcessEvent();
+         while (SDL_GetTicks() <= time)
+         {
+            PAL_ProcessEvent();
+            SDL_Delay(5);
+         }
+         time = SDL_GetTicks() + wSpeed;
+
+         //
+         // Blend the pixels in the 2 buffers, and put the result into the
+         // backup buffer
+         //
+         for (k = rgIndex[j]; k < gpScreen->pitch * gpScreen->h; k += 6)
+         {
+            a = ((LPBYTE)(gpScreen->pixels))[k];
+            b = ((LPBYTE)(gpScreenBak->pixels))[k];
+
+            if (i > 0)
+            {
+               if ((a & 0x0F) > (b & 0x0F))
+               {
+                  b++;
+               }
+               else if ((a & 0x0F) < (b & 0x0F))
+               {
+                  b--;
+               }
+            }
+
+            ((LPBYTE)(gpScreenBak->pixels))[k] = ((a & 0xF0) | (b & 0x0F));
+         }
+
+         //
+         // Draw the backup buffer to the screen
+         //
+         if (g_wShakeTime != 0)
+         {
+            //
+            // Shake the screen
+            //
+            SDL_Rect srcrect, dstrect;
+
+            srcrect.x = 0;
+            srcrect.y = 0;
+            srcrect.w = 320;
+            srcrect.h = 200 - g_wShakeLevel;
+
+            dstrect.x = 0;
+            dstrect.y = screenRealY;
+            dstrect.w = 320 * gpScreenReal->w / gpScreen->w;
+            dstrect.h = (200 - g_wShakeLevel) * screenRealHeight / gpScreen->h;
+
+            if (g_wShakeTime & 1)
+            {
+               srcrect.y = g_wShakeLevel;
+            }
+            else
+            {
+               dstrect.y = (screenRealY + g_wShakeLevel) * screenRealHeight / gpScreen->h;
+            }
+
+            SDL_SoftStretch(gpScreenBak, &srcrect, gpScreenReal, &dstrect);
+
+            if (g_wShakeTime & 1)
+            {
+               dstrect.y = (screenRealY + screenRealHeight - g_wShakeLevel) * screenRealHeight / gpScreen->h;
+            }
+            else
+            {
+               dstrect.y = screenRealY;
+            }
+
+            dstrect.h = g_wShakeLevel * screenRealHeight / gpScreen->h;
+
+            SDL_FillRect(gpScreenReal, &dstrect, 0);
+            SDL_UpdateRect(gpScreenReal, 0, 0, gpScreenReal->w, gpScreenReal->h);
+            g_wShakeTime--;
+         }
+         else
+         {
+            dstrect.x = 0;
+            dstrect.y = screenRealY;
+            dstrect.w = gpScreenReal->w;
+            dstrect.h = screenRealHeight;
+
+            SDL_SoftStretch(gpScreenBak, NULL, gpScreenReal, &dstrect);
+            SDL_UpdateRect(gpScreenReal, 0, 0, gpScreenReal->w, gpScreenReal->h);
+         }
+      }
+   }
+
+   if (SDL_MUSTLOCK(gpScreenReal))
+   {
+      SDL_UnlockSurface(gpScreenReal);
+   }
+
+   //
+   // Draw the result buffer to the screen as the final step
+   //
+   VIDEO_UpdateScreen(NULL);
+}

+ 111 - 0
video.h

@@ -0,0 +1,111 @@
+//
+// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
+// All rights reserved.
+//
+// This file is part of SDLPAL.
+//
+// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef VIDEO_H
+#define VIDEO_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "common.h"
+
+extern SDL_Surface *gpScreen;
+extern SDL_Surface *gpScreenBak;
+
+INT
+#ifdef GEKKO // Rikku2000: Crash on compile, allready define on WIISDK
+VIDEO_Init_GEKKO(
+#else
+VIDEO_Init(
+#endif
+   WORD             wScreenWidth,
+   WORD             wScreenHeight,
+   BOOL             fFullScreen
+);
+
+VOID
+VIDEO_Shutdown(
+   VOID
+);
+
+VOID
+VIDEO_UpdateScreen(
+   const SDL_Rect  *lpRect
+);
+
+VOID
+VIDEO_SetPalette(
+   SDL_Color        rgPalette[256]
+);
+
+VOID
+VIDEO_Resize(
+   INT             w,
+   INT             h
+);
+
+SDL_Color *
+VIDEO_GetPalette(
+   VOID
+);
+
+VOID
+VIDEO_ToggleFullscreen(
+   VOID
+);
+
+VOID
+VIDEO_SaveScreenshot(
+   VOID
+);
+
+VOID
+VIDEO_BackupScreen(
+   VOID
+);
+
+VOID
+VIDEO_RestoreScreen(
+   VOID
+);
+
+VOID
+VIDEO_ShakeScreen(
+   WORD           wShakeTime,
+   WORD           wShakeLevel
+);
+
+VOID
+VIDEO_SwitchScreen(
+   WORD           wSpeed
+);
+
+VOID
+VIDEO_FadeScreen(
+   WORD           wSpeed
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 495 - 0
yj1.c

@@ -0,0 +1,495 @@
+//
+// PAL DOS compress format (YJ_1) library
+//
+// Author: Lou Yihua <louyihua@21cn.com>
+//
+// Copyright 2006 - 2007 Lou Yihua
+//
+// This file is part of PAL library.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+//
+
+// Ported to C from C++ and modified for compatibility with Big-Endian
+// by Wei Mingzhi <whistler@openoffice.org>.
+
+// TODO: fix YJ_2 for big-endian
+
+#include "common.h"
+
+#ifndef PAL_WIN95
+
+typedef struct _TreeNode
+{
+   unsigned char   value;
+   unsigned char   leaf;
+   unsigned short  level;
+   unsigned int    weight;
+
+   struct _TreeNode *parent;
+   struct _TreeNode *left;
+   struct _TreeNode *right;
+} TreeNode;
+
+typedef struct _TreeNodeList
+{
+   TreeNode *node;
+   struct _TreeNodeList *next;
+} TreeNodeList;
+
+typedef struct _YJ_1_FILEHEADER
+{
+   unsigned int   Signature;          // 'YJ_1'
+   unsigned int   UncompressedLength; // size before compression
+   unsigned int   CompressedLength;   // size after compression
+   unsigned short BlockCount;       // number of blocks
+   unsigned char Unknown;
+   unsigned char HuffmanTreeLength; // length of huffman tree
+} YJ_1_FILEHEADER, *PYJ_1_FILEHEADER;
+
+typedef struct _YJ_1_BLOCKHEADER
+{
+   unsigned short UncompressedLength; // maximum 0x4000
+   unsigned short CompressedLength;   // including the header
+   unsigned short LZSSRepeatTable[4];
+   unsigned char LZSSOffsetCodeLengthTable[4];
+   unsigned char LZSSRepeatCodeLengthTable[3];
+   unsigned char CodeCountCodeLengthTable[3];
+   unsigned char CodeCountTable[2];
+} YJ_1_BLOCKHEADER, *PYJ_1_BLOCKHEADER;
+
+static unsigned int
+get_bits(
+   const void *src,
+   unsigned int *bitptr,
+   unsigned int count
+)
+{
+   unsigned char *temp = ((unsigned char *)src) + ((*bitptr >> 4) << 1);
+   unsigned int bptr = *bitptr & 0xf;
+   unsigned short mask;
+   *bitptr += count;
+   if (count > 16 - bptr)
+   {
+      count = count + bptr - 16;
+      mask = 0xffff >> bptr;
+      return (((temp[0] | (temp[1] << 8)) & mask) << count) | ((temp[2] | (temp[3] << 8)) >> (16 - count));
+   }
+   else
+      return (((unsigned short)((temp[0] | (temp[1] << 8)) << bptr)) >> (16 - count));
+}
+
+static unsigned short
+get_loop(
+   const void *src,
+   unsigned int *bitptr,
+   PYJ_1_BLOCKHEADER header
+)
+{
+   if (get_bits(src, bitptr, 1))
+      return header->CodeCountTable[0];
+   else
+   {
+      unsigned int temp = get_bits(src, bitptr, 2);
+      if (temp)
+         return get_bits(src, bitptr, header->CodeCountCodeLengthTable[temp - 1]);
+      else
+         return header->CodeCountTable[1];
+   }
+}
+
+static unsigned short
+get_count(
+   const void *src,
+   unsigned int *bitptr,
+   PYJ_1_BLOCKHEADER header
+)
+{
+   unsigned short temp;
+   if ((temp = get_bits(src, bitptr, 2)) != 0)
+   {
+      if (get_bits(src, bitptr, 1))
+         return get_bits(src, bitptr, header->LZSSRepeatCodeLengthTable[temp - 1]);
+      else
+         return SWAP16(header->LZSSRepeatTable[temp]);
+   }
+   else
+      return SWAP16(header->LZSSRepeatTable[0]);
+}
+
+INT
+Decompress(
+   LPCVOID       Source,
+   LPVOID        Destination,
+   INT           DestSize
+)
+{
+   PYJ_1_FILEHEADER hdr = (PYJ_1_FILEHEADER)Source;
+   unsigned char *src = (unsigned char *)Source;
+   unsigned char *dest;
+   unsigned int i;
+   TreeNode *root, *node;
+
+   if (Source == NULL)
+      return -1;
+   if (SWAP32(hdr->Signature) != 0x315f4a59)
+      return -1;
+   if (SWAP32(hdr->UncompressedLength) > (unsigned int)DestSize)
+      return -1;
+
+   do
+   {
+      unsigned short tree_len = ((unsigned short)hdr->HuffmanTreeLength) * 2;
+      unsigned int bitptr = 0;
+      unsigned char *flag = (unsigned char *)src + 16 + tree_len;
+
+      if ((node = root = (TreeNode *)malloc(sizeof(TreeNode) * (tree_len + 1))) == NULL)
+         return -1;
+      root[0].leaf = 0;
+      root[0].value = 0;
+      root[0].left = root + 1;
+      root[0].right = root + 2;
+      for (i = 1; i <= tree_len; i++)
+      {
+         root[i].leaf = !get_bits(flag, &bitptr, 1);
+         root[i].value = src[15 + i];
+         if (root[i].leaf)
+            root[i].left = root[i].right = NULL;
+         else
+         {
+            root[i].left =  root + (root[i].value << 1) + 1;
+            root[i].right = root[i].left + 1;
+         }
+      }
+      src += 16 + tree_len + (((tree_len & 0xf) ? (tree_len >> 4) + 1 : (tree_len >> 4)) << 1);
+   } while (0);
+
+   dest = (unsigned char *)Destination;
+
+   for (i = 0; i < SWAP16(hdr->BlockCount); i++)
+   {
+      unsigned int bitptr;
+      PYJ_1_BLOCKHEADER header;
+
+      header = (PYJ_1_BLOCKHEADER)src;
+      src += 4;
+      if (!SWAP16(header->CompressedLength))
+      {
+         unsigned short hul = SWAP16(header->UncompressedLength);
+         while (hul--)
+         {
+            *dest++ = *src++;
+         }
+         continue;
+      }
+      src += 20;
+      bitptr = 0;
+      for (;;)
+      {
+         unsigned short loop;
+         if ((loop = get_loop(src, &bitptr, header)) == 0)
+            break;
+
+         while (loop--)
+         {
+            node = root;
+            for(; !node->leaf;)
+            {
+               if (get_bits(src, &bitptr, 1))
+                  node = node->right;
+               else
+                  node = node->left;
+            }
+            *dest++ = node->value;
+         }
+
+         if ((loop = get_loop(src, &bitptr, header)) == 0)
+            break;
+
+         while (loop--)
+         {
+            unsigned int pos, count;
+            count = get_count(src, &bitptr, header);
+            pos = get_bits(src, &bitptr, 2);
+            pos = get_bits(src, &bitptr, header->LZSSOffsetCodeLengthTable[pos]);
+            while (count--)
+            {
+               *dest = *(dest - pos);
+               dest++;
+            }
+         }
+      }
+      src = ((unsigned char *)header) + SWAP16(header->CompressedLength);
+   }
+   free(root);
+
+   return SWAP32(hdr->UncompressedLength);
+}
+
+#else
+
+typedef struct _TreeNode
+{
+   unsigned short      weight;
+   unsigned short      value;
+   struct _TreeNode*   parent;
+   struct _TreeNode*   left;
+   struct _TreeNode*   right;
+} TreeNode;
+
+typedef struct _Tree
+{
+   TreeNode*   node;
+   TreeNode**   list;
+} Tree;
+
+static unsigned char data1[0x100] =
+{
+0x3f,0x0b,0x17,0x03,0x2f,0x0a,0x16,0x00,0x2e,0x09,0x15,0x02,0x2d,0x01,0x08,0x00,
+0x3e,0x07,0x14,0x03,0x2c,0x06,0x13,0x00,0x2b,0x05,0x12,0x02,0x2a,0x01,0x04,0x00,
+0x3d,0x0b,0x11,0x03,0x29,0x0a,0x10,0x00,0x28,0x09,0x0f,0x02,0x27,0x01,0x08,0x00,
+0x3c,0x07,0x0e,0x03,0x26,0x06,0x0d,0x00,0x25,0x05,0x0c,0x02,0x24,0x01,0x04,0x00,
+0x3b,0x0b,0x17,0x03,0x23,0x0a,0x16,0x00,0x22,0x09,0x15,0x02,0x21,0x01,0x08,0x00,
+0x3a,0x07,0x14,0x03,0x20,0x06,0x13,0x00,0x1f,0x05,0x12,0x02,0x1e,0x01,0x04,0x00,
+0x39,0x0b,0x11,0x03,0x1d,0x0a,0x10,0x00,0x1c,0x09,0x0f,0x02,0x1b,0x01,0x08,0x00,
+0x38,0x07,0x0e,0x03,0x1a,0x06,0x0d,0x00,0x19,0x05,0x0c,0x02,0x18,0x01,0x04,0x00,
+0x37,0x0b,0x17,0x03,0x2f,0x0a,0x16,0x00,0x2e,0x09,0x15,0x02,0x2d,0x01,0x08,0x00,
+0x36,0x07,0x14,0x03,0x2c,0x06,0x13,0x00,0x2b,0x05,0x12,0x02,0x2a,0x01,0x04,0x00,
+0x35,0x0b,0x11,0x03,0x29,0x0a,0x10,0x00,0x28,0x09,0x0f,0x02,0x27,0x01,0x08,0x00,
+0x34,0x07,0x0e,0x03,0x26,0x06,0x0d,0x00,0x25,0x05,0x0c,0x02,0x24,0x01,0x04,0x00,
+0x33,0x0b,0x17,0x03,0x23,0x0a,0x16,0x00,0x22,0x09,0x15,0x02,0x21,0x01,0x08,0x00,
+0x32,0x07,0x14,0x03,0x20,0x06,0x13,0x00,0x1f,0x05,0x12,0x02,0x1e,0x01,0x04,0x00,
+0x31,0x0b,0x11,0x03,0x1d,0x0a,0x10,0x00,0x1c,0x09,0x0f,0x02,0x1b,0x01,0x08,0x00,
+0x30,0x07,0x0e,0x03,0x1a,0x06,0x0d,0x00,0x19,0x05,0x0c,0x02,0x18,0x01,0x04,0x00
+};
+static unsigned char data2[0x10] =
+{
+0x08,0x05,0x06,0x04,0x07,0x05,0x06,0x03,0x07,0x05,0x06,0x04,0x07,0x04,0x05,0x03
+};
+
+static void adjust_tree(Tree tree, unsigned short value)
+{
+   TreeNode* node = tree.list[value];
+   TreeNode tmp;
+   TreeNode* tmp1;
+   TreeNode* temp;
+   while(node->value != 0x280)
+   {
+      temp = node + 1;
+      while(node->weight == temp->weight)
+         temp++;
+      temp--;
+      if (temp != node)
+      {
+         tmp1 = node->parent;
+         node->parent = temp->parent;
+         temp->parent = tmp1;
+         if (node->value > 0x140)
+         {
+            node->left->parent = temp;
+            node->right->parent = temp;
+         }
+         else
+            tree.list[node->value] = temp;
+         if (temp->value > 0x140)
+         {
+            temp->left->parent = node;
+            temp->right->parent = node;
+         }
+         else
+            tree.list[temp->value] = node;
+         tmp = *node; *node = *temp; *temp = tmp;
+         node = temp;
+      }
+      node->weight++;
+      node = node->parent;
+   }
+   node->weight++;
+}
+
+static int build_tree(Tree *tree)
+{
+   int i, ptr;
+   TreeNode** list;
+   TreeNode* node;
+   if ((tree->list = list = (TreeNode **)malloc(sizeof(TreeNode*) * 321)) == NULL)
+      return 0;
+   if ((tree->node = node = (TreeNode *)malloc(sizeof(TreeNode) * 641)) == NULL)
+   {
+      free(list);
+      return 0;
+   }
+   memset(list, 0, 321 * sizeof(TreeNode*));
+   memset(node, 0, 641 * sizeof(TreeNode));
+   for(i = 0; i <= 0x140; i++)
+      list[i] = node + i;
+   for(i = 0; i <= 0x280; i++)
+   {
+      node[i].value = i;
+      node[i].weight = 1;
+   }
+   tree->node[0x280].parent = tree->node + 0x280;
+   for(i = 0, ptr = 0x141; ptr <= 0x280; i += 2, ptr++)
+   {
+      node[ptr].left = node + i;
+      node[ptr].right = node + i + 1;
+      node[i].parent = node[i + 1].parent = node + ptr;
+      node[ptr].weight = node[i].weight + node[i + 1].weight;
+   }
+   return 1;
+}
+
+#pragma pack(1)
+typedef struct _BitField
+{
+   unsigned char   b0:   1;
+   unsigned char   b1:   1;
+   unsigned char   b2:   1;
+   unsigned char   b3:   1;
+   unsigned char   b4:   1;
+   unsigned char   b5:   1;
+   unsigned char   b6:   1;
+   unsigned char   b7:   1;
+} BitField;
+#pragma pack()
+
+static int bt(const void* data, unsigned int pos)
+{
+   BitField* bit = (BitField*)((unsigned char*)data + (pos >> 3));
+   switch(pos & 0x7)
+   {
+   case 0:   return bit->b0;
+   case 1:   return bit->b1;
+   case 2:   return bit->b2;
+   case 3:   return bit->b3;
+   case 4:   return bit->b4;
+   case 5:   return bit->b5;
+   case 6:   return bit->b6;
+   case 7:   return bit->b7;
+   }
+   return 0;
+}
+
+static void bit(void* data, unsigned int pos, int set)
+{
+   BitField* bit = (BitField*)((unsigned char*)data + (pos >> 3));
+   switch(pos & 0x7)
+   {
+   case 0:
+      bit->b0 = set;
+      break;
+   case 1:
+      bit->b1 = set;
+      break;
+   case 2:
+      bit->b2 = set;
+      break;
+   case 3:
+      bit->b3 = set;
+      break;
+   case 4:
+      bit->b4 = set;
+      break;
+   case 5:
+      bit->b5 = set;
+      break;
+   case 6:
+      bit->b6 = set;
+      break;
+   case 7:
+      bit->b7 = set;
+      break;
+   }
+}
+
+INT
+Decompress(
+   LPCVOID       Source,
+   LPVOID        Destination,
+   INT           DestSize
+)
+{
+   unsigned int len = 0, ptr = 0, Length = 0;
+   unsigned char* src = (unsigned char*)Source + 4;
+   unsigned char* dest;
+   Tree tree;
+   TreeNode* node;
+
+   if (Source == NULL)
+      return -1;
+
+   if (!build_tree(&tree))
+      return -1;
+
+   Length = SWAP32(*((unsigned int*)Source));
+   if (Length > DestSize)
+      return -1;
+   dest = (unsigned char*)Destination;
+
+   while (1)
+   {
+      unsigned short val;
+      node = tree.node + 0x280;
+      while(node->value > 0x140)
+      {
+         if (bt(src, ptr))
+            node = node->right;
+         else
+            node = node->left;
+         ptr++;
+      }
+      val = node->value;
+      if (tree.node[0x280].weight == 0x8000)
+      {
+         int i;
+         for(i = 0; i < 0x141; i++)
+            if (tree.list[i]->weight & 0x1)
+               adjust_tree(tree, i);
+         for(i = 0; i <= 0x280; i++)
+            tree.node[i].weight >>= 1;
+      }
+      adjust_tree(tree, val);
+      if (val > 0xff)
+      {
+         int i;
+         unsigned int temp, tmp, pos;
+         unsigned char* pre;
+         for(i = 0, temp = 0; i < 8; i++, ptr++)
+            temp |= (unsigned int)bt(src, ptr) << i;
+         tmp = temp & 0xff;
+         for(; i < data2[tmp & 0xf] + 6; i++, ptr++)
+            temp |= (unsigned int)bt(src, ptr) << i;
+         temp >>= data2[tmp & 0xf];
+         pos = (temp & 0x3f) | ((unsigned int)data1[tmp] << 6);
+         if (pos == 0xfff)
+            break;
+         pre = dest - pos - 1;
+         for(i = 0; i < val - 0xfd; i++)
+            *dest++ = *pre++;
+         len += val - 0xfd;
+      }
+      else
+      {
+         *dest++ = (unsigned char)val;
+         len++;
+      }
+   }
+
+   free(tree.list);
+   free(tree.node);
+   return Length;
+}
+
+#endif