Browse Source

Merge pull request #42 from sdlpal/whistler/aviplay

AVI support for Win95 version
[ci skip]
Lou Yihua 7 years ago
parent
commit
9e2b62cdd6
44 changed files with 1479 additions and 288 deletions
  1. 6 3
      android/app/src/main/java/io/github/sdlpal/SettingsActivity.java
  2. 30 22
      android/app/src/main/res/layout/content_settings.xml
  3. 6 6
      android/app/src/main/res/values-zh-rTW/arrays.xml
  4. 1 0
      android/app/src/main/res/values-zh-rTW/strings.xml
  5. 6 6
      android/app/src/main/res/values-zh/arrays.xml
  6. 1 0
      android/app/src/main/res/values-zh/strings.xml
  7. 3 0
      android/app/src/main/res/values/arrays.xml
  8. 1 0
      android/app/src/main/res/values/strings.xml
  9. 6 0
      audio.c
  10. 782 0
      aviplay.c
  11. 58 0
      aviplay.h
  12. 90 57
      ending.c
  13. 5 0
      global.c
  14. 15 2
      ios/SDLPal/SDLPal.xcodeproj/project.pbxproj
  15. 8 0
      macos/Pal.xcodeproj/project.pbxproj
  16. 6 0
      main.c
  17. 1 0
      main.h
  18. 5 0
      palcfg.c
  19. 2 0
      palcfg.h
  20. 133 0
      riff.h
  21. 21 62
      sound.c
  22. 5 0
      uigame.c
  23. 70 69
      unix/Makefile
  24. 22 17
      unix/unix.cpp
  25. 4 1
      util.c
  26. 60 3
      video.c
  27. 5 0
      video.h
  28. 3 1
      win32/resource.h
  29. 12 9
      win32/sdlpal.rc
  30. 3 0
      win32/sdlpal.vcxproj
  31. 9 0
      win32/sdlpal.vcxproj.filters
  32. 2 0
      win32/win32.cpp
  33. 18 17
      winrt/SDLPal.Common/MainPage.xaml
  34. 2 0
      winrt/SDLPal.Common/MainPage.xaml.cpp
  35. 9 0
      winrt/SDLPal.Common/Strings/en/Resources.resw
  36. 15 6
      winrt/SDLPal.Common/Strings/zh-hans/Resources.resw
  37. 15 6
      winrt/SDLPal.Common/Strings/zh-hant/Resources.resw
  38. 3 0
      winrt/SDLPal.UWP/SDLPal.Core.vcxproj
  39. 9 0
      winrt/SDLPal.UWP/SDLPal.Core.vcxproj.filters
  40. 4 0
      winrt/SDLPal.Windows/SDLPal.Core.vcxproj
  41. 9 0
      winrt/SDLPal.Windows/SDLPal.Core.vcxproj.filters
  42. 4 0
      winrt/SDLPal.WindowsPhone/SDLPal.Core.vcxproj
  43. 9 0
      winrt/SDLPal.WindowsPhone/SDLPal.Core.vcxproj.filters
  44. 1 1
      yj1.c

+ 6 - 3
android/app/src/main/java/io/github/sdlpal/SettingsActivity.java

@@ -36,6 +36,7 @@ public class SettingsActivity extends AppCompatActivity {
     private static final String Stereo = "Stereo";
     private static final String UseSurroundOPL = "UseSurroundOPL";
     private static final String UseTouchOverlay = "UseTouchOverlay";
+    private static final String EnableAviPlay = "EnableAviPlay";
     private static final String AudioBufferSize = "AudioBufferSize";
     private static final String LogLevel = "LogLevel";
     private static final String OPLSampleRate = "OPLSampleRate";
@@ -54,7 +55,7 @@ public class SettingsActivity extends AppCompatActivity {
 
     private static final int AudioSampleRates[] = { 11025, 22050, 44100 };
     private static final int AudioBufferSizes[] = { 512, 1024, 2048, 4096, 8192 };
-    private static final int OPLSampleRates[] = { 12429, 24858, 49716 };
+    private static final int OPLSampleRates[] = { 11025, 12429, 22050, 24858, 44100, 49716 };
     private static final String CDFormats[] = { "MP3", "OGG" };
     private static final String MusicFormats[] = { "MIDI", "RIX", "MP3", "OGG" };
     private static final String OPLFormats[] = { "DOSBOX", "MAME", "DOSBOXNEW" };
@@ -196,6 +197,7 @@ public class SettingsActivity extends AppCompatActivity {
         ((SwitchCompat)findViewById(R.id.swMsgFile)).setChecked(false);
         ((SwitchCompat)findViewById(R.id.swFontFile)).setChecked(false);
         ((SwitchCompat)findViewById(R.id.swLogFile)).setChecked(false);
+        ((SwitchCompat)findViewById(R.id.swAVI)).setChecked(getConfigBoolean(EnableAviPlay, true));
         ((SwitchCompat)findViewById(R.id.swTouch)).setChecked(getConfigBoolean(UseTouchOverlay, true));
         ((SwitchCompat)findViewById(R.id.swAspect)).setChecked(getConfigBoolean(KeepAspectRatio, true));
         ((SwitchCompat)findViewById(R.id.swSurround)).setChecked(getConfigBoolean(UseSurroundOPL, true));
@@ -207,7 +209,7 @@ public class SettingsActivity extends AppCompatActivity {
         ((AppCompatSpinner)findViewById(R.id.spCDFmt)).setSelection(findMatchedStringIndex(getConfigString(CDFormat, true), CDFormats, 1));     // OGG
         ((AppCompatSpinner)findViewById(R.id.spMusFmt)).setSelection(findMatchedStringIndex(getConfigString(MusicFormat, true), MusicFormats, 1));    // RIX
         ((AppCompatSpinner)findViewById(R.id.spOPL)).setSelection(findMatchedStringIndex(getConfigString(OPLFormat, true), OPLFormats, 1));       // MAME
-        ((AppCompatSpinner)findViewById(R.id.spOPLRate)).setSelection(findMatchedIntIndex(getConfigInt(OPLSampleRate, true), OPLSampleRates, 2));  // 49716Hz
+        ((AppCompatSpinner)findViewById(R.id.spOPLRate)).setSelection(findMatchedIntIndex(getConfigInt(OPLSampleRate, true), OPLSampleRates, 5));  // 49716Hz
     }
 
 
@@ -230,6 +232,7 @@ public class SettingsActivity extends AppCompatActivity {
         ((SwitchCompat)findViewById(R.id.swMsgFile)).setChecked(msgFile != null && !msgFile.isEmpty());
         ((SwitchCompat)findViewById(R.id.swFontFile)).setChecked(fontFile != null && !fontFile.isEmpty());
         ((SwitchCompat)findViewById(R.id.swLogFile)).setChecked(logFile != null && !logFile.isEmpty());
+        ((SwitchCompat)findViewById(R.id.swAVI)).setChecked(getConfigBoolean(EnableAviPlay, false));
         ((SwitchCompat)findViewById(R.id.swTouch)).setChecked(getConfigBoolean(UseTouchOverlay, false));
         ((SwitchCompat)findViewById(R.id.swAspect)).setChecked(getConfigBoolean(KeepAspectRatio, false));
         ((SwitchCompat)findViewById(R.id.swSurround)).setChecked(getConfigBoolean(UseSurroundOPL, false));
@@ -241,7 +244,7 @@ public class SettingsActivity extends AppCompatActivity {
         ((AppCompatSpinner)findViewById(R.id.spCDFmt)).setSelection(findMatchedStringIndex(getConfigString(CDFormat, false), CDFormats, 1));     // OGG
         ((AppCompatSpinner)findViewById(R.id.spMusFmt)).setSelection(findMatchedStringIndex(getConfigString(MusicFormat, false), MusicFormats, 1));    // RIX
         ((AppCompatSpinner)findViewById(R.id.spOPL)).setSelection(findMatchedStringIndex(getConfigString(OPLFormat, false), OPLFormats, 1));       // MAME
-        ((AppCompatSpinner)findViewById(R.id.spOPLRate)).setSelection(findMatchedIntIndex(getConfigInt(OPLSampleRate, false), OPLSampleRates, 2));  // 49716Hz
+        ((AppCompatSpinner)findViewById(R.id.spOPLRate)).setSelection(findMatchedIntIndex(getConfigInt(OPLSampleRate, false), OPLSampleRates, 5));  // 49716Hz
     }
 
     protected boolean setConfigs() {

+ 30 - 22
android/app/src/main/res/layout/content_settings.xml

@@ -65,35 +65,19 @@
                 android:ems="10"
                 android:inputType="textUri" />
 
-            <TextView
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:text="@string/label_loglevel" />
-
-            <android.support.v7.widget.AppCompatSpinner
-                android:id="@+id/spLogLevel"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:entries="@array/log_level" />
-
             <android.support.v7.widget.SwitchCompat
-                android:id="@+id/swLogFile"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:text="@string/action_uselogfile" />
-
-            <EditText
-                android:id="@+id/edLogFile"
+                android:id="@+id/swTouch"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:ems="10"
-                android:inputType="textUri" />
+                android:text="@string/action_touch"
+                tools:layout_editor_absoluteX="8dp"
+                tools:layout_editor_absoluteY="228dp" />
 
             <android.support.v7.widget.SwitchCompat
-                android:id="@+id/swTouch"
+                android:id="@+id/swAVI"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:text="@string/action_touch"
+                android:text="@string/action_avi"
                 tools:layout_editor_absoluteX="8dp"
                 tools:layout_editor_absoluteY="228dp" />
 
@@ -226,6 +210,30 @@
 
             </LinearLayout>
 
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/label_loglevel" />
+
+            <android.support.v7.widget.AppCompatSpinner
+                android:id="@+id/spLogLevel"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:entries="@array/log_level" />
+
+            <android.support.v7.widget.SwitchCompat
+                android:id="@+id/swLogFile"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/action_uselogfile" />
+
+            <EditText
+                android:id="@+id/edLogFile"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:ems="10"
+                android:inputType="textUri" />
+
             <android.support.v7.widget.GridLayout
                 app:columnCount="3"
                 app:rowCount="1"

+ 6 - 6
android/app/src/main/res/values-zh-rTW/arrays.xml

@@ -1,10 +1,10 @@
 <resources>
     <string-array name="log_level">
-        <item>詳細</item>
-        <item>調試</item>
-        <item>信息</item>
-        <item>警告</item>
-        <item>錯誤</item>
-        <item>致命</item>
+        <item>詳細信息</item>
+        <item>調試信息</item>
+        <item>運行信息</item>
+        <item>普通警告</item>
+        <item>嚴重錯誤</item>
+        <item>致命錯誤</item>
     </string-array>
 </resources>

+ 1 - 0
android/app/src/main/res/values-zh-rTW/strings.xml

@@ -13,6 +13,7 @@
     <string name="action_stereo">立體聲</string>
     <string name="action_suropl">環繞聲 OPL</string>
     <string name="action_touch">啟用觸屏輔助</string>
+    <string name="action_avi">啟用 AVI 過場動畫</string>
     <string name="title_settings">設定模式</string>
     <string name="label_sfxvol">音效音量</string>
     <string name="label_musvol">音樂音量</string>

+ 6 - 6
android/app/src/main/res/values-zh/arrays.xml

@@ -1,10 +1,10 @@
 <resources>
     <string-array name="log_level">
-        <item>详细</item>
-        <item>调试</item>
-        <item>信息</item>
-        <item>警告</item>
-        <item>错误</item>
-        <item>致命</item>
+        <item>详细信息</item>
+        <item>调试信息</item>
+        <item>运行信息</item>
+        <item>普通警告</item>
+        <item>严重错误</item>
+        <item>致命错误</item>
     </string-array>
 </resources>

+ 1 - 0
android/app/src/main/res/values-zh/strings.xml

@@ -5,6 +5,7 @@
     <string name="title_settings">设置模式</string>
     <string name="action_touch">启用触屏辅助</string>
     <string name="action_aspect">保持纵横比</string>
+    <string name="action_avi">启用 AVI 过场动画</string>
     <string name="action_default">默认设置</string>
     <string name="action_finish">完成设置</string>
     <string name="action_suropl">环绕声 OPL</string>

+ 3 - 0
android/app/src/main/res/values/arrays.xml

@@ -27,8 +27,11 @@
         <item>DOSBOXNEW</item>
     </string-array>
     <string-array name="opl_rate" translatable="false">
+        <item>11025</item>
         <item>12429</item>
+        <item>22050</item>
         <item>24858</item>
+        <item>44100</item>
         <item>49716</item>
     </string-array>
     <string-array name="log_level">

+ 1 - 0
android/app/src/main/res/values/strings.xml

@@ -6,6 +6,7 @@
     <string name="action_touch">Enable touch overlay</string>
     <string name="action_aspect">Keep aspect ratio</string>
     <string name="action_stereo">Stereo</string>
+    <string name="action_avi">Enable AVI animation</string>
     <string name="label_musvol">Music volume</string>
     <string name="label_sfxvol">SFX volume</string>
     <string name="label_quality">Audio quality</string>

+ 6 - 0
audio.c

@@ -28,6 +28,7 @@
 #include "util.h"
 #include "resampler.h"
 #include "midi.h"
+#include "aviplay.h"
 #include <math.h>
 
 #if PAL_HAS_OGG
@@ -167,6 +168,11 @@ AUDIO_FillBuffer(
 	   AUDIO_MixNative((short *)stream, gAudioDevice.pSoundBuffer, len >> 1);
    }
 
+   //
+   // Play sound for AVI
+   //
+   AVI_FillAudioBuffer(AVI_GetPlayState(), (LPBYTE)stream, len);
+
    //
    // Convert audio from native byte-order to actual byte-order
    //

+ 782 - 0
aviplay.c

@@ -0,0 +1,782 @@
+/* -*- mode: c; tab-width: 4; c-basic-offset: 4; c-file-style: "linux" -*- */
+//
+// Copyright (c) 2017, SDLPAL development team.
+// 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/>.
+//
+
+//
+// aviplay.c
+//
+// Simple quick and dirty AVI player specially designed for PAL Win95.
+//
+
+/*
+ * Portions based on:
+ *
+ * Microsoft Video-1 Decoder
+ * Copyright (C) 2003 The FFmpeg project
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Microsoft Video-1 Decoder by Mike Melanson (melanson@pcisys.net)
+ * For more information about the MS Video-1 format, visit:
+ *   http://www.pcisys.net/~melanson/codecs/
+ */
+
+#include "util.h"
+#include "audio.h"
+#include "aviplay.h"
+#include "input.h"
+#include "video.h"
+#include "riff.h"
+#include "palcfg.h"
+
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+
+# define SwapStruct32(v, s) \
+	for(int s##_i = 0; s##_i < sizeof(s) / sizeof(uint32_t); s##_i++) \
+		((uint32_t *)&v)[s##_i] = SDL_Swap32(((uint32_t *)&v)[s##_i])
+
+# define SwapStructFields(v, f1, f2) v.##f1 ^= v.##f2, v.##f2 ^= v.##f1, v.##f1 ^= v.##f2
+
+
+#else
+
+# define SwapStruct32(...)
+# define SwapStructFields(...)
+
+#endif
+
+#define HAS_FLAG(v, f) (((v) & (f)) == (f))
+
+#define MAX_AVI_BLOCK_LEVELS 3
+
+#define FLAGS_AVI_MAIN_HEADER  0x01
+#define FLAGS_AVI_VIDEO_FORMAT 0x02
+#define FLAGS_AVI_AUDIO_FORMAT 0x04
+#define FLAGS_AVI_ALL_HEADERS  0x07
+
+typedef struct AVIPlayState
+{
+	SDL_mutex     *selfMutex;
+    volatile FILE *fp;                 // pointer to the AVI file
+    SDL_Surface   *surface;            // video buffer
+
+    long           lVideoEndPos;
+	uint32_t       dwMillisPerFrame;       // milliseconds per frame
+	uint32_t       dwBufferSize;
+    SDL_AudioCVT   cvt;
+
+	uint8_t       *pChunkBuffer;
+	uint8_t       *pbAudioBuf;  // ring buffer for audio data
+	uint32_t       dwAudBufLen;
+	uint32_t       dwAudioReadPos;
+	uint32_t       dwAudioWritePos;
+
+	BOOL          fInterleaved;
+} AVIPlayState;
+
+static AVIPlayState gAVIPlayState;
+
+static AVIPlayState *
+PAL_ReadAVIInfo(
+	FILE         *fp,
+	AVIPlayState *avi
+)
+{
+	RIFFHeader hdr;
+	AVIMainHeader aviHeader;
+	AVIStreamHeader streamHeader = { 0 };
+	BitmapInfoHeader bih;
+	WAVEFormatEx wfe;
+	uint32_t   block_type[MAX_AVI_BLOCK_LEVELS];
+	long       next_pos[MAX_AVI_BLOCK_LEVELS];
+	long       file_length = (fseek(fp, 0, SEEK_END), ftell(fp)), pos = 0;
+	int        current_level = 0, flags = 0;
+
+    //
+    // Check RIFF file header
+    //
+	fseek(fp, 0, SEEK_SET);
+	if (fread(&hdr, sizeof(RIFFHeader), 1, fp) != 1 ||
+		hdr.signature != RIFF_RIFF || hdr.type != RIFF_AVI ||
+		hdr.length > (uint32_t)(file_length - sizeof(RIFFHeader) + sizeof(uint32_t)))
+	{
+		UTIL_LogOutput(LOGLEVEL_WARNING, "Illegal AVI RIFF header!");
+		return NULL;
+	}
+	else
+	{
+		next_pos[current_level] = (pos += sizeof(RIFFHeader)) + hdr.length;
+		block_type[current_level++] = hdr.type;
+	}
+    
+    while (!feof(fp) && current_level > 0)
+    {
+		RIFFBlockHeader block;
+		fseek(fp, pos, SEEK_SET);
+		if (fread(&block.type, sizeof(RIFFChunkHeader), 1, fp) != 1)
+		{
+			UTIL_LogOutput(LOGLEVEL_WARNING, "Illegal AVI RIFF LIST/Chunk header!");
+			return NULL;
+		}
+		else
+		{
+			block.type = SDL_SwapLE32(block.type);
+			block.length = SDL_SwapLE32(block.length);
+			pos += sizeof(RIFFChunkHeader);
+		}
+
+		//
+		// Read further if current block is a 'LIST'
+		//
+		if (block.type == AVI_LIST)
+		{
+			if (fread(&block.list.type, sizeof(RIFFListHeader) - sizeof(RIFFChunkHeader), 1, fp) != 1)
+			{
+				UTIL_LogOutput(LOGLEVEL_WARNING, "Illegal AVI RIFF LIST header!");
+				return NULL;
+			}
+			else
+			{
+				block.list.type = SDL_SwapLE32(block.list.type);
+			}
+		}
+
+		switch (block_type[current_level - 1])
+		{
+		case RIFF_AVI:
+			//
+			// RIFF_AVI only appears at top-level
+			//
+			if (current_level != 1)
+			{
+				UTIL_LogOutput(LOGLEVEL_WARNING, "RIFF 'AVI ' block appears at non-top level!");
+				return NULL;
+			}
+			//
+			// For 'LIST' block, should read its contents
+			//
+			if (block.type == AVI_LIST)
+			{
+				next_pos[current_level] = pos + block.length;
+				block_type[current_level++] = block.list.type;
+				pos += sizeof(RIFFListHeader) - sizeof(RIFFChunkHeader);
+				continue;
+			}
+			//
+			// Ignore any block types other than 'LIST'
+			//
+			break;
+
+		case AVI_hdrl:
+			//
+			// AVI_hdrl only appears at second-level
+			//
+			if (current_level != 2)
+			{
+				UTIL_LogOutput(LOGLEVEL_WARNING, "RIFF 'hdrl' block does not appear at second level!");
+				return NULL;
+			}
+			switch (block.type)
+			{
+			case AVI_avih:
+				//
+				// The main header should only appear once
+				//
+				if (HAS_FLAG(flags, FLAGS_AVI_MAIN_HEADER))
+				{
+					UTIL_LogOutput(LOGLEVEL_WARNING, "More than one RIFF 'avih' blocks appear!");
+					return NULL;
+				}
+				if (fread(&aviHeader, sizeof(AVIMainHeader), 1, fp) != 1)
+				{
+					UTIL_LogOutput(LOGLEVEL_WARNING, "RIFF 'avih' blocks corrupted!");
+					return NULL;
+				}
+				SwapStruct32(aviHeader, AVIMainHeader);
+				flags |= FLAGS_AVI_MAIN_HEADER;
+				if (aviHeader.dwWidth == 0 || aviHeader.dwHeight == 0)
+				{
+					UTIL_LogOutput(LOGLEVEL_WARNING, "Invalid AVI frame size!");
+					return NULL;
+				}
+				if (HAS_FLAG(aviHeader.dwFlags, AVIF_MUSTUSEINDEX))
+				{
+					UTIL_LogOutput(LOGLEVEL_WARNING, "No built-in support for index-based AVI!");
+					return NULL;
+				}
+				break;
+			case AVI_LIST:
+				if (block.list.type == AVI_strl)
+				{
+					next_pos[current_level] = pos + block.length;
+					block_type[current_level++] = block.list.type;
+					pos += sizeof(RIFFListHeader) - sizeof(RIFFChunkHeader);
+					continue;
+				}
+				break;
+			}
+			break;
+
+		case AVI_movi:
+			//
+			// AVI_movi only appears at second-level and all headers should be read before
+			//
+			if (current_level != 2 || !HAS_FLAG(flags, FLAGS_AVI_ALL_HEADERS))
+			{
+				UTIL_LogOutput(LOGLEVEL_WARNING, "RIFF 'movi' block does not appear at second level or the AVI does not contain both video & audio!");
+				return NULL;
+			}
+			//
+			// Stop parsing here as actual movie data starts
+			//
+			fseek(fp, pos - sizeof(RIFFChunkHeader), SEEK_SET);
+			avi->lVideoEndPos = next_pos[current_level - 1];
+			avi->dwMillisPerFrame = aviHeader.dwMicroSecPerFrame / 1000;
+			//
+			// Create surface
+			//
+			avi->surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
+				bih.biWidth, bih.biHeight, bih.biBitCount,
+				0x7C00, 0x03E0, 0x001F, 0x0000);
+			//
+			// Build SDL audio conversion info
+			//
+			SDL_BuildAudioCVT(&avi->cvt,
+				(wfe.format.wBitsPerSample == 8) ? AUDIO_U8 : AUDIO_S16LSB,
+				wfe.format.nChannels, wfe.format.nSamplesPerSec,
+				AUDIO_S16SYS,
+				AUDIO_GetDeviceSpec()->channels,
+				AUDIO_GetDeviceSpec()->freq);
+			//
+			// Allocate chunk buffer
+			// Since SDL converts audio in-place, we need to make the buffer large enough to hold converted data
+			//
+			avi->dwBufferSize = aviHeader.dwSuggestedBufferSize * avi->cvt.len_mult + sizeof(RIFFChunkHeader);
+			if (avi->dwBufferSize > 0)
+				avi->pChunkBuffer = UTIL_malloc(avi->dwBufferSize);
+			else
+				avi->pChunkBuffer = NULL;
+			//
+			// Allocate audio buffer, the buffer size is large enough to hold two-second audio data
+			//
+			avi->dwAudBufLen = max(wfe.format.nAvgBytesPerSec * 2, aviHeader.dwSuggestedBufferSize) * avi->cvt.len_mult;
+			avi->pbAudioBuf = (uint8_t *)UTIL_malloc(avi->dwAudBufLen);
+			avi->dwAudioReadPos = avi->dwAudioWritePos = 0;
+			return avi;
+
+		case AVI_strl:
+			//
+			// AVI_strl only appears at third-level
+			//
+			if (current_level != 3)
+			{
+				UTIL_LogOutput(LOGLEVEL_WARNING, "RIFF 'hdrl' block does not appear at third level!");
+				return NULL;
+			}
+			switch (block.type)
+			{
+			case AVI_strh:
+				// strh should be the first block of the list
+				if (streamHeader.fccType != 0)
+				{
+					UTIL_LogOutput(LOGLEVEL_WARNING, "RIFF 'strh' block does not appear at first!");
+					return NULL;
+				}
+				if (fread(&streamHeader, sizeof(AVIStreamHeader), 1, fp) != 1)
+				{
+					UTIL_LogOutput(LOGLEVEL_WARNING, "RIFF 'hdrl' block data corrupted!");
+					return NULL;
+				}
+				SwapStruct32(streamHeader, AVIStreamHeader);
+				SwapStructFields(streamHeader, wLanguage, wPriority);
+				SwapStructFields(streamHeader, rcFrame[0], rcFrame[1]);
+				SwapStructFields(streamHeader, rcFrame[2], rcFrame[3]);
+				break;
+			case AVI_strf:
+				//
+				// AVI_strf should follow AVI_strh
+				// Accept only one video stream & one audio stream
+				//
+				switch (streamHeader.fccType)
+				{
+				case AVI_vids:
+					if (HAS_FLAG(flags, FLAGS_AVI_VIDEO_FORMAT) || (streamHeader.fccHandler != VIDS_MSVC && streamHeader.fccHandler != VIDS_msvc))
+					{
+						UTIL_LogOutput(LOGLEVEL_WARNING, "The AVI uses video codec with no built-in support, or video codec appeared before!");
+						return NULL;
+					}
+					if (fread(&bih, sizeof(BitmapInfoHeader), 1, fp) != 1)
+					{
+						UTIL_LogOutput(LOGLEVEL_WARNING, "Video codec information corrupted!");
+						return NULL;
+					}
+					SwapStruct32(bih, BitmapInfoHeader);
+					SwapStructFields(bih, biPlanes, biBitCount);
+					if (bih.biBitCount != 16)
+					{
+						UTIL_LogOutput(LOGLEVEL_WARNING, "Built-in AVI playing support only 16-bit video!");
+						return NULL;
+					}
+					flags |= FLAGS_AVI_VIDEO_FORMAT;
+					break;
+				case AVI_auds:
+					if (HAS_FLAG(flags, FLAGS_AVI_AUDIO_FORMAT) || streamHeader.fccHandler != 0)
+					{
+						UTIL_LogOutput(LOGLEVEL_WARNING, "The AVI uses audio codec with no built-in support, or audio codec appeared before!");
+						return NULL;
+					}
+					if (fread(&wfe, sizeof(WAVEFormatPCM) + sizeof(uint16_t), 1, fp) != 1)
+					{
+						UTIL_LogOutput(LOGLEVEL_WARNING, "Audio codec information corrupted!");
+						return NULL;
+					}
+					SwapStruct32(wfe, WAVEFormatPCM);
+					SwapStructFields(wfe, wFormatTag, nChannels);
+					SwapStructFields(wfe, nBlockAlign, wBitsPerSample);
+					flags |= FLAGS_AVI_AUDIO_FORMAT;
+					break;
+				}
+				//
+				// One strf per strh, reset the fccType here to prepare for next strh
+				//
+				streamHeader.fccType = 0;
+				break;
+			}
+		}
+
+		//
+		// Goto next block
+		//
+		pos += block.length;
+
+		//
+		// Check if it is the end of the parent block
+		//
+		while (current_level > 0 && pos == next_pos[current_level - 1])
+		{
+			current_level--;
+		}
+		//
+		// Returns NULL if block is illegaly formed
+		//
+		if (current_level > 0 && pos > next_pos[current_level - 1])
+		{
+			return NULL;
+		}
+    }
+
+	return NULL;
+}
+
+
+static RIFFChunk *
+PAL_ReadDataChunk(
+	FILE     *fp,
+	long      endPos,
+	void     *userbuf,
+	uint32_t  buflen,
+	int       mult
+)
+{
+	RIFFBlockHeader  hdr;
+	RIFFChunk       *chunk = NULL;
+	long             pos = feof(fp) ? endPos : ftell(fp);
+
+	while (chunk == NULL && pos < endPos)
+	{
+		if (fread(&hdr, sizeof(RIFFChunkHeader), 1, fp) != 1) return NULL;
+
+		hdr.type = SDL_SwapLE32(hdr.type);
+		hdr.length = SDL_SwapLE32(hdr.length);
+		pos += sizeof(RIFFChunkHeader);
+
+		switch (hdr.type)
+		{
+		case AVI_01wb:
+		case AVI_00db:
+		case AVI_00dc:
+			//
+			// got actual audio/video frame
+			//
+			if (userbuf && buflen >= sizeof(RIFFChunkHeader) + hdr.length)
+				chunk = (RIFFChunk *)userbuf;
+			else
+				chunk = (RIFFChunk *)UTIL_malloc(sizeof(RIFFChunkHeader) + hdr.length * (hdr.type == AVI_01wb ? mult : 1));
+			if (fread(chunk->data, hdr.length, 1, fp) != 1)
+			{
+				free(chunk);
+				return NULL;
+			}
+			chunk->header = hdr.chunk;
+			break;
+
+		case AVI_LIST:
+			//
+			// Only 'rec ' LIST is allowed here, if not, skip it completely
+			//
+			if (fread(&hdr.list.type, sizeof(uint32_t), 1, fp) != 1) return NULL;
+			if (hdr.list.type == AVI_rec) break;
+		case AVI_JUNK:
+		default:
+			//
+			// Ignore unrecognized chunks
+			//
+			fseek(fp, pos += hdr.length, SEEK_SET);
+		}
+	}
+
+    return chunk;
+}
+
+static void
+PAL_AVIFeedAudio(
+    AVIPlayState   *avi,
+    uint8_t        *buffer,
+    uint32_t        size
+)
+{
+	//
+	// Convert audio in-place at the original buffer
+	// This makes filling process much more simpler
+	//
+	avi->cvt.buf = buffer;
+	avi->cvt.len = size;
+	SDL_ConvertAudio(&avi->cvt);
+	size = avi->cvt.len_cvt;
+
+	SDL_mutexP(avi->selfMutex);
+
+    while (size > 0)
+    {
+        uint32_t feed_size = (avi->dwAudioWritePos + size > avi->dwAudBufLen) ? avi->dwAudBufLen - avi->dwAudioWritePos : size;
+
+        memcpy(avi->pbAudioBuf + avi->dwAudioWritePos, buffer, feed_size);
+
+        avi->dwAudioWritePos = (avi->dwAudioWritePos + feed_size) % avi->dwAudBufLen;
+
+		buffer += feed_size;
+        size -= feed_size;
+    }
+
+    SDL_mutexV(avi->selfMutex);
+}
+
+void
+PAL_AVIInit(
+	void
+)
+{
+    gAVIPlayState.selfMutex = SDL_CreateMutex();
+}
+
+void
+PAL_AVIShutdown(
+	void
+)
+{
+    SDL_DestroyMutex(gAVIPlayState.selfMutex);
+}
+
+static void
+PAL_RenderAVIFrameToSurface(
+    SDL_Surface      *lpSurface,
+    const RIFFChunk  *lpChunk
+)
+{
+#define AV_RL16(x) ((((const uint8_t *)(x))[1] << 8) | ((const uint8_t *)(x))[0])
+#define CHECK_STREAM_PTR(n) if ((stream_ptr + n) > lpChunk->header.length) { return; }
+
+    /* decoding parameters */
+	uint16_t *pixels = (unsigned short *)lpSurface->pixels;
+	uint32_t  stream_ptr = 0, skip_blocks = 0;
+	uint32_t  stride = lpSurface->pitch >> 1;
+	const int block_inc = 4;
+	const int row_dec = stride + 4;
+	const int blocks_wide = lpSurface->w >> 2; // width in 4x4 blocks
+	const int blocks_high = lpSurface->h >> 2; // height in 4x4 blocks
+	uint32_t  total_blocks = blocks_wide * blocks_high;
+
+    for (int block_y = blocks_high; block_y > 0; block_y--)
+    {
+        int block_ptr = ((block_y * 4) - 1) * stride;
+        for (int block_x = blocks_wide; block_x > 0; block_x--)
+        {
+            // check if this block should be skipped
+            if (skip_blocks)
+            {
+                block_ptr += block_inc;
+                skip_blocks--;
+                total_blocks--;
+                continue;
+            }
+            
+            int pixel_ptr = block_ptr;
+            
+            // get the next two bytes in the encoded data stream
+            CHECK_STREAM_PTR(2);
+            uint8_t byte_a = lpChunk->data[stream_ptr++];
+			uint8_t byte_b = lpChunk->data[stream_ptr++];
+            
+            // check if the decode is finished
+            if ((byte_a == 0) && (byte_b == 0) && (total_blocks == 0))
+            {
+                return;
+            }
+            else if ((byte_b & 0xFC) == 0x84)
+            {
+                // skip code, but don't count the current block
+                skip_blocks = ((byte_b - 0x84) << 8) + byte_a - 1;
+            }
+            else if (byte_b < 0x80)
+            {
+                // 2- or 8-color encoding modes
+                uint16_t flags = (byte_b << 8) | byte_a;
+				uint16_t colors[8];
+                
+                CHECK_STREAM_PTR(4);
+                colors[0] = AV_RL16(&lpChunk->data[stream_ptr]);
+                stream_ptr += 2;
+                colors[1] = AV_RL16(&lpChunk->data[stream_ptr]);
+                stream_ptr += 2;
+                
+                if (colors[0] & 0x8000)
+                {
+                    // 8-color encoding
+                    CHECK_STREAM_PTR(12);
+                    colors[2] = AV_RL16(&lpChunk->data[stream_ptr]);
+                    stream_ptr += 2;
+                    colors[3] = AV_RL16(&lpChunk->data[stream_ptr]);
+                    stream_ptr += 2;
+                    colors[4] = AV_RL16(&lpChunk->data[stream_ptr]);
+                    stream_ptr += 2;
+                    colors[5] = AV_RL16(&lpChunk->data[stream_ptr]);
+                    stream_ptr += 2;
+                    colors[6] = AV_RL16(&lpChunk->data[stream_ptr]);
+                    stream_ptr += 2;
+                    colors[7] = AV_RL16(&lpChunk->data[stream_ptr]);
+                    stream_ptr += 2;
+                    
+                    for (int pixel_y = 0; pixel_y < 4; pixel_y++)
+                    {
+                        for (int pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1)
+                        {
+                            pixels[pixel_ptr++] =
+                            colors[((pixel_y & 0x2) << 1) +
+                                   (pixel_x & 0x2) + ((flags & 0x1) ^ 1)];
+                        }
+                        pixel_ptr -= row_dec;
+                    }
+                }
+                else
+                {
+                    // 2-color encoding
+                    for (int pixel_y = 0; pixel_y < 4; pixel_y++)
+                    {
+                        for (int pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1)
+                        {
+                            pixels[pixel_ptr++] = colors[(flags & 0x1) ^ 1];
+                        }
+                        pixel_ptr -= row_dec;
+                    }
+                }
+            }
+            else
+            {
+                // otherwise, it's a 1-color block
+				uint16_t color = (byte_b << 8) | byte_a;
+
+                for (int pixel_y = 0; pixel_y < 4; pixel_y++)
+                {
+                    for (int pixel_x = 0; pixel_x < 4; pixel_x++)
+                    {
+                        pixels[pixel_ptr++] = color;
+                    }
+                    pixel_ptr -= row_dec;
+                }
+            }
+            
+            block_ptr += block_inc;
+            total_blocks--;
+        }
+    }
+}
+
+BOOL
+PAL_PlayAVI(
+    LPCSTR     lpszPath
+)
+{
+	if (!gConfig.fEnableAviPlay) return FALSE;
+
+	//
+	// Open the file
+	//
+	FILE *fp = UTIL_OpenFile(lpszPath);
+	if (fp == NULL)
+	{
+		UTIL_LogOutput(LOGLEVEL_WARNING, "Cannot open AVI file: %s!\n", lpszPath);
+		return FALSE;
+	}
+
+	AVIPlayState *avi = PAL_ReadAVIInfo(fp, &gAVIPlayState);
+	if (avi == NULL)
+	{
+		UTIL_LogOutput(LOGLEVEL_WARNING, "Failed to parse AVI file or its format not supported!\n");
+		fclose(fp);
+		return FALSE;
+	}
+
+    PAL_ClearKeyState();
+
+	BOOL       fEndPlay = FALSE;
+	RIFFChunk *buf = (RIFFChunk *)avi->pChunkBuffer;
+	uint32_t   len = avi->dwBufferSize;
+
+    while (!fEndPlay)
+    {
+		RIFFChunk *chunk = PAL_ReadDataChunk(fp, avi->lVideoEndPos, buf, len, avi->cvt.len_mult);
+		uint32_t   dwCurrentTime = SDL_GetTicks();
+		uint32_t   dwNextFrameTime = dwCurrentTime + avi->dwMillisPerFrame;
+
+		if (chunk == NULL) break;
+
+        switch (chunk->header.type)
+        {
+        case AVI_00dc:
+        case AVI_00db:
+            //
+            // Video frame
+            //
+			PAL_RenderAVIFrameToSurface(avi->surface, chunk);
+            VIDEO_DrawSurfaceToScreen(avi->surface);
+
+            dwCurrentTime = SDL_GetTicks();
+			// Check input states here
+			UTIL_Delay(dwCurrentTime >= dwNextFrameTime ? 1 : dwNextFrameTime - dwCurrentTime);
+
+            if (g_InputState.dwKeyPress & (kKeyMenu | kKeySearch))
+            {
+                fEndPlay = TRUE;
+            }
+            break;
+
+        case AVI_01wb:
+            //
+            // Audio data, just convert it & feed into buffer
+            //
+            PAL_AVIFeedAudio(avi, chunk->data, chunk->header.length);
+			//
+			// Only enable AVI audio when data are available
+			// We do not lock on the 'if' because only this function changes 'avi->fp'
+			//
+			if (!avi->fp)
+			{
+				SDL_mutexP(avi->selfMutex);
+				avi->fp = fp;
+				SDL_mutexV(avi->selfMutex);
+			}
+            break;
+        }
+
+        if (chunk != buf) free(chunk);
+    }
+
+	SDL_mutexP(avi->selfMutex);
+	avi->fp = NULL;
+	SDL_mutexV(avi->selfMutex);
+
+    if (fEndPlay)
+    {
+        //
+        // Simulate a short delay (like the original game)
+        //
+        UTIL_Delay(500);
+    }
+
+	if (avi->surface != NULL)
+	{
+		SDL_FreeSurface(avi->surface);
+		avi->surface = NULL;
+	}
+
+	if (avi->pChunkBuffer)
+	{
+		free(avi->pChunkBuffer);
+		avi->pChunkBuffer = NULL;
+	}
+
+	if (avi->pbAudioBuf)
+	{
+		free(avi->pbAudioBuf);
+		avi->pbAudioBuf = NULL;
+	}
+
+	fclose(fp);
+	
+	return TRUE;
+}
+
+VOID SDLCALL
+AVI_FillAudioBuffer(
+	void       *udata,
+	uint8_t    *stream,
+	int         len
+)
+{
+	AVIPlayState *avi = (AVIPlayState *)udata;
+    SDL_mutexP(avi->selfMutex);
+	//
+	// We do not check whether Read pointer & Write pointer overlaps like DSound does
+	//
+	while (avi->fp != NULL && len > 0)
+	{
+		uint32_t fill_size = (avi->dwAudioReadPos + len > avi->dwAudBufLen) ? avi->dwAudBufLen - avi->dwAudioReadPos : len;
+
+		memcpy(stream, avi->pbAudioBuf + avi->dwAudioReadPos, fill_size);
+
+		avi->dwAudioReadPos = (avi->dwAudioReadPos + fill_size) % avi->dwAudBufLen;
+
+		stream += fill_size;
+		len -= fill_size;
+	}
+    SDL_mutexV(avi->selfMutex);
+}
+
+void *
+AVI_GetPlayState(
+	void
+)
+{
+	return &gAVIPlayState;
+}

+ 58 - 0
aviplay.h

@@ -0,0 +1,58 @@
+/* -*- mode: c; tab-width: 4; c-basic-offset: 4; c-file-style: "linux" -*- */
+//
+// Copyright (c) 2017, SDLPAL development team.
+// 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 AVIPLAY_H
+#define AVIPLAY_H
+
+#include "common.h"
+
+PAL_C_LINKAGE_BEGIN
+
+VOID
+PAL_AVIInit(
+    VOID
+);
+
+VOID
+PAL_AVIShutdown(
+    VOID
+);
+
+BOOL
+PAL_PlayAVI(
+    const char *lpszPath
+);
+
+void SDLCALL
+AVI_FillAudioBuffer(
+    void       *udata,
+    uint8_t    *stream,
+    int         len
+);
+
+void *
+AVI_GetPlayState(
+	void
+);
+
+PAL_C_LINKAGE_END
+
+#endif

+ 90 - 57
ending.c

@@ -397,83 +397,116 @@ VOID
 PAL_EndingScreen(
    VOID
 )
+/*++
+ Purpose:
+ 
+   Show the ending screen for Win95 version.
+
+ Parameters:
+
+   None.
+
+ Return value:
+
+   None.
+
+--*/
 {
-	AUDIO_PlayMusic(0x1a, TRUE, 0);
-	PAL_RNGPlay(gpGlobals->iCurPlayingRNG, 110, 150, 7);
-	PAL_RNGPlay(gpGlobals->iCurPlayingRNG, 151, 999, 9);
+    //
+    // Use AVI & WIN95's music if we can
+	// Otherwise, simulate the ending of DOS version
+	//
+	PAL_PlayAVI("4.avi");
 
-	PAL_FadeOut(2);
+	if (!PAL_PlayAVI("5.avi"))
+	{
+		BOOL win_music = AUDIO_PlayCDTrack(12);
 
-	AUDIO_PlayMusic(0x19, TRUE, 0);
+		if (!win_music) AUDIO_PlayMusic(0x1a, TRUE, 0);
+		PAL_RNGPlay(gpGlobals->iCurPlayingRNG, 110, 150, 7);
+		PAL_RNGPlay(gpGlobals->iCurPlayingRNG, 151, 999, 9);
 
-	PAL_ShowFBP(75, 0);
-	PAL_FadeIn(5, FALSE, 1);
-	PAL_ScrollFBP(74, 0xf, TRUE);
+		PAL_FadeOut(2);
 
-	PAL_FadeOut(1);
+		if (!win_music) AUDIO_PlayMusic(0x19, TRUE, 0);
 
-	SDL_FillRect(gpScreen, NULL, 0);
-	gpGlobals->wNumPalette = 4;
-	gpGlobals->fNeedToFadeIn = TRUE;
-	PAL_EndingAnimation();
+		PAL_ShowFBP(75, 0);
+		PAL_FadeIn(5, FALSE, 1);
+		PAL_ScrollFBP(74, 0xf, TRUE);
 
-	AUDIO_PlayMusic(0, FALSE, 2);
-	PAL_ColorFade(7, 15, FALSE);
+		PAL_FadeOut(1);
 
-	if (!AUDIO_PlayCDTrack(2))
-	{
-		AUDIO_PlayMusic(0x11, TRUE, 0);
-	}
+		SDL_FillRect(gpScreen, NULL, 0);
+		gpGlobals->wNumPalette = 4;
+		gpGlobals->fNeedToFadeIn = TRUE;
+		PAL_EndingAnimation();
 
-	SDL_FillRect(gpScreen, NULL, 0);
-	PAL_SetPalette(0, FALSE);
-	PAL_RNGPlay(0xb, 0, 999, 7);
+		if (!win_music) AUDIO_PlayMusic(0, FALSE, 2);
+		PAL_ColorFade(7, 15, FALSE);
 
-	PAL_FadeOut(2);
+		if (!win_music && !AUDIO_PlayCDTrack(2))
+		{
+			AUDIO_PlayMusic(0x11, TRUE, 0);
+		}
 
-	SDL_FillRect(gpScreen, NULL, 0);
-	gpGlobals->wNumPalette = 8;
-	gpGlobals->fNeedToFadeIn = TRUE;
-	PAL_RNGPlay(10, 0, 999, 6);
+		SDL_FillRect(gpScreen, NULL, 0);
+		PAL_SetPalette(0, FALSE);
+		PAL_RNGPlay(0xb, 0, 999, 7);
 
-	PAL_EndingSetEffectSprite(0);
-	PAL_ShowFBP(77, 10);
+		PAL_FadeOut(2);
 
-	VIDEO_BackupScreen(gpScreen);
+		SDL_FillRect(gpScreen, NULL, 0);
+		gpGlobals->wNumPalette = 8;
+		gpGlobals->fNeedToFadeIn = TRUE;
+		PAL_RNGPlay(10, 0, 999, 6);
 
-	PAL_EndingSetEffectSprite(0x27b);
-	PAL_ShowFBP(76, 7);
+		PAL_EndingSetEffectSprite(0);
+		PAL_ShowFBP(77, 10);
 
-	PAL_SetPalette(5, FALSE);
-	PAL_ShowFBP(73, 7);
-	PAL_ScrollFBP(72, 0xf, TRUE);
+		VIDEO_BackupScreen(gpScreen);
 
-	PAL_ShowFBP(71, 7);
-	PAL_ShowFBP(68, 7);
+		PAL_EndingSetEffectSprite(0x27b);
+		PAL_ShowFBP(76, 7);
 
-	PAL_EndingSetEffectSprite(0);
-	PAL_ShowFBP(68, 6);
+		PAL_SetPalette(5, FALSE);
+		PAL_ShowFBP(73, 7);
+		PAL_ScrollFBP(72, 0xf, TRUE);
 
-	PAL_WaitForKey(0);
-	AUDIO_PlayMusic(0, FALSE, 1);
-	UTIL_Delay(500);
+		PAL_ShowFBP(71, 7);
+		PAL_ShowFBP(68, 7);
 
-	if (!AUDIO_PlayCDTrack(13))
+		PAL_EndingSetEffectSprite(0);
+		PAL_ShowFBP(68, 6);
+
+		PAL_WaitForKey(0);
+		AUDIO_PlayMusic(0, FALSE, 1);
+		UTIL_Delay(500);
+	}
+	else
 	{
-		AUDIO_PlayMusic(9, TRUE, 0);
+		gpGlobals->fNeedToFadeIn = FALSE;
+		PAL_SetPalette(5, FALSE);
+		PAL_EndingSetEffectSprite(0);
 	}
 
-	PAL_ScrollFBP(67, 0xf, TRUE);
-	PAL_ScrollFBP(66, 0xf, TRUE);
-	PAL_ScrollFBP(65, 0xf, TRUE);
-	PAL_ScrollFBP(64, 0xf, TRUE);
-	PAL_ScrollFBP(63, 0xf, TRUE);
-	PAL_ScrollFBP(62, 0xf, TRUE);
-	PAL_ScrollFBP(61, 0xf, TRUE);
-	PAL_ScrollFBP(60, 0xf, TRUE);
-	PAL_ScrollFBP(59, 0xf, TRUE);
-
-	AUDIO_PlayMusic(0, FALSE, 6);
-	PAL_FadeOut(3);
+	if (!PAL_PlayAVI("6.avi"))
+	{
+		if (!AUDIO_PlayCDTrack(13))
+		{
+			AUDIO_PlayMusic(9, TRUE, 0);
+		}
+
+		PAL_ScrollFBP(67, 0xf, TRUE);
+		PAL_ScrollFBP(66, 0xf, TRUE);
+		PAL_ScrollFBP(65, 0xf, TRUE);
+		PAL_ScrollFBP(64, 0xf, TRUE);
+		PAL_ScrollFBP(63, 0xf, TRUE);
+		PAL_ScrollFBP(62, 0xf, TRUE);
+		PAL_ScrollFBP(61, 0xf, TRUE);
+		PAL_ScrollFBP(60, 0xf, TRUE);
+		PAL_ScrollFBP(59, 0xf, TRUE);
+
+		AUDIO_PlayMusic(0, FALSE, 6);
+		PAL_FadeOut(3);
+	}
 }
-

+ 5 - 0
global.c

@@ -187,6 +187,11 @@ PAL_InitGlobals(
    //
    if (!PAL_IsWINVersion(&gConfig.fIsWIN95)) return -1;
 
+   //
+   // Enable AVI playing only when the resource is WIN95
+   //
+   gConfig.fEnableAviPlay = gConfig.fEnableAviPlay && gConfig.fIsWIN95;
+
    //
    // Detect game language only when no message file specified
    //

+ 15 - 2
ios/SDLPal/SDLPal.xcodeproj/project.pbxproj

@@ -95,11 +95,12 @@
 		716552AE195BBE7D006E1227 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 716552AD195BBE7D006E1227 /* AudioToolbox.framework */; };
 		716552B0195BBECE006E1227 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 716552AF195BBECE006E1227 /* QuartzCore.framework */; };
 		71AA35801A0BCA4E00793FFF /* util.mm in Sources */ = {isa = PBXBuildFile; fileRef = 71AA357F1A0BCA4E00793FFF /* util.mm */; };
+		71DCB6961ED9CBB000F120DB /* aviplay.c in Sources */ = {isa = PBXBuildFile; fileRef = 71DCB6941ED9CBB000F120DB /* aviplay.c */; };
+		71DCB6991ED9CCFE00F120DB /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C622BE731E474CE2000C8970 /* GameController.framework */; };
 		C622BE661E474B5E000C8970 /* audio.c in Sources */ = {isa = PBXBuildFile; fileRef = C622BE641E474B5E000C8970 /* audio.c */; };
 		C622BE6C1E474C01000C8970 /* palcfg.c in Sources */ = {isa = PBXBuildFile; fileRef = C622BE6A1E474C01000C8970 /* palcfg.c */; };
 		C622BE701E474CA9000C8970 /* overlay.c in Sources */ = {isa = PBXBuildFile; fileRef = C622BE6F1E474CA9000C8970 /* overlay.c */; };
 		C622BE721E474CD5000C8970 /* CoreMotion.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C622BE711E474CD5000C8970 /* CoreMotion.framework */; };
-		C622BE741E474CE2000C8970 /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C622BE731E474CE2000C8970 /* GameController.framework */; };
 		C622BE761E474CFC000C8970 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C622BE751E474CFC000C8970 /* AVFoundation.framework */; };
 		C622BE771E474DF5000C8970 /* util.c in Sources */ = {isa = PBXBuildFile; fileRef = 71655251195BB6DB006E1227 /* util.c */; };
 		C63505731EA6570300186049 /* midi.c in Sources */ = {isa = PBXBuildFile; fileRef = C635056B1EA6570300186049 /* midi.c */; };
@@ -337,6 +338,8 @@
 		716552AD195BBE7D006E1227 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
 		716552AF195BBECE006E1227 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
 		71AA357F1A0BCA4E00793FFF /* util.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = util.mm; sourceTree = "<group>"; };
+		71DCB6941ED9CBB000F120DB /* aviplay.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = aviplay.c; path = ../../../aviplay.c; sourceTree = "<group>"; };
+		71DCB6951ED9CBB000F120DB /* aviplay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = aviplay.h; path = ../../../aviplay.h; sourceTree = "<group>"; };
 		C622BE4E1E474AAF000C8970 /* SDL.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SDL.xcodeproj; path = "../../3rd/SDL/Xcode-iOS/SDL/SDL.xcodeproj"; sourceTree = "<group>"; };
 		C622BE641E474B5E000C8970 /* audio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = audio.c; path = ../../../audio.c; sourceTree = "<group>"; };
 		C622BE651E474B5E000C8970 /* audio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = audio.h; path = ../../../audio.h; sourceTree = "<group>"; };
@@ -364,8 +367,8 @@
 			files = (
 				C6E974A21E49C24500F76B17 /* libSDL2.a in Frameworks */,
 				C622BE761E474CFC000C8970 /* AVFoundation.framework in Frameworks */,
-				C622BE741E474CE2000C8970 /* GameController.framework in Frameworks */,
 				C622BE721E474CD5000C8970 /* CoreMotion.framework in Frameworks */,
+				71DCB6991ED9CCFE00F120DB /* GameController.framework in Frameworks */,
 				716552B0195BBECE006E1227 /* QuartzCore.framework in Frameworks */,
 				716552AE195BBE7D006E1227 /* AudioToolbox.framework in Frameworks */,
 				716552AA195BBE1B006E1227 /* CoreAudio.framework in Frameworks */,
@@ -576,6 +579,7 @@
 			isa = PBXGroup;
 			children = (
 				C63505771EA6575200186049 /* Headers */,
+				71DCB6941ED9CBB000F120DB /* aviplay.c */,
 				C635056B1EA6570300186049 /* midi.c */,
 				C635056C1EA6570300186049 /* private.c */,
 				57FB015C1B7A50E0005FCF4C /* mp3play.c */,
@@ -703,6 +707,7 @@
 		C63505771EA6575200186049 /* Headers */ = {
 			isa = PBXGroup;
 			children = (
+				71DCB6951ED9CBB000F120DB /* aviplay.h */,
 				C626FFBF1E5BCBE700E39DD9 /* pal_config.h */,
 				C635056A1EA6570300186049 /* midi.h */,
 				57FB015A1B7A50E0005FCF4C /* codepage.h */,
@@ -796,6 +801,7 @@
 				ORGANIZATIONNAME = "SDLPAL team";
 				TargetAttributes = {
 					7165505C195BB372006E1227 = {
+						DevelopmentTeam = 8M76ZZN98N;
 						ProvisioningStyle = Automatic;
 					};
 				};
@@ -954,6 +960,7 @@
 				71655287195BB6DB006E1227 /* sound.c in Sources */,
 				71655288195BB6DB006E1227 /* text.c in Sources */,
 				57FB01461B7A50B0005FCF4C /* codebook.c in Sources */,
+				71DCB6961ED9CBB000F120DB /* aviplay.c in Sources */,
 				71655289195BB6DB006E1227 /* ui.c in Sources */,
 				7165528A195BB6DB006E1227 /* uibattle.c in Sources */,
 				7165528B195BB6DB006E1227 /* uigame.c in Sources */,
@@ -1037,6 +1044,7 @@
 		7165507B195BB372006E1227 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				CODE_SIGN_IDENTITY = "iPhone Developer";
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
 				DEVELOPMENT_TEAM = "";
 				GCC_PRECOMPILE_PREFIX_HEADER = NO;
@@ -1053,9 +1061,11 @@
 					../../liboggvorbis/src,
 				);
 				INFOPLIST_FILE = "SDLPal/SDLPal-Info.plist";
+				IPHONEOS_DEPLOYMENT_TARGET = 7.0;
 				PRODUCT_BUNDLE_IDENTIFIER = io.github.sdlpal;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PROVISIONING_PROFILE_SPECIFIER = "";
+				TARGETED_DEVICE_FAMILY = "1,2";
 				WRAPPER_EXTENSION = app;
 			};
 			name = Debug;
@@ -1063,6 +1073,7 @@
 		7165507C195BB372006E1227 /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				CODE_SIGN_IDENTITY = "iPhone Developer";
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
 				DEVELOPMENT_TEAM = "";
 				GCC_PRECOMPILE_PREFIX_HEADER = NO;
@@ -1078,9 +1089,11 @@
 					../../liboggvorbis/src,
 				);
 				INFOPLIST_FILE = "SDLPal/SDLPal-Info.plist";
+				IPHONEOS_DEPLOYMENT_TARGET = 7.0;
 				PRODUCT_BUNDLE_IDENTIFIER = io.github.sdlpal;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PROVISIONING_PROFILE_SPECIFIER = "";
+				TARGETED_DEVICE_FAMILY = "1,2";
 				WRAPPER_EXTENSION = app;
 			};
 			name = Release;

+ 8 - 0
macos/Pal.xcodeproj/project.pbxproj

@@ -66,6 +66,7 @@
 		7104FDB40D772FBC00A97E53 /* player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7104FDA50D772FBC00A97E53 /* player.cpp */; };
 		7104FDB60D772FBC00A97E53 /* rix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7104FDA70D772FBC00A97E53 /* rix.cpp */; };
 		71147E4014085E31003FB2DB /* surroundopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71147E3E14085E31003FB2DB /* surroundopl.cpp */; };
+		713765641ED86C930084F495 /* aviplay.c in Sources */ = {isa = PBXBuildFile; fileRef = 713765621ED86C930084F495 /* aviplay.c */; };
 		7138FD0F1424E4810060DE76 /* dosbox_opl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7138FD0C1424E4810060DE76 /* dosbox_opl.cpp */; };
 		716EB9BC0D77318900D5DE1F /* game.c in Sources */ = {isa = PBXBuildFile; fileRef = 7104FD3F0D772F6300A97E53 /* game.c */; };
 		716EB9C70D77340300D5DE1F /* sdlpal.icns in Resources */ = {isa = PBXBuildFile; fileRef = 716EB9C60D77340300D5DE1F /* sdlpal.icns */; };
@@ -315,8 +316,11 @@
 		7104FDA60D772FBC00A97E53 /* player.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = player.h; path = adplug/player.h; sourceTree = "<group>"; };
 		7104FDA70D772FBC00A97E53 /* rix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rix.cpp; path = adplug/rix.cpp; sourceTree = "<group>"; };
 		7104FDA80D772FBC00A97E53 /* rix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rix.h; path = adplug/rix.h; sourceTree = "<group>"; };
+		71110B6B1EDAFAC20002E505 /* riff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = riff.h; sourceTree = "<group>"; };
 		71147E3E14085E31003FB2DB /* surroundopl.cpp */ = {isa = PBXFileReference; fileEncoding = 0; lastKnownFileType = sourcecode.cpp.cpp; name = surroundopl.cpp; path = adplug/surroundopl.cpp; sourceTree = "<group>"; };
 		71147E3F14085E31003FB2DB /* surroundopl.h */ = {isa = PBXFileReference; fileEncoding = 0; lastKnownFileType = sourcecode.c.h; name = surroundopl.h; path = adplug/surroundopl.h; sourceTree = "<group>"; };
+		713765621ED86C930084F495 /* aviplay.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aviplay.c; sourceTree = "<group>"; };
+		713765631ED86C930084F495 /* aviplay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aviplay.h; sourceTree = "<group>"; };
 		7138FD0B1424E4810060DE76 /* demuopl.h */ = {isa = PBXFileReference; fileEncoding = 18446744071562067968; lastKnownFileType = sourcecode.c.h; name = demuopl.h; path = adplug/demuopl.h; sourceTree = "<group>"; };
 		7138FD0C1424E4810060DE76 /* dosbox_opl.cpp */ = {isa = PBXFileReference; fileEncoding = 18446744071562067968; lastKnownFileType = sourcecode.cpp.cpp; name = dosbox_opl.cpp; path = adplug/dosbox_opl.cpp; sourceTree = "<group>"; };
 		7138FD0D1424E4810060DE76 /* dosbox_opl.h */ = {isa = PBXFileReference; fileEncoding = 18446744071562067968; lastKnownFileType = sourcecode.c.h; name = dosbox_opl.h; path = adplug/dosbox_opl.h; sourceTree = "<group>"; };
@@ -469,6 +473,7 @@
 		29B97315FDCFA39411CA2CEA /* Sources */ = {
 			isa = PBXGroup;
 			children = (
+				713765621ED86C930084F495 /* aviplay.c */,
 				7104FD3A0D772F6300A97E53 /* battle.c */,
 				71F0F6D10DAA63B400F88C16 /* ending.c */,
 				71E27E030D8C7E2F0048BA16 /* fight.c */,
@@ -780,6 +785,7 @@
 			children = (
 				C626FFC31E5BD49100E39DD9 /* pal_config.h */,
 				7104FD390D772F6300A97E53 /* ascii.h */,
+				713765631ED86C930084F495 /* aviplay.h */,
 				7104FD3B0D772F6300A97E53 /* battle.h */,
 				5757D2981B7A3E0300464A74 /* codepage.h */,
 				7104FD3C0D772F6300A97E53 /* common.h */,
@@ -800,6 +806,7 @@
 				5757D2921B7A3DCE00464A74 /* players.h */,
 				7104FD510D772F6300A97E53 /* res.h */,
 				5757D2941B7A3DCE00464A74 /* resampler.h */,
+				71110B6B1EDAFAC20002E505 /* riff.h */,
 				7104FD550D772F6300A97E53 /* rngplay.h */,
 				7104FD570D772F6300A97E53 /* scene.h */,
 				7104FD590D772F6300A97E53 /* script.h */,
@@ -1073,6 +1080,7 @@
 				C63DF68B1EC50B7300C778B8 /* util.mm in Sources */,
 				71E23EA913F6D1AD001287B6 /* stream.c in Sources */,
 				71E23EAB13F6D1AD001287B6 /* synth.c in Sources */,
+				713765641ED86C930084F495 /* aviplay.c in Sources */,
 				5757D2961B7A3DCE00464A74 /* oggplay.c in Sources */,
 				71E23EAD13F6D1AD001287B6 /* timer.c in Sources */,
 				5757D2741B7A3D5800464A74 /* codebook.c in Sources */,

+ 6 - 0
main.c

@@ -100,6 +100,7 @@ PAL_Init(
    PAL_InitInput();
    PAL_InitResources();
    AUDIO_OpenDevice();
+   PAL_AVIInit();
 
    VIDEO_SetWindowTitle(va("Pal %s%s%s",
 	   gConfig.fIsWIN95 ? "Win95" : "DOS",
@@ -136,6 +137,7 @@ PAL_Shutdown(
 --*/
 {
    AUDIO_CloseDevice();
+   PAL_AVIShutdown();
    PAL_FreeFont();
    PAL_FreeResources();
    PAL_FreeGlobals();
@@ -169,6 +171,8 @@ PAL_TrademarkScreen(
 
 --*/
 {
+   if (PAL_PlayAVI("1.avi")) return;
+
    PAL_SetPalette(3, FALSE);
    PAL_RNGPlay(6, 0, 1000, 25);
    UTIL_Delay(1000);
@@ -205,6 +209,8 @@ PAL_SplashScreen(
    DWORD          dwTime, dwBeginTime;
    BOOL           fUseCD = TRUE;
 
+   if (PAL_PlayAVI("2.avi")) return;
+
    if (palette == NULL)
    {
       fprintf(stderr, "ERROR: PAL_SplashScreen(): palette == NULL\n");

+ 1 - 0
main.h

@@ -51,6 +51,7 @@
 #include "play.h"
 #include "game.h"
 #include "midi.h"
+#include "aviplay.h"
 
 VOID
 PAL_Shutdown(

+ 5 - 0
palcfg.c

@@ -42,6 +42,7 @@ static const ConfigItem gConfigItems[PALCFG_ALL_MAX] = {
 	{ PALCFG_STEREO,            PALCFG_BOOLEAN,  "Stereo",             6, MAKE_VALUE(TRUE,                          FALSE,                 TRUE) },                  // Default for stereo audio
 	{ PALCFG_USESURROUNDOPL,    PALCFG_BOOLEAN,  "UseSurroundOPL",    14, MAKE_VALUE(TRUE,                          FALSE,                 TRUE) },                  // Default for using surround opl
 	{ PALCFG_USETOUCHOVERLAY,   PALCFG_BOOLEAN,  "UseTouchOverlay",   15, MAKE_VALUE(PAL_HAS_TOUCH,                 FALSE,                 TRUE) },
+	{ PALCFG_ENABLEAVIPLAY,     PALCFG_BOOLEAN,  "EnableAviPlay",     13, MAKE_VALUE(TRUE,                          FALSE,                 TRUE) },
 
 	{ PALCFG_SURROUNDOPLOFFSET, PALCFG_INTEGER,  "SurroundOPLOffset", 17, MAKE_VALUE(384,                           INT32_MIN,             INT32_MAX) },
 	{ PALCFG_LOGLEVEL,          PALCFG_INTEGER,  "LogLevel",           8, MAKE_VALUE(PAL_DEFAULT_LOGLEVEL,          LOGLEVEL_MIN,          LOGLEVEL_MAX) },
@@ -439,6 +440,7 @@ PAL_LoadConfig(
 	gConfig.fUseTouchOverlay = values[PALCFG_USETOUCHOVERLAY].bValue;
 	gConfig.fKeepAspectRatio = values[PALCFG_KEEPASPECTRATIO].bValue;
 	gConfig.fFullScreen = values[PALCFG_FULLSCREEN].bValue;
+	gConfig.fEnableAviPlay = values[PALCFG_ENABLEAVIPLAY].bValue;
 	gConfig.iAudioChannels = values[PALCFG_STEREO].bValue ? 2 : 1;
 
 	gConfig.iSurroundOPLOffset = values[PALCFG_SURROUNDOPLOFFSET].iValue;
@@ -480,6 +482,7 @@ PAL_SaveConfig(
 		sprintf(buf, "%s=%d\n", PAL_ConfigName(PALCFG_STEREO), gConfig.iAudioChannels == 2 ? TRUE : FALSE); fputs(buf, fp);
 		sprintf(buf, "%s=%d\n", PAL_ConfigName(PALCFG_USESURROUNDOPL), gConfig.fUseSurroundOPL); fputs(buf, fp);
 		sprintf(buf, "%s=%d\n", PAL_ConfigName(PALCFG_USETOUCHOVERLAY), gConfig.fUseTouchOverlay); fputs(buf, fp);
+		sprintf(buf, "%s=%d\n", PAL_ConfigName(PALCFG_ENABLEAVIPLAY), gConfig.fEnableAviPlay); fputs(buf, fp);
 
 		sprintf(buf, "%s=%d\n", PAL_ConfigName(PALCFG_SURROUNDOPLOFFSET), gConfig.iSurroundOPLOffset); fputs(buf, fp);
 		sprintf(buf, "%s=%d\n", PAL_ConfigName(PALCFG_LOGLEVEL), gConfig.iLogLevel); fputs(buf, fp);
@@ -530,6 +533,7 @@ PAL_GetConfigItem(
 		case PALCFG_STEREO:            value.bValue = (gConfig.iAudioChannels == 2); break;
 		case PALCFG_USESURROUNDOPL:    value.bValue = gConfig.fUseSurroundOPL; break;
 		case PALCFG_USETOUCHOVERLAY:   value.bValue = gConfig.fUseTouchOverlay; break;
+		case PALCFG_ENABLEAVIPLAY:     value.bValue = gConfig.fEnableAviPlay; break;
 		case PALCFG_SURROUNDOPLOFFSET: value.iValue = gConfig.iSurroundOPLOffset; break;
 		case PALCFG_LOGLEVEL:          value.iValue = gConfig.iLogLevel; break;
 		case PALCFG_AUDIOBUFFERSIZE:   value.uValue = gConfig.wAudioBufferSize; break;
@@ -570,6 +574,7 @@ PAL_SetConfigItem(
 	case PALCFG_STEREO:            gConfig.iAudioChannels = value.bValue ? 2 : 1; break;
 	case PALCFG_USESURROUNDOPL:    gConfig.fUseSurroundOPL = value.bValue; break;
 	case PALCFG_USETOUCHOVERLAY:   gConfig.fUseTouchOverlay = value.bValue; break;
+	case PALCFG_ENABLEAVIPLAY:     gConfig.fEnableAviPlay = value.bValue; break;
 	case PALCFG_SURROUNDOPLOFFSET: gConfig.iSurroundOPLOffset = value.iValue; break;
 	case PALCFG_LOGLEVEL:          gConfig.iLogLevel = value.iValue; break;
 	case PALCFG_AUDIOBUFFERSIZE:   gConfig.wAudioBufferSize = value.uValue; break;

+ 2 - 0
palcfg.h

@@ -44,6 +44,7 @@ typedef enum tagPALCFG_ITEM
 	PALCFG_STEREO,
 	PALCFG_USESURROUNDOPL,
 	PALCFG_USETOUCHOVERLAY,
+	PALCFG_ENABLEAVIPLAY,
 	/* Booleans */
 	PALCFG_BOOLEAN_MAX,
 
@@ -190,6 +191,7 @@ typedef struct tagCONFIGURATION
 	BOOL             fUseCustomScreenLayout;
 	BOOL             fLaunchSetting;
 	BOOL             fUseTouchOverlay;
+	BOOL             fEnableAviPlay;
 #if USE_RIX_EXTRA_INIT
 	uint32_t        *pExtraFMRegs;
 	uint8_t         *pExtraFMVals;

+ 133 - 0
riff.h

@@ -0,0 +1,133 @@
+#ifndef PAL_RIFF_H
+#define PAL_RIFF_H
+
+#include <stdint.h>
+
+typedef struct RIFFHeader
+{
+	uint32_t signature;         /* 'RIFF' */
+	uint32_t length;            /* Total length minus eight, little-endian */
+	uint32_t type;              /* 'WAVE', 'AVI ', ... */
+} RIFFHeader;
+
+typedef struct RIFFChunkHeader
+{
+	uint32_t type;             /* 'fmt ', 'hdrl', 'movi' and so on */
+	uint32_t length;           /* Total chunk length minus eight, little-endian */
+} RIFFChunkHeader;
+
+typedef struct RIFFChunk
+{
+	RIFFChunkHeader header;
+	uint8_t         data[1];
+} RIFFChunk;
+
+typedef struct RIFFListHeader
+{
+	uint32_t signature;        /* 'LIST' */
+	uint32_t length;           /* Total list length minus eight, little-endian */
+	uint32_t type;             /* 'fmt ', 'hdrl', 'movi' and so on */
+} RIFFListHeader;
+
+typedef union RIFFBlockHeader
+{
+	struct {
+		uint32_t  type;
+		uint32_t  length;
+	};
+	RIFFChunkHeader chunk;
+	RIFFListHeader  list;
+} RIFFBlockHeader;
+
+typedef struct WAVEFormatPCM
+{
+	uint16_t wFormatTag;      /* format type */
+	uint16_t nChannels;       /* number of channels (i.e. mono, stereo, etc.) */
+	uint32_t nSamplesPerSec;  /* sample rate */
+	uint32_t nAvgBytesPerSec; /* for buffer estimation */
+	uint16_t nBlockAlign;     /* block size of data */
+	uint16_t wBitsPerSample;
+} WAVEFormatPCM;
+
+typedef struct WAVEFormatEx
+{
+	WAVEFormatPCM format;
+	uint16_t      cbSize;
+} WAVEFormatEx;
+
+typedef struct AVIMainHeader
+{
+	uint32_t      dwMicroSecPerFrame; // frame display rate (or 0)
+	uint32_t      dwMaxBytesPerSec; // max. transfer rate
+	uint32_t      dwPaddingGranularity; // pad to multiples of this size
+#define AVIF_HASINDEX        0x00000010 // Index at end of file?
+#define AVIF_MUSTUSEINDEX    0x00000020
+#define AVIF_ISINTERLEAVED   0x00000100
+	uint32_t      dwFlags; // the ever-present flags
+	uint32_t      dwTotalFrames; // # frames in file
+	uint32_t      dwInitialFrames;
+	uint32_t      dwStreams;
+	uint32_t      dwSuggestedBufferSize;
+	uint32_t      dwWidth;
+	uint32_t      dwHeight;
+	uint32_t      dwReserved[4];
+} AVIMainHeader;
+
+typedef struct AVIStreamHeader
+{
+	uint32_t      fccType;
+	uint32_t      fccHandler;
+	uint32_t      dwFlags;
+	uint16_t      wPriority;
+	uint16_t      wLanguage;
+	uint32_t      dwInitialFrames;
+	uint32_t      dwScale;
+	uint32_t      dwRate; /* dwRate / dwScale == samples/second */
+	uint32_t      dwStart;
+	uint32_t      dwLength; /* In units above... */
+	uint32_t      dwSuggestedBufferSize;
+	uint32_t      dwQuality;
+	uint32_t      dwSampleSize;
+	uint16_t      rcFrame[4];
+} AVIStreamHeader;
+
+typedef struct BitmapInfoHeader
+{
+	uint32_t      biSize;
+	uint32_t      biWidth;
+	uint32_t      biHeight;
+	uint16_t      biPlanes;
+	uint16_t      biBitCount;
+	uint32_t      biCompression;
+	uint32_t      biSizeImage;
+	uint32_t      biXPelsPerMeter;
+	uint32_t      biYPelsPerMeter;
+	uint32_t      biClrUsed;
+	uint32_t      biClrImportant;
+} BitmapInfoHeader;
+
+#define RIFF_RIFF (((uint32_t)'R') | (((uint32_t)'I') << 8) | (((uint32_t)'F') << 16) | (((uint32_t)'F') << 24))
+
+#define RIFF_WAVE (((uint32_t)'W') | (((uint32_t)'A') << 8) | (((uint32_t)'V') << 16) | (((uint32_t)'E') << 24))
+#define WAVE_fmt  (((uint32_t)'f') | (((uint32_t)'m') << 8) | (((uint32_t)'t') << 16) | (((uint32_t)' ') << 24))
+#define WAVE_data (((uint32_t)'d') | (((uint32_t)'a') << 8) | (((uint32_t)'t') << 16) | (((uint32_t)'a') << 24))
+
+#define RIFF_AVI  (((uint32_t)'A') | (((uint32_t)'V') << 8) | (((uint32_t)'I') << 16) | (((uint32_t)' ') << 24))
+#define AVI_hdrl  (((uint32_t)'h') | (((uint32_t)'d') << 8) | (((uint32_t)'r') << 16) | (((uint32_t)'l') << 24))
+#define AVI_strl  (((uint32_t)'s') | (((uint32_t)'t') << 8) | (((uint32_t)'r') << 16) | (((uint32_t)'l') << 24))
+#define AVI_strh  (((uint32_t)'s') | (((uint32_t)'t') << 8) | (((uint32_t)'r') << 16) | (((uint32_t)'h') << 24))
+#define AVI_strf  (((uint32_t)'s') | (((uint32_t)'t') << 8) | (((uint32_t)'r') << 16) | (((uint32_t)'f') << 24))
+#define AVI_avih  (((uint32_t)'a') | (((uint32_t)'v') << 8) | (((uint32_t)'i') << 16) | (((uint32_t)'h') << 24))
+#define AVI_LIST  (((uint32_t)'L') | (((uint32_t)'I') << 8) | (((uint32_t)'S') << 16) | (((uint32_t)'T') << 24))
+#define AVI_movi  (((uint32_t)'m') | (((uint32_t)'o') << 8) | (((uint32_t)'v') << 16) | (((uint32_t)'i') << 24))
+#define AVI_01wb  (((uint32_t)'0') | (((uint32_t)'1') << 8) | (((uint32_t)'w') << 16) | (((uint32_t)'b') << 24))
+#define AVI_00dc  (((uint32_t)'0') | (((uint32_t)'0') << 8) | (((uint32_t)'d') << 16) | (((uint32_t)'c') << 24))
+#define AVI_00db  (((uint32_t)'0') | (((uint32_t)'0') << 8) | (((uint32_t)'d') << 16) | (((uint32_t)'b') << 24))
+#define AVI_rec   (((uint32_t)'r') | (((uint32_t)'e') << 8) | (((uint32_t)'c') << 16) | (((uint32_t)' ') << 24))
+#define AVI_JUNK  (((uint32_t)'J') | (((uint32_t)'U') << 8) | (((uint32_t)'N') << 16) | (((uint32_t)'K') << 24))
+#define AVI_vids  (((uint32_t)'v') | (((uint32_t)'i') << 8) | (((uint32_t)'d') << 16) | (((uint32_t)'s') << 24))
+#define AVI_auds  (((uint32_t)'a') | (((uint32_t)'u') << 8) | (((uint32_t)'d') << 16) | (((uint32_t)'s') << 24))
+#define VIDS_MSVC (((uint32_t)'M') | (((uint32_t)'S') << 8) | (((uint32_t)'V') << 16) | (((uint32_t)'C') << 24))
+#define VIDS_msvc (((uint32_t)'m') | (((uint32_t)'s') << 8) | (((uint32_t)'v') << 16) | (((uint32_t)'c') << 24))
+
+#endif

+ 21 - 62
sound.c

@@ -28,6 +28,7 @@
 #include "util.h"
 #include "resampler.h"
 #include "midi.h"
+#include "riff.h"
 #include <math.h>
 
 #if PAL_HAS_OGG
@@ -74,32 +75,6 @@ typedef struct tagSOUNDPLAYER
 	int                 cursounds;
 } SOUNDPLAYER, *LPSOUNDPLAYER;
 
-typedef struct tagRIFFHEADER
-{
-	DWORD   riff_sig;		/* 'RIFF' */
-	DWORD   data_length;	/* Total length minus eight, little-endian */
-	DWORD   riff_type;		/* 'WAVE' */
-} RIFFHEADER, *LPRIFFHEADER;
-typedef const RIFFHEADER *LPCRIFFHEADER;
-
-typedef struct tagRIFFCHUNK
-{
-	DWORD   chunk_type;		/* 'fmt ' and so on */
-	DWORD   chunk_length;	/* Total chunk length minus eight, little-endian */
-} RIFFCHUNK, *LPRIFFCHUNK;
-typedef const RIFFCHUNK *LPCRIFFCHUNK;
-
-typedef struct tagWAVEFORMATPCM
-{
-	WORD    wFormatTag;        /* format type */
-	WORD    nChannels;         /* number of channels (i.e. mono, stereo, etc.) */
-	DWORD   nSamplesPerSec;    /* sample rate */
-	DWORD   nAvgBytesPerSec;   /* for buffer estimation */
-	WORD    nBlockAlign;       /* block size of data */
-	WORD    wBitsPerSample;
-} WAVEFORMATPCM, *LPWAVEFORMATPCM;
-typedef const WAVEFORMATPCM *LPCWAVEFORMATPCM;
-
 static const void *
 SOUND_LoadWAVEData(
 	LPCBYTE                lpData,
@@ -125,54 +100,42 @@ SOUND_LoadWAVEData(
     Pointer to the WAVE data inside the input buffer, NULL if failed.
 --*/
 {
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
-#	define RIFF		0x52494646 // 'RIFF'
-#	define WAVE		0x57415645 // 'WAVE'
-#	define FMT		0x666D7420 // 'fmt '
-#	define DATA		0x64617461 // 'data'
-#	define PCM      0x0100
-#else
-#	define RIFF		0x46464952 // 'FFIR'
-#	define WAVE		0x45564157 // 'EVAW'
-#	define FMT		0x20746D66 // ' tmf'
-#	define DATA		0x61746164 // 'atad'
-#	define PCM      0x0001
-#endif
-	LPCRIFFHEADER lpRiff = (LPCRIFFHEADER)lpData;
-	LPCRIFFCHUNK lpChunk;
-	LPCWAVEFORMATPCM lpFormat = NULL;
-	LPCBYTE lpWaveData = NULL;
-	DWORD len;
-
-	if (dwLen < sizeof(RIFFHEADER) || lpRiff->riff_sig != RIFF || lpRiff->riff_type != WAVE || dwLen < SDL_SwapLE32(lpRiff->data_length) + 8)
+	const RIFFHeader      *lpRiff   = (const RIFFHeader *)lpData;
+	const RIFFChunkHeader *lpChunk  = NULL;
+	const WAVEFormatPCM   *lpFormat = NULL;
+	const uint8_t         *lpWaveData = NULL;
+	uint32_t len;
+
+	if (dwLen < sizeof(RIFFHeader) || lpRiff->signature != RIFF_RIFF ||
+		lpRiff->type != RIFF_WAVE || dwLen < SDL_SwapLE32(lpRiff->length) + 8)
 	{
 		return NULL;
 	}
 
-	lpChunk = (LPCRIFFCHUNK)(lpRiff + 1); dwLen -= sizeof(RIFFHEADER);
-	while (dwLen >= sizeof(RIFFCHUNK))
+	lpChunk = (const RIFFChunkHeader *)(lpRiff + 1); dwLen -= sizeof(RIFFHeader);
+	while (dwLen >= sizeof(RIFFChunkHeader))
 	{
-		len = SDL_SwapLE32(lpChunk->chunk_length);
-		if (dwLen >= sizeof(RIFFCHUNK) + len)
-			dwLen -= sizeof(RIFFCHUNK) + len;
+		len = SDL_SwapLE32(lpChunk->length);
+		if (dwLen >= sizeof(RIFFChunkHeader) + len)
+			dwLen -= sizeof(RIFFChunkHeader) + len;
 		else
 			return NULL;
 
-		switch (lpChunk->chunk_type)
+		switch (lpChunk->type)
 		{
-		case FMT:
-			lpFormat = (LPCWAVEFORMATPCM)(lpChunk + 1);
-			if (len != sizeof(WAVEFORMATPCM) || lpFormat->wFormatTag != PCM)
+		case WAVE_fmt:
+			lpFormat = (const WAVEFormatPCM *)(lpChunk + 1);
+			if (len != sizeof(WAVEFormatPCM) || lpFormat->wFormatTag != SDL_SwapLE16(0x0001))
 			{
 				return NULL;
 			}
 			break;
-		case DATA:
-			lpWaveData = (LPCBYTE)(lpChunk + 1);
+		case WAVE_data:
+			lpWaveData = (const uint8_t *)(lpChunk + 1);
 			dwLen = 0;
 			break;
 		}
-		lpChunk = (LPCRIFFCHUNK)((LPCBYTE)(lpChunk + 1) + len);
+		lpChunk = (const RIFFChunkHeader *)((const uint8_t *)(lpChunk + 1) + len);
 	}
 
 	if (lpFormat == NULL || lpWaveData == NULL)
@@ -187,10 +150,6 @@ SOUND_LoadWAVEData(
 	lpSpec->align = lpFormat->nChannels * lpFormat->wBitsPerSample >> 3;
 
 	return lpWaveData;
-
-#undef RIFF
-#undef WAVE
-#undef FMT
 }
 
 typedef struct tagVOCHEADER

+ 5 - 0
uigame.c

@@ -156,6 +156,11 @@ PAL_OpeningMenu(
    AUDIO_PlayMusic(0, FALSE, 1);
    PAL_FadeOut(1);
 
+   if (wItemSelected == 0)
+   {
+      PAL_PlayAVI("3.avi");
+   }
+
    return (INT)wItemSelected;
 }
 

+ 70 - 69
unix/Makefile

@@ -1,69 +1,70 @@
-# General makefile for generic unix & linux platforms
-
-TARGET = sdlpal
-TEST_TARGET = ./sdlpal-tests
-
-HOST =
-TEST_CCFLAGS =
-
-GENERATED = -DPAL_HAS_GIT_REVISION $(shell ../scripts/gengitrev)
-
-DEPFLAGS = -MT $@ -MMD -MP -MF $*.Td
-
-GTEST_DIR = ../3rd/googletest/googletest
-
-CFILES = $(wildcard ../adplug/*.c) $(wildcard ../libmad/*.c) $(wildcard ../liboggvorbis/src/*.c) $(wildcard ../*.c) 
-CPPFILES = $(wildcard ../adplug/*.cpp) $(wildcard ../*.cpp) $(wildcard ./*.cpp)
-OBJFILES = $(CFILES:.c=.o) $(CPPFILES:.cpp=.o)
-DEPFILES = $(OBJFILES:.o=.d) 
-TEST_CPPFILES = $(wildcard ../tests/*.cpp)
-TEST_OBJFILES = $(TEST_CPPFILES:.cpp=.o)
-
-CCFLAGS = `sdl2-config --cflags` -g -Wall -O2 -fno-strict-aliasing -I. -I../ -I../liboggvorbis/include -I../liboggvorbis/src -DPAL_HAS_PLATFORM_SPECIFIC_UTILS $(TEST_CCFLAGS) $(GENERATED)
-CXXFLAGS = $(CCFLAGS) -std=c++11 `fltk-config --cxxflags`
-CFLAGS = $(CCFLAGS) -std=gnu99 `fltk-config --cflags`
-LDFLAGS = `sdl2-config --libs` `fltk-config --ldflags` -lstdc++ -lm -pthread
-TEST_CXXFLAGS += -isystem $(GTEST_DIR)/include -I $(GTEST_DIR) -g -Wall -Wextra -pthread
-
-POSTCOMPILE = @mv -f $*.Td $*.d && touch $@
-
-.PHONY : all clean check
-
-all: $(TARGET)
-
-$(TARGET): $(OBJFILES)
-	@echo [LD] $@
-	@$(HOST)gcc $^ -o $@ $(LDFLAGS)
-
-gtest-all.o : $(GTEST_DIR)/src/gtest-all.cc %.d
-	@echo [CC] $<
-	@$(HOST)g++ $(DEPFLAGS) $(TEST_CXXFLAGS) -c $< -o $@
-	$(POSTCOMPILE)
-
-%.o: %.c %.d
-	@echo [CC] $<
-	@$(HOST)gcc $(DEPFLAGS) $(CFLAGS) -c $< -o $@
-	$(POSTCOMPILE)
-
-%.o: %.cpp %.d
-	@echo [CC] $<
-	@$(HOST)g++ $(DEPFLAGS) $(CXXFLAGS) -c $< -o $@
-	$(POSTCOMPILE)
-
-$(TEST_TARGET): $(OBJFILES) $(TEST_OBJFILES) gtest-all.o
-	@echo [LD] $@
-	@$(HOST)g++ $^ -o $@ $(LDFLAGS) -lpthread
-
-clean:
-	-rm -f $(TARGET) $(TEST_TARGET) $(OBJFILES) $(TEST_OBJFILES) $(DEPFILES)
-
-%.d: ;
-.PRECIOUS: %.d
-
--include $(DEPFILES)
-
-check: TEST_CCFLAGS = -DUNIT_TEST=1 -isystem $(GTEST_DIR)/include
-check: $(TEST_TARGET)
-	@echo [EXEC] $(TEST_TARGET)
-	@chmod +x $(TEST_TARGET)
-	@exec $(TEST_TARGET)
+# General makefile for generic unix & linux platforms
+
+TARGET = sdlpal
+TEST_TARGET = ./sdlpal-tests
+
+HOST =
+TEST_CCFLAGS =
+
+GENERATED = -DPAL_HAS_GIT_REVISION $(shell ../scripts/gengitrev)
+
+DEPFLAGS = -MT $@ -MMD -MP -MF $*.Td
+
+GTEST_DIR = ../3rd/googletest/googletest
+
+CFILES = $(wildcard ../adplug/*.c) $(wildcard ../libmad/*.c) $(wildcard ../liboggvorbis/src/*.c) $(wildcard ../*.c) 
+CPPFILES = $(wildcard ../adplug/*.cpp) $(wildcard ../*.cpp) $(wildcard ./*.cpp)
+OBJFILES = $(CFILES:.c=.o) $(CPPFILES:.cpp=.o)
+DEPFILES = $(OBJFILES:.o=.d) 
+TEST_CPPFILES = $(wildcard ../tests/*.cpp)
+TEST_OBJFILES = $(TEST_CPPFILES:.cpp=.o)
+SDL_CONFIG = sdl2-config
+
+CCFLAGS = `$(SDL_CONFIG) --cflags` -g -Wall -O2 -fno-strict-aliasing -I. -I../ -I../liboggvorbis/include -I../liboggvorbis/src -DPAL_HAS_PLATFORM_SPECIFIC_UTILS $(TEST_CCFLAGS) $(GENERATED)
+CXXFLAGS = $(CCFLAGS) -std=c++11 `fltk-config --cxxflags`
+CFLAGS = $(CCFLAGS) -std=gnu99 `fltk-config --cflags`
+LDFLAGS = `$(SDL_CONFIG) --libs` `fltk-config --ldflags` -lstdc++ -lm -pthread
+TEST_CXXFLAGS += -isystem $(GTEST_DIR)/include -I $(GTEST_DIR) -g -Wall -Wextra -pthread
+
+POSTCOMPILE = @mv -f $*.Td $*.d && touch $@
+
+.PHONY : all clean check
+
+all: $(TARGET)
+
+$(TARGET): $(OBJFILES)
+	@echo [LD] $@
+	@$(HOST)gcc $^ -o $@ $(LDFLAGS)
+
+gtest-all.o : $(GTEST_DIR)/src/gtest-all.cc %.d
+	@echo [CC] $<
+	@$(HOST)g++ $(DEPFLAGS) $(TEST_CXXFLAGS) -c $< -o $@
+	$(POSTCOMPILE)
+
+%.o: %.c %.d
+	@echo [CC] $<
+	@$(HOST)gcc $(DEPFLAGS) $(CFLAGS) -c $< -o $@
+	$(POSTCOMPILE)
+
+%.o: %.cpp %.d
+	@echo [CC] $<
+	@$(HOST)g++ $(DEPFLAGS) $(CXXFLAGS) -c $< -o $@
+	$(POSTCOMPILE)
+
+$(TEST_TARGET): $(OBJFILES) $(TEST_OBJFILES) gtest-all.o
+	@echo [LD] $@
+	@$(HOST)g++ $^ -o $@ $(LDFLAGS) -lpthread
+
+clean:
+	-rm -f $(TARGET) $(TEST_TARGET) $(OBJFILES) $(TEST_OBJFILES) $(DEPFILES)
+
+%.d: ;
+.PRECIOUS: %.d
+
+-include $(DEPFILES)
+
+check: TEST_CCFLAGS = -DUNIT_TEST=1 -isystem $(GTEST_DIR)/include
+check: $(TEST_TARGET)
+	@echo [EXEC] $(TEST_TARGET)
+	@chmod +x $(TEST_TARGET)
+	@exec $(TEST_TARGET)

+ 22 - 17
unix/unix.cpp

@@ -29,6 +29,7 @@ struct {
    Fl_Check_Button* touch;
    Fl_Check_Button* aspect;
    Fl_Check_Button* fullscreen;
+   Fl_Check_Button* avi;
    Fl_Choice* loglevel;
    Fl_Choice* cd;
    Fl_Choice* bgm;
@@ -57,6 +58,7 @@ struct {
    const char* touch;
    const char* aspect;
    const char* fullscreen;
+   const char* avi;
    const char* cd;
    const char* bgm;
    const char* opl;
@@ -75,22 +77,22 @@ struct {
 } gLabels[3] = {
    { "SDLPAL Launcher",     "Language & Font",    "Display",      "Audio",      "Logging",
      "Game resource path:", "Message file:",      "Font file:",   "Log file:",  "Log level:",
-     "Use touc&h overlay",  "&Keep aspect ratio", "&Full screen", "&CD type:",  "&BGM type:",
-     "&OPL type:",          "Sample rate:",       "Ste&reo",      "OPL rate:",  "Surround O&PL",
-     "Music volume:",       "Sound volume:",      "Buffer:",      "Quality:",   "E&xit",
-     "&Launch game",        "&Default",           "Verbose|Debug|Informational|Warning|Error|Fatal" },
+     "Use touc&h overlay",  "&Keep aspect ratio", "&Full screen", "Enable A&VI","&CD type:",
+     "&BGM type:",          "&OPL type:",         "Sample rate:", "Ste&reo",    "OPL rate:",
+     "Surround O&PL",       "Music volume:",      "Sound volume:","Buffer:",    "Quality:",
+     "E&xit",               "&Launch game",       "&Default",     "Verbose|Debug|Informational|Warning|Error|Fatal" },
    { "SDLPAL 启动器",        "字体及语言设置",      "显示设置",      "音频设置",     "日志记录设置",
      "游戏资源目录:",        "语言文件:",         "字体文件",      "日志文件",     "日志记录级别:",
-     "启用触屏辅助(&H)",      "保持纵横比(&K)",     "全屏模式(&F)",  "&CD 音源:",   "&BGM 音源:",
-     "&OPL 类型:",          "采样率:",           "立体声(&R)",    "OPL 采样率:", "环绕声 O&PL",
-     "音乐音量:",            "音效音量:",         "缓冲区:",      "质量:",       "退出(&X)",
-     "启动游戏(&L)",          "默认设置(&D)",       "详细|调试|信息|警告|错误|致命" },
+     "启用触屏辅助(&H)",      "保持纵横比(&K)",     "全屏模式(&F)",  "AVI 动画(&V)", "&CD 音源:",
+     "&BGM 音源:",          "&OPL 类型:",        "采样率:",      "立体声(&R)",   "OPL 采样率:",
+     "环绕声 O&PL",          "音乐音量:",         "音效音量:",     "缓冲区:",     "质量:",
+     "退出(&X)",             "启动游戏(&L)",       "默认设置(&D)",  "详细信息|调试信息|运行信息|普通警告|严重错误|致命错误" },
    { "SDLPAL 啟動器",        "字體及語言設定",      "顯示設定",      "音訊設定",      "日誌記錄設定",
      "遊戲資源檔夾:",        "語言檔:",           "字體檔:",      "日誌檔:",      "日誌記錄級別:",
-     "啟用觸屏輔助(&H)",      "保持縱橫比(&K)",     "全屏模式(&F)",  "&CD 音源:",    "&BGM 音源:",
-     "&OPL 類型:",          "取樣速率:",          "立體聲(&R)",   "OPL 取樣速率:", "環繞聲 O&PL",
-     "音樂音量:",            "音效音量:",         "緩衝區:",      "品質:",        "退出(&X)",
-     "啟動遊戲(&L)",          "默認設定(&D)",       "詳細|調試|信息|警告|錯誤|致命" },
+     "啟用觸屏輔助(&H)",      "保持縱橫比(&K)",     "全屏模式(&F)",  "AVI 動畫(&V)",  "&CD 音源:",
+     "&BGM 音源:",          "&OPL 類型:",        "取樣速率:",    "立體聲(&R)",    "OPL 取樣速率:",
+     "環繞聲 O&PL",          "音樂音量:",          "音效音量:",    "緩衝區:",      "品質:",
+     "退出(&X)",             "啟動遊戲(&L)",       "默認設定(&D)",  "詳細信息|調試信息|運行信息|普通警告|嚴重錯誤|致命錯誤" },
 };
 
 void InitControls()
@@ -104,6 +106,7 @@ void InitControls()
    gWidgets.touch->value(gConfig.fUseTouchOverlay ? 1 : 0);
    gWidgets.aspect->value(gConfig.fKeepAspectRatio ? 1 : 0);
    gWidgets.fullscreen->value(gConfig.fFullScreen ? 1 : 0);
+   gWidgets.avi->value(gConfig.fEnableAviPlay ? 1 : 0);
    gWidgets.cd->value(gConfig.eCDType - MUSIC_MP3);
    gWidgets.bgm->value(gConfig.eMusicType);
    gWidgets.stereo->value(gConfig.iAudioChannels == 2 ? 1 : 0);
@@ -133,6 +136,7 @@ void SaveControls()
    gConfig.fUseTouchOverlay = gWidgets.touch->value();
    gConfig.fKeepAspectRatio = gWidgets.aspect->value();
    gConfig.fFullScreen = gWidgets.fullscreen->value();
+   gConfig.fEnableAviPlay = gWidgets.avi->value();
    gConfig.eCDType = (MUSICTYPE)(gWidgets.cd->value() + MUSIC_MP3);
    gConfig.eMusicType = (MUSICTYPE)(gWidgets.bgm->value());
    gConfig.iAudioChannels = gWidgets.stereo->value() ? 2 : 1;
@@ -178,18 +182,19 @@ Fl_Window* InitWindow()
    gWidgets.logfile = new Fl_Input(284, 131, 341, 22, gLabels[lang].logfile);
 
    (new Fl_Box(FL_BORDER_BOX, 5, 180, 630, 30, gLabels[lang].display))->align(FL_ALIGN_TOP);
-   gWidgets.touch = new Fl_Check_Button(30, 185, 160, 20, gLabels[lang].touch);
-   gWidgets.aspect = new Fl_Check_Button(260, 185, 160, 20, gLabels[lang].aspect);
-   gWidgets.fullscreen = new Fl_Check_Button(510, 185, 120, 20, gLabels[lang].fullscreen);
+   gWidgets.touch = new Fl_Check_Button(10, 185, 150, 20, gLabels[lang].touch);
+   gWidgets.aspect = new Fl_Check_Button(200, 185, 150, 20, gLabels[lang].aspect);
+   gWidgets.avi = new Fl_Check_Button(390, 185, 100, 20, gLabels[lang].avi);
+   gWidgets.fullscreen = new Fl_Check_Button(530, 185, 100, 20, gLabels[lang].fullscreen);
 
    (new Fl_Box(FL_BORDER_BOX, 5, 230, 630, 130, gLabels[lang].audio))->align(FL_ALIGN_TOP);
    (gWidgets.cd = new Fl_Choice(84, 239, lang ? 100 : 120, 22, gLabels[lang].cd))->add("MP3|OGG");
    (gWidgets.bgm = new Fl_Choice(285, 239, 60, 22, gLabels[lang].bgm))->add("MIDI|RIX|MP3|OGG");
-   gWidgets.stereo = new Fl_Check_Button(365, 240, 60, 20, gLabels[lang].stereo);
+   gWidgets.stereo = new Fl_Check_Button(365, 240, 70, 20, gLabels[lang].stereo);
    gWidgets.samplerate = new Fl_Int_Input(570, 239, 60, 22, gLabels[lang].samplerate);
    (gWidgets.opl = new Fl_Choice(84, 269, lang ? 100 : 120, 22, gLabels[lang].opl))->add("DOSBOX|MAME|DOSBOXNEW");
    gWidgets.oplrate = new Fl_Int_Input(285, 269, 60, 22, gLabels[lang].oplrate);
-   gWidgets.surround = new Fl_Check_Button(365, 270, 60, 20, gLabels[lang].surround);
+   gWidgets.surround = new Fl_Check_Button(365, 270, 120, 20, gLabels[lang].surround);
    gWidgets.buffer = new Fl_Int_Input(570, 269, 60, 22, gLabels[lang].buffer);
 
    gWidgets.quality = new Fl_Hor_Value_Slider(72, 299, 180, 22, gLabels[lang].quality);

+ 4 - 1
util.c

@@ -32,7 +32,9 @@
 #include "SDL_messagebox.h"
 #endif
 
-void UTIL_MsgBox(char *string){
+void UTIL_MsgBox(char *string)
+{
+#if SDL_VERSION_ATLEAST(2, 0, 0)
     extern SDL_Window *gpWindow;
     char buffer[300];
     SDL_MessageBoxButtonData buttons[] = { { 0, 0, "OK" } };
@@ -40,6 +42,7 @@ void UTIL_MsgBox(char *string){
     int btnid;
     sprintf(buffer, "%s\n", string);
     SDL_ShowMessageBox(&mbd, &btnid);
+#endif
 }
 
 long

+ 60 - 3
video.c

@@ -508,6 +508,8 @@ VIDEO_SetPalette(
 --*/
 {
 #if SDL_VERSION_ATLEAST(2,0,0)
+   SDL_Rect rect;
+
    SDL_SetPaletteColors(gpPalette, rgPalette, 0, 256);
 
    SDL_SetSurfacePalette(gpScreen, gpPalette);
@@ -522,7 +524,12 @@ VIDEO_SetPalette(
    SDL_SetSurfaceColorMod(gpScreenBak, 0, 0, 0);
    SDL_SetSurfaceColorMod(gpScreenBak, 0xFF, 0xFF, 0xFF);
 
-   VIDEO_UpdateScreen(NULL);
+   rect.x = 0;
+   rect.y = 0;
+   rect.w = 320;
+   rect.h = 200;
+
+   VIDEO_UpdateScreen(&rect);
 #else
    SDL_SetPalette(gpScreen, SDL_LOGPAL | SDL_PHYSPAL, rgPalette, 0, 256);
    SDL_SetPalette(gpScreenBak, SDL_LOGPAL | SDL_PHYSPAL, rgPalette, 0, 256);
@@ -1059,7 +1066,7 @@ VIDEO_SetWindowTitle(
 
   Parameters:
 
-    [IN]  lpszTitle - the new caption of the window.
+    [IN]  pszTitle - the new caption of the window.
 
   Return value:
 
@@ -1070,7 +1077,7 @@ VIDEO_SetWindowTitle(
 #if SDL_VERSION_ATLEAST(2,0,0)
 	SDL_SetWindowTitle(gpWindow, pszTitle);
 #else
-	SDL_WM_SetCaption(lpszCaption, NULL);
+	SDL_WM_SetCaption(pszTitle, NULL);
 #endif
 }
 
@@ -1177,3 +1184,53 @@ VIDEO_UpdateSurfacePalette(
 	SDL_SetPalette(pSurface, SDL_PHYSPAL | SDL_LOGPAL, gpPalette->colors, 0, 256);
 #endif
 }
+
+VOID
+VIDEO_DrawSurfaceToScreen(
+    SDL_Surface    *pSurface
+)
+/*++
+  Purpose:
+
+    Draw a surface directly to screen.
+
+  Parameters:
+
+    [IN]  pSurface - the surface which needs to be drawn to screen.
+
+  Return value:
+
+    None.
+
+--*/
+{
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+   //
+   // Draw the surface to screen.
+   //
+   SDL_BlitScaled(pSurface, NULL, gpScreenReal, NULL);
+   VIDEO_RenderCopy();
+#else
+   SDL_Surface   *pCompatSurface;
+   SDL_Rect       rect;
+
+   rect.x = rect.y = 0;
+   rect.w = pSurface->w;
+   rect.h = pSurface->h;
+
+   pCompatSurface = VIDEO_CreateCompatibleSizedSurface(gpScreenReal, &rect);
+
+   //
+   // First convert the surface to compatible format.
+   //
+   SDL_BlitSurface(pSurface, NULL, pCompatSurface, NULL);
+
+   //
+   // Draw the surface to screen.
+   //
+   SDL_SoftStretch(pCompatSurface, NULL, gpScreenReal, NULL);
+
+   SDL_UpdateRect(gpScreenReal, 0, 0, gpScreenReal->w, gpScreenReal->h);
+   SDL_FreeSurface(pCompatSurface);
+#endif
+}

+ 5 - 0
video.h

@@ -121,6 +121,11 @@ VIDEO_UpdateSurfacePalette(
 	SDL_Surface    *pSurface
 );
 
+VOID
+VIDEO_DrawSurfaceToScreen(
+    SDL_Surface    *pSurface
+);
+
 PAL_C_LINKAGE_END
 
 #endif

+ 3 - 1
win32/resource.h

@@ -33,6 +33,8 @@
 #define IDC_USELOGFILE                  1024
 #define IDC_BRLOG                       1025
 #define IDC_LOGFILE                     1026
+#define IDC_CHECK1                      1027
+#define IDC_ENABLEAVI                   1027
 #define IDC_STATIC                      -1
 
 // Next default values for new objects
@@ -41,7 +43,7 @@
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_NEXT_RESOURCE_VALUE        104
 #define _APS_NEXT_COMMAND_VALUE         40001
-#define _APS_NEXT_CONTROL_VALUE         1024
+#define _APS_NEXT_CONTROL_VALUE         1028
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif

+ 12 - 9
win32/sdlpal.rc

@@ -37,9 +37,9 @@ BEGIN
     LTEXT           "CD source:",IDC_STATIC,14,160,36,8
     GROUPBOX        "Font && Language",IDC_STATIC,7,30,391,50
     GROUPBOX        "Display",IDC_STATIC,7,110,391,30
-    CONTROL         "&Keep aspect ratio",IDC_ASPECTRATIO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,124,72,10
+    CONTROL         "&Keep aspect ratio",IDC_ASPECTRATIO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,114,124,72,10
     LTEXT           "BGM source:",IDC_STATIC,115,160,41,8
-    CONTROL         "&Full screen",IDC_FULLSCREEN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,124,50,10
+    CONTROL         "Start game in &full screen",IDC_FULLSCREEN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,124,93,10
     EDITTEXT        IDC_MSGFILE,70,61,270,14,ES_AUTOHSCROLL | ES_READONLY
     GROUPBOX        "Audio",IDC_STATIC,7,140,391,85
     COMBOBOX        IDC_CD,54,157,48,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
@@ -57,7 +57,7 @@ BEGIN
     EDITTEXT        IDC_AUDIOBUFFER,342,176,40,14,ES_AUTOHSCROLL | ES_NUMBER
     PUSHBUTTON      "&Browse",IDC_BRGAME,348,7,50,14
     CONTROL         "Message file:",IDC_USEMSGFILE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,64,56,8
-    CONTROL         "Enable &touch overlay",IDC_TOUCHOVERLAY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,306,124,83,10
+    CONTROL         "Enable &touch overlay",IDC_TOUCHOVERLAY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,314,124,80,10
     LTEXT           "Quality:",IDC_STATIC,14,198,26,8
     LTEXT           "Low                              High",IDC_STATIC,31,212,88,8
     CONTROL         "",IDC_QUALITY,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,43,197,60,15
@@ -77,6 +77,7 @@ BEGIN
     PUSHBUTTON      "&Browse",IDC_BRFONT,348,43,50,14,WS_DISABLED
     PUSHBUTTON      "&Browse",IDC_BRMSG,348,61,50,14,WS_DISABLED
     PUSHBUTTON      "&Browse",IDC_BRLOG,348,92,50,14,WS_DISABLED
+    CONTROL         "Enable A&VI animation",IDC_ENABLEAVI,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,214,124,77,10
 END
 
 
@@ -226,7 +227,7 @@ BEGIN
     LTEXT           "CD 音源:",IDC_STATIC,14,160,36,8
     GROUPBOX        "字体及语言设置",IDC_STATIC,7,30,391,50
     GROUPBOX        "显示设置",IDC_STATIC,7,110,391,30
-    CONTROL         "保持纵横比(&K)",IDC_ASPECTRATIO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,160,124,65,10
+    CONTROL         "保持纵横比(&K)",IDC_ASPECTRATIO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,114,124,65,10
     LTEXT           "BGM 音源:",IDC_STATIC,115,160,41,8
     CONTROL         "全屏启动游戏(&F)",IDC_FULLSCREEN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,124,73,10
     EDITTEXT        IDC_MSGFILE,70,61,270,14,ES_AUTOHSCROLL | ES_READONLY
@@ -246,7 +247,7 @@ BEGIN
     EDITTEXT        IDC_AUDIOBUFFER,335,176,40,14,ES_AUTOHSCROLL | ES_NUMBER
     PUSHBUTTON      "浏览(&B)",IDC_BRGAME,348,7,50,14
     CONTROL         "语言文件:",IDC_USEMSGFILE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,64,56,8
-    CONTROL         "启用触屏辅助(&T)",IDC_TOUCHOVERLAY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,309,124,73,10
+    CONTROL         "启用触屏辅助(&T)",IDC_TOUCHOVERLAY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,314,124,73,10
     LTEXT           "音频质量:",IDC_STATIC,14,198,41,8
     LTEXT           "低                              高",IDC_STATIC,47,212,77,8
     CONTROL         "",IDC_QUALITY,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,55,197,60,15
@@ -266,6 +267,7 @@ BEGIN
     PUSHBUTTON      "浏览(&B)",IDC_BRFONT,348,43,50,14,WS_DISABLED
     PUSHBUTTON      "浏览(&B)",IDC_BRMSG,348,61,50,14,WS_DISABLED
     PUSHBUTTON      "浏览(&B)",IDC_BRLOG,348,92,50,14,WS_DISABLED
+    CONTROL         "启用 AVI 过场动画(&V)",IDC_ENABLEAVI,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,214,124,86,10
 END
 
 
@@ -344,7 +346,7 @@ END
 STRINGTABLE
 BEGIN
     IDC_MSGFILE             "语言文件(*.msg)|*.msg|所有文件 (*.*)|*.*||"
-    IDC_LOGLEVEL            "详细;调试;信息;警告;错误;致命"
+    IDC_LOGLEVEL            "详细信息;调试信息;运行信息;普通警告;严重错误;致命错误"
     IDC_BRFONT              "选择自定义字体文件"
     IDC_FONTFILE            "字体文件(*.bdf)|*.bdf|所有文件 (*.*)|*.*||"
 END
@@ -384,7 +386,7 @@ BEGIN
     LTEXT           "CD �方�",IDC_STATIC,14,160,36,8
     GROUPBOX        "�砰の粂ē砞﹚",IDC_STATIC,7,30,391,50
     GROUPBOX        "陪ボ砞﹚",IDC_STATIC,7,110,391,30
-    CONTROL         "玂�羇绢ゑ(&K)",IDC_ASPECTRATIO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,160,124,65,10
+    CONTROL         "玂�羇绢ゑ(&K)",IDC_ASPECTRATIO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,114,124,65,10
     LTEXT           "BGM �方�",IDC_STATIC,115,160,41,8
     CONTROL         "��币笆笴栏(&F)",IDC_FULLSCREEN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,124,73,10
     EDITTEXT        IDC_MSGFILE,70,61,270,14,ES_AUTOHSCROLL | ES_READONLY
@@ -406,7 +408,7 @@ BEGIN
     EDITTEXT        IDC_AUDIOBUFFER,342,175,40,14,ES_AUTOHSCROLL | ES_NUMBER
     PUSHBUTTON      "聅凝(&B)",IDC_BRGAME,348,7,50,14
     CONTROL         "�璹粂ē郎�",IDC_USEMSGFILE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,64,56,8
-    CONTROL         "币ノ牟�徊�(&T)",IDC_TOUCHOVERLAY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,309,124,73,10
+    CONTROL         "币ノ牟�徊�(&T)",IDC_TOUCHOVERLAY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,314,124,73,10
     LTEXT           "�                              蔼",IDC_STATIC,47,212,77,8
     LTEXT           "�贾�秖�",IDC_STATIC,131,198,41,8
     CONTROL         "",IDC_MUSICVOLUME,"msctls_trackbar32",WS_TABSTOP,172,197,80,15
@@ -424,6 +426,7 @@ BEGIN
     PUSHBUTTON      "聅凝(&B)",IDC_BRFONT,348,43,50,14,WS_DISABLED
     PUSHBUTTON      "聅凝(&B)",IDC_BRMSG,348,61,50,14,WS_DISABLED
     PUSHBUTTON      "聅凝(&B)",IDC_BRLOG,348,92,50,14,WS_DISABLED
+    CONTROL         "币ノ AVI 筁初笆礶(&V)",IDC_ENABLEAVI,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,214,124,85,10
 END
 
 
@@ -476,7 +479,7 @@ END
 STRINGTABLE
 BEGIN
     IDC_MSGFILE             "粂ē郎(*.msg)|*.msg|┮Τ郎� (*.*)|*.*||"
-    IDC_LOGLEVEL            "冈灿;秸刚;獺�;牡�;岿粇;璓㏑"
+    IDC_LOGLEVEL            "冈灿獺�;秸刚獺�;笲︽獺�;炊硄牡�;腨�岿粇;璓㏑岿粇"
     IDC_BRFONT              "匡拒�璹�砰郎"
     IDC_FONTFILE            "�砰郎(*.bdf)|*.bdf|┮Τ郎� (*.*)|*.*||"
 END

+ 3 - 0
win32/sdlpal.vcxproj

@@ -299,6 +299,7 @@
     </ClCompile>
     <ClCompile Include="..\adplug\demuopl.cpp" />
     <ClCompile Include="..\audio.c" />
+    <ClCompile Include="..\aviplay.c" />
     <ClCompile Include="..\battle.c" />
     <ClCompile Include="..\ending.c" />
     <ClCompile Include="..\fight.c" />
@@ -398,6 +399,7 @@
     <ClInclude Include="..\adplug\dbopl.h" />
     <ClInclude Include="..\adplug\kemuopl.h" />
     <ClInclude Include="..\ascii.h" />
+    <ClInclude Include="..\aviplay.h" />
     <ClInclude Include="..\battle.h" />
     <ClInclude Include="..\codepage.h" />
     <ClInclude Include="..\common.h" />
@@ -468,6 +470,7 @@
     <ClInclude Include="..\res.h" />
     <ClInclude Include="..\resampler.h" />
     <ClInclude Include="..\players.h" />
+    <ClInclude Include="..\riff.h" />
     <ClInclude Include="..\rngplay.h" />
     <ClInclude Include="..\scene.h" />
     <ClInclude Include="..\script.h" />

+ 9 - 0
win32/sdlpal.vcxproj.filters

@@ -330,6 +330,9 @@
     <ClCompile Include="native_midi.cpp">
       <Filter>native_midi</Filter>
     </ClCompile>
+    <ClCompile Include="..\aviplay.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\ascii.h">
@@ -677,6 +680,12 @@
     <ClInclude Include="..\generated.h">
       <Filter>generated</Filter>
     </ClInclude>
+    <ClInclude Include="..\aviplay.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\riff.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="..\sdlpal.ico">

+ 2 - 0
win32/win32.cpp

@@ -150,6 +150,7 @@ void SaveSettings(HWND hwndDlg, BOOL fWriteFile)
 
 	gConfig.fFullScreen = IsDlgButtonChecked(hwndDlg, IDC_FULLSCREEN);
 	gConfig.fUseTouchOverlay = IsDlgButtonChecked(hwndDlg, IDC_TOUCHOVERLAY);
+	gConfig.fEnableAviPlay = IsDlgButtonChecked(hwndDlg, IDC_ENABLEAVI);
 	gConfig.fKeepAspectRatio = IsDlgButtonChecked(hwndDlg, IDC_ASPECTRATIO);
 	gConfig.eCDType = (MUSICTYPE)(ComboBox_GetCurSel(hwndDlg, IDC_CD) + MUSIC_MP3);
 	gConfig.eMusicType = (MUSICTYPE)ComboBox_GetCurSel(hwndDlg, IDC_BGM);
@@ -181,6 +182,7 @@ void ResetControls(HWND hwndDlg)
 
 	CheckDlgButton(hwndDlg, IDC_FULLSCREEN, gConfig.fFullScreen);
 	CheckDlgButton(hwndDlg, IDC_TOUCHOVERLAY, gConfig.fUseTouchOverlay);
+	CheckDlgButton(hwndDlg, IDC_ENABLEAVI, gConfig.fEnableAviPlay);
 	CheckDlgButton(hwndDlg, IDC_ASPECTRATIO, gConfig.fKeepAspectRatio);
 	CheckDlgButton(hwndDlg, IDC_SURROUNDOPL, gConfig.fUseSurroundOPL);
 	CheckDlgButton(hwndDlg, IDC_STEREO, gConfig.iAudioChannels == 2);

+ 18 - 17
winrt/SDLPal.Common/MainPage.xaml

@@ -45,25 +45,9 @@
                     <TextBox x:Name="tbFontFile" x:Uid="FontFile" Grid.Column="0" TextWrapping="Wrap" VerticalAlignment="Top" PlaceholderText="无自定义字体文件" IsReadOnly="True"/>
                     <Button x:Name="btnBrowseFontFile" x:Uid="ButtonBrowse" Grid.Column="1" Content="浏览" HorizontalAlignment="Right" Click="btnBrowseFileOpen_Click" />
                 </Grid>
-                <ComboBox x:Name="cbLogLevel" x:Uid="LogLevel" HorizontalAlignment="Stretch" Header="日志记录级别" PlaceholderText="选择日志记录级别">
-                    <ComboBoxItem x:Uid="Verbose" Content="详细"/>
-                    <ComboBoxItem x:Uid="Debug" Content="调试"/>
-                    <ComboBoxItem x:Uid="Information" Content="信息"/>
-                    <ComboBoxItem x:Uid="Warning" Content="警告"/>
-                    <ComboBoxItem x:Uid="Error" Content="错误"/>
-                    <ComboBoxItem x:Uid="Fatal" Content="致命"/>
-                </ComboBox>
-                <CheckBox x:Name="cbUseLogFile" x:Uid="UseLogFile" Content="记录日志到文件" Checked="cbUseFile_CheckChanged" Unchecked="cbUseFile_CheckChanged" />
-                <Grid x:Name="gridLogFile">
-                    <Grid.ColumnDefinitions>
-                        <ColumnDefinition/>
-                        <ColumnDefinition Width="Auto"/>
-                    </Grid.ColumnDefinitions>
-                    <TextBox x:Name="tbLogFile" x:Uid="LogFile" Grid.Column="0" TextWrapping="Wrap" VerticalAlignment="Top" PlaceholderText="无日志文件" IsReadOnly="True"/>
-                    <Button x:Name="btnBrowseLogFile" x:Uid="ButtonBrowse" Grid.Column="1" Content="浏览" HorizontalAlignment="Right" Click="btnBrowseFileSave_Click" />
-                </Grid>
                 <ToggleSwitch x:Name="tsTouchOverlay" x:Uid="TouchOverlay" Header="启用触屏辅助" OffContent="否" OnContent="是" />
                 <ToggleSwitch x:Name="tsKeepAspect" x:Uid="AspectRatio" Header="保持纵横比" OffContent="否" OnContent="是" />
+                <ToggleSwitch x:Name="tsEnableAVI" x:Uid="EnableAVI" Header="启用 AVI 过场动画" OffContent="否" OnContent="是" />
                 <ToggleSwitch x:Name="tsStereo" x:Uid="Stereo" Header="立体声" OffContent="否" OnContent="是" />
                 <Slider x:Name="slMusicVolume" x:Uid="MusicVolume" Header="音乐音量" TickPlacement="Inline" TickFrequency="10" />
                 <Slider x:Name="slSoundVolume" x:Uid="SoundVolume" Header="音效音量" TickPlacement="Inline" TickFrequency="10" />
@@ -101,6 +85,23 @@
                     <ComboBoxItem Content="49716"/>
                 </ComboBox>
                 <ToggleSwitch x:Name="tsSurroundOPL" x:Uid="SurroundOPL" Header="环绕声 OPL" OffContent="否" OnContent="是" />
+                <ComboBox x:Name="cbLogLevel" x:Uid="LogLevel" HorizontalAlignment="Stretch" Header="日志记录级别" PlaceholderText="选择日志记录级别">
+                    <ComboBoxItem x:Uid="Verbose" Content="详细信息"/>
+                    <ComboBoxItem x:Uid="Debug" Content="调试信息"/>
+                    <ComboBoxItem x:Uid="Information" Content="运行信息"/>
+                    <ComboBoxItem x:Uid="Warning" Content="普通警告"/>
+                    <ComboBoxItem x:Uid="Error" Content="严重错误"/>
+                    <ComboBoxItem x:Uid="Fatal" Content="致命错误"/>
+                </ComboBox>
+                <CheckBox x:Name="cbUseLogFile" x:Uid="UseLogFile" Content="记录日志到文件" Checked="cbUseFile_CheckChanged" Unchecked="cbUseFile_CheckChanged" />
+                <Grid x:Name="gridLogFile">
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition/>
+                        <ColumnDefinition Width="Auto"/>
+                    </Grid.ColumnDefinitions>
+                    <TextBox x:Name="tbLogFile" x:Uid="LogFile" Grid.Column="0" TextWrapping="Wrap" VerticalAlignment="Top" PlaceholderText="无日志文件" IsReadOnly="True"/>
+                    <Button x:Name="btnBrowseLogFile" x:Uid="ButtonBrowse" Grid.Column="1" Content="浏览" HorizontalAlignment="Right" Click="btnBrowseFileSave_Click" />
+                </Grid>
                 <Grid VerticalAlignment="Top">
                     <Grid.ColumnDefinitions>
                         <ColumnDefinition />

+ 2 - 0
winrt/SDLPal.Common/MainPage.xaml.cpp

@@ -98,6 +98,7 @@ void SDLPal::MainPage::LoadControlContents(bool loadDefault)
 	tsStereo->IsOn = (gConfig.iAudioChannels == 2);
 	tsSurroundOPL->IsOn = (gConfig.fUseSurroundOPL == TRUE);
 	tsTouchOverlay->IsOn = (gConfig.fUseTouchOverlay == TRUE);
+	tsEnableAVI->IsOn = (gConfig.fEnableAviPlay == TRUE);
 
 	slMusicVolume->Value = gConfig.iMusicVolume;
 	slSoundVolume->Value = gConfig.iSoundVolume;
@@ -143,6 +144,7 @@ void SDLPal::MainPage::SaveControlContents()
 	gConfig.iAudioChannels = tsStereo->IsOn ? 2 : 1;
 	gConfig.fUseSurroundOPL = tsSurroundOPL->IsOn ? TRUE : FALSE;
 	gConfig.fUseTouchOverlay = tsTouchOverlay->IsOn ? TRUE : FALSE;
+	gConfig.fEnableAviPlay = tsEnableAVI->IsOn ? TRUE : FALSE;
 
 	gConfig.iMusicVolume = (int)slMusicVolume->Value;
 	gConfig.iSoundVolume = (int)slSoundVolume->Value;

+ 9 - 0
winrt/SDLPal.Common/Strings/en/Resources.resw

@@ -162,6 +162,15 @@
   <data name="Debug.Content" xml:space="preserve">
     <value>Debug</value>
   </data>
+  <data name="EnableAVI.Header" xml:space="preserve">
+    <value>Enable AVI animation</value>
+  </data>
+  <data name="EnableAVI.OffContent" xml:space="preserve">
+    <value>No</value>
+  </data>
+  <data name="EnableAVI.OnContent" xml:space="preserve">
+    <value>Yes</value>
+  </data>
   <data name="Error.Content" xml:space="preserve">
     <value>Error</value>
   </data>

+ 15 - 6
winrt/SDLPal.Common/Strings/zh-hans/Resources.resw

@@ -160,13 +160,22 @@
     <value>选择 CD 音轨格式</value>
   </data>
   <data name="Debug.Content" xml:space="preserve">
-    <value>调试</value>
+    <value>调试信息</value>
+  </data>
+  <data name="EnableAVI.Header" xml:space="preserve">
+    <value>启用 AVI 过场动画</value>
+  </data>
+  <data name="EnableAVI.OffContent" xml:space="preserve">
+    <value>否</value>
+  </data>
+  <data name="EnableAVI.OnContent" xml:space="preserve">
+    <value>是</value>
   </data>
   <data name="Error.Content" xml:space="preserve">
-    <value>错误</value>
+    <value>严重错误</value>
   </data>
   <data name="Fatal.Content" xml:space="preserve">
-    <value>致命</value>
+    <value>致命错误</value>
   </data>
   <data name="FontFile.PlaceholderText" xml:space="preserve">
     <value>无自定义字体文件</value>
@@ -178,7 +187,7 @@
     <value>未选择文件夹</value>
   </data>
   <data name="Information.Content" xml:space="preserve">
-    <value>信息</value>
+    <value>运行信息</value>
   </data>
   <data name="LogFile.PlaceholderText" xml:space="preserve">
     <value>无日志文件</value>
@@ -278,9 +287,9 @@
     <value>自定义语言文件</value>
   </data>
   <data name="Verbose.Content" xml:space="preserve">
-    <value>详细</value>
+    <value>详细信息</value>
   </data>
   <data name="Warning.Content" xml:space="preserve">
-    <value>警告</value>
+    <value>普通警告</value>
   </data>
 </root>

+ 15 - 6
winrt/SDLPal.Common/Strings/zh-hant/Resources.resw

@@ -160,13 +160,22 @@
     <value>選擇 CD 音軌格式</value>
   </data>
   <data name="Debug.Content" xml:space="preserve">
-    <value>調試</value>
+    <value>調試信息</value>
+  </data>
+  <data name="EnableAVI.Header" xml:space="preserve">
+    <value>啟用 AVI 過場動畫</value>
+  </data>
+  <data name="EnableAVI.OffContent" xml:space="preserve">
+    <value>否</value>
+  </data>
+  <data name="EnableAVI.OnContent" xml:space="preserve">
+    <value>是</value>
   </data>
   <data name="Error.Content" xml:space="preserve">
-    <value>錯誤</value>
+    <value>嚴重錯誤</value>
   </data>
   <data name="Fatal.Content" xml:space="preserve">
-    <value>致命</value>
+    <value>致命錯誤</value>
   </data>
   <data name="FontFile.PlaceholderText" xml:space="preserve">
     <value>無自訂字體檔案</value>
@@ -178,7 +187,7 @@
     <value>未選擇檔案夾</value>
   </data>
   <data name="Information.Content" xml:space="preserve">
-    <value>信息</value>
+    <value>運行信息</value>
   </data>
   <data name="LogFile.PlaceholderText" xml:space="preserve">
     <value>無日誌檔案</value>
@@ -278,9 +287,9 @@
     <value>自訂語言檔案</value>
   </data>
   <data name="Verbose.Content" xml:space="preserve">
-    <value>詳細</value>
+    <value>詳細信息</value>
   </data>
   <data name="Warning.Content" xml:space="preserve">
-    <value>警告</value>
+    <value>普通警告</value>
   </data>
 </root>

+ 3 - 0
winrt/SDLPal.UWP/SDLPal.Core.vcxproj

@@ -226,6 +226,7 @@
     <ClInclude Include="..\..\adplug\rix.h" />
     <ClInclude Include="..\..\adplug\surroundopl.h" />
     <ClInclude Include="..\..\ascii.h" />
+    <ClInclude Include="..\..\aviplay.h" />
     <ClInclude Include="..\..\battle.h" />
     <ClInclude Include="..\..\codepage.h" />
     <ClInclude Include="..\..\common.h" />
@@ -313,6 +314,7 @@
     <ClInclude Include="..\..\players.h" />
     <ClInclude Include="..\..\res.h" />
     <ClInclude Include="..\..\resampler.h" />
+    <ClInclude Include="..\..\riff.h" />
     <ClInclude Include="..\..\rngplay.h" />
     <ClInclude Include="..\..\scene.h" />
     <ClInclude Include="..\..\script.h" />
@@ -385,6 +387,7 @@
     <ClCompile Include="..\..\liboggvorbis\src\window.c" />
     <ClCompile Include="..\..\magicmenu.c" />
     <ClCompile Include="..\..\main.c" />
+    <ClCompile Include="..\..\aviplay.c" />
     <ClCompile Include="..\..\map.c" />
     <ClCompile Include="..\..\midi.c" />
     <ClCompile Include="..\..\mp3play.c" />

+ 9 - 0
winrt/SDLPal.UWP/SDLPal.Core.vcxproj.filters

@@ -393,6 +393,12 @@
     <ClInclude Include="..\..\generated.h">
       <Filter>generated</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\aviplay.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\riff.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\adplug\binfile.cpp">
@@ -644,6 +650,9 @@
     <ClCompile Include="..\..\midi.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\aviplay.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <None Include="..\libmad\D.dat">

+ 4 - 0
winrt/SDLPal.Windows/SDLPal.Core.vcxproj

@@ -224,6 +224,7 @@
     <ClInclude Include="..\..\adplug\rix.h" />
     <ClInclude Include="..\..\adplug\surroundopl.h" />
     <ClInclude Include="..\..\ascii.h" />
+    <ClInclude Include="..\..\aviplay.h" />
     <ClInclude Include="..\..\battle.h" />
     <ClInclude Include="..\..\codepage.h" />
     <ClInclude Include="..\..\common.h" />
@@ -309,6 +310,7 @@
     <ClInclude Include="..\..\players.h" />
     <ClInclude Include="..\..\res.h" />
     <ClInclude Include="..\..\resampler.h" />
+    <ClInclude Include="..\..\riff.h" />
     <ClInclude Include="..\..\rngplay.h" />
     <ClInclude Include="..\..\scene.h" />
     <ClInclude Include="..\..\script.h" />
@@ -336,6 +338,7 @@
     <ClCompile Include="..\..\adplug\rix.cpp" />
     <ClCompile Include="..\..\adplug\surroundopl.cpp" />
     <ClCompile Include="..\..\audio.c" />
+    <ClCompile Include="..\..\aviplay.c" />
     <ClCompile Include="..\..\battle.c" />
     <ClCompile Include="..\..\ending.c" />
     <ClCompile Include="..\..\fight.c" />
@@ -381,6 +384,7 @@
     <ClCompile Include="..\..\liboggvorbis\src\window.c" />
     <ClCompile Include="..\..\magicmenu.c" />
     <ClCompile Include="..\..\main.c" />
+    <ClCompile Include="..\..\aviplay.c" />
     <ClCompile Include="..\..\map.c" />
     <ClCompile Include="..\..\midi.c" />
     <ClCompile Include="..\..\mp3play.c" />

+ 9 - 0
winrt/SDLPal.Windows/SDLPal.Core.vcxproj.filters

@@ -384,6 +384,12 @@
     <ClInclude Include="..\pal_config.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\aviplay.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\riff.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\adplug\binfile.cpp">
@@ -428,6 +434,9 @@
     <ClCompile Include="..\..\audio.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\aviplay.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\liboggvorbis\src\analysis.c">
       <Filter>liboggvorbis\src</Filter>
     </ClCompile>

+ 4 - 0
winrt/SDLPal.WindowsPhone/SDLPal.Core.vcxproj

@@ -145,6 +145,7 @@
     <ClInclude Include="..\..\adplug\rix.h" />
     <ClInclude Include="..\..\adplug\surroundopl.h" />
     <ClInclude Include="..\..\ascii.h" />
+    <ClInclude Include="..\..\aviplay.h" />
     <ClInclude Include="..\..\battle.h" />
     <ClInclude Include="..\..\codepage.h" />
     <ClInclude Include="..\..\common.h" />
@@ -230,6 +231,7 @@
     <ClInclude Include="..\..\players.h" />
     <ClInclude Include="..\..\res.h" />
     <ClInclude Include="..\..\resampler.h" />
+    <ClInclude Include="..\..\riff.h" />
     <ClInclude Include="..\..\rngplay.h" />
     <ClInclude Include="..\..\scene.h" />
     <ClInclude Include="..\..\script.h" />
@@ -257,6 +259,7 @@
     <ClCompile Include="..\..\adplug\rix.cpp" />
     <ClCompile Include="..\..\adplug\surroundopl.cpp" />
     <ClCompile Include="..\..\audio.c" />
+    <ClCompile Include="..\..\aviplay.c" />
     <ClCompile Include="..\..\battle.c" />
     <ClCompile Include="..\..\ending.c" />
     <ClCompile Include="..\..\fight.c" />
@@ -302,6 +305,7 @@
     <ClCompile Include="..\..\liboggvorbis\src\window.c" />
     <ClCompile Include="..\..\magicmenu.c" />
     <ClCompile Include="..\..\main.c" />
+    <ClCompile Include="..\..\aviplay.c" />
     <ClCompile Include="..\..\map.c" />
     <ClCompile Include="..\..\midi.c" />
     <ClCompile Include="..\..\mp3play.c" />

+ 9 - 0
winrt/SDLPal.WindowsPhone/SDLPal.Core.vcxproj.filters

@@ -384,6 +384,12 @@
     <ClInclude Include="..\pal_config.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\aviplay.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\riff.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\adplug\binfile.cpp">
@@ -428,6 +434,9 @@
     <ClCompile Include="..\..\audio.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\aviplay.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\liboggvorbis\src\analysis.c">
       <Filter>liboggvorbis\src</Filter>
     </ClCompile>

+ 1 - 1
yj1.c

@@ -23,7 +23,7 @@
 // Copyright (c) 2006-2007, Lou Yihua.
 //
 // Ported to C from C++ and modified for compatibility with Big-Endian
-// by Wei Mingzhi <whistler@openoffice.org>.
+// by Wei Mingzhi <whistler_wmz@users.sf.net>.
 //
 
 #include "common.h"