summaryrefslogtreecommitdiff
path: root/dumb/dumb-kode54/winamp
diff options
context:
space:
mode:
Diffstat (limited to 'dumb/dumb-kode54/winamp')
-rw-r--r--dumb/dumb-kode54/winamp/config.rc144
-rw-r--r--dumb/dumb-kode54/winamp/gui.c331
-rw-r--r--dumb/dumb-kode54/winamp/gui.h26
-rw-r--r--dumb/dumb-kode54/winamp/in2.h123
-rw-r--r--dumb/dumb-kode54/winamp/in_duh.c655
-rw-r--r--dumb/dumb-kode54/winamp/in_duh.h40
-rw-r--r--dumb/dumb-kode54/winamp/minalleg.c2179
-rw-r--r--dumb/dumb-kode54/winamp/out.h71
-rw-r--r--dumb/dumb-kode54/winamp/resource.h36
-rw-r--r--dumb/dumb-kode54/winamp/winamp.dsp141
-rw-r--r--dumb/dumb-kode54/winamp/winamp.dsw29
11 files changed, 3775 insertions, 0 deletions
diff --git a/dumb/dumb-kode54/winamp/config.rc b/dumb/dumb-kode54/winamp/config.rc
new file mode 100644
index 00000000..1c8bf83c
--- /dev/null
+++ b/dumb/dumb-kode54/winamp/config.rc
@@ -0,0 +1,144 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_CONFIG DIALOGEX 0, 0, 297, 170
+STYLE DS_MODALFRAME | DS_3DLOOK | WS_CAPTION | WS_SYSMENU
+CAPTION "Duh! Plug-in"
+FONT 8, "MS Sans Serif"
+BEGIN
+ GROUPBOX "Output Bit Rate",IDC_STATIC,5,10,70,40,WS_TABSTOP
+ CONTROL "8 bits",IDC_8BPS,"Button",BS_AUTORADIOBUTTON | WS_GROUP |
+ WS_TABSTOP,15,25,47,8
+ CONTROL "16 bits",IDC_16BPS,"Button",BS_AUTORADIOBUTTON,15,35,45,
+ 10
+ CONTROL "Stereo",IDC_STEREO,"Button",BS_AUTOCHECKBOX | BS_CENTER |
+ WS_TABSTOP,5,55,70,12,WS_EX_DLGMODALFRAME
+ GROUPBOX "Frequency",IDC_STATIC,5,85,70,60
+ CONTROL "11025 Hz",IDC_11KHZ,"Button",BS_AUTORADIOBUTTON |
+ WS_GROUP | WS_TABSTOP,15,95,47,10
+ CONTROL "22050 Hz",IDC_22KHZ,"Button",BS_AUTORADIOBUTTON,15,105,
+ 47,10
+ CONTROL "44100 Hz",IDC_44KHZ,"Button",BS_AUTORADIOBUTTON,15,115,
+ 47,10
+ CONTROL "48000 Hz",IDC_48KHZ,"Button",BS_AUTORADIOBUTTON,15,125,
+ 47,10
+ GROUPBOX "Resampling",IDC_STATIC,85,10,100,70
+ CONTROL "Aliasing",IDC_ALIASING,"Button",BS_AUTORADIOBUTTON |
+ WS_GROUP | WS_TABSTOP,95,20,53,11
+ CONTROL "Linear",IDC_LINEAR,"Button",BS_AUTORADIOBUTTON,95,31,53,
+ 11
+ CONTROL "Linear + Low-pass",IDC_LINEAR_LOW_PASS,"Button",
+ BS_AUTORADIOBUTTON,95,42,70,11
+ CONTROL "Quadratic",IDC_QUADRATIC,"Button",BS_AUTORADIOBUTTON,95,
+ 52,53,11
+ CONTROL "Cubic",IDC_CUBIC,"Button",BS_AUTORADIOBUTTON,95,63,53,
+ 11
+ LTEXT "Fastest",IDC_STATIC,150,22,24,8
+ LTEXT "Nicest",IDC_STATIC,150,65,21,8
+ GROUPBOX "Thread Priority",IDC_STATIC,85,85,100,60
+ CONTROL "Slider1",IDC_THREAD_PRI,"msctls_trackbar32",
+ TBS_AUTOTICKS | WS_TABSTOP,100,95,70,15
+ LTEXT "Normal",IDC_STATIC,90,110,23,8
+ LTEXT "Higher",IDC_STATIC,125,110,22,8
+ LTEXT "Highest",IDC_STATIC,155,110,25,8
+ LTEXT "You shouldn't normally change this",IDC_STATIC,95,120,
+ 75,20
+ GROUPBOX "Buffer Size",IDC_STATIC,195,10,95,70
+ CONTROL "Slider1",IDC_BUFFERSIZE,"msctls_trackbar32",
+ TBS_AUTOTICKS | WS_TABSTOP,200,20,85,15
+ RTEXT "0 KS",IDC_BUFFERSIZE2,250,35,35,10
+ LTEXT "Increase this value to give better skip protection on slower computers",
+ IDC_STATIC,200,45,85,30
+ DEFPUSHBUTTON "OK",IDC_OK,155,150,65,15,0,WS_EX_DLGMODALFRAME
+ PUSHBUTTON "Cancel",IDC_CANCEL,225,150,65,15,0,WS_EX_DLGMODALFRAME
+ PUSHBUTTON "Default",IDC_DEFAULT,5,150,70,15
+ PUSHBUTTON "Nicest",IDC_NICEST,210,100,65,15
+ PUSHBUTTON "Fastest",IDC_FASTEST,210,120,65,15
+ GROUPBOX "Quick Settings",IDC_STATIC,195,85,95,60
+END
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_CONFIG, DIALOG
+ BEGIN
+ LEFTMARGIN, 6
+ RIGHTMARGIN, 289
+ TOPMARGIN, 13
+ BOTTOMMARGIN, 163
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/dumb/dumb-kode54/winamp/gui.c b/dumb/dumb-kode54/winamp/gui.c
new file mode 100644
index 00000000..45ac978b
--- /dev/null
+++ b/dumb/dumb-kode54/winamp/gui.c
@@ -0,0 +1,331 @@
+
+#include <stdio.h>
+#include <windows.h>
+//#include <mmreg.h>
+//#include <msacm.h>
+#include <math.h>
+#include <commctrl.h>
+
+#include "in_duh.h"
+#include "resource.h"
+
+#include "gui.h"
+
+
+/* Registry settings */
+#define REGISTRY_KEY "Software\\Winamp\\DUH Plug-in"
+static HKEY registry = INVALID_HANDLE_VALUE;
+
+/* Some default values for config */
+int config_bits_per_sample = 16;
+int config_frequency = 44100;
+int config_stereo = CHANNEL_STEREO;
+int config_resampling = RESAMPLING_CUBIC;
+int config_buffer_size = 8192;
+int config_thread_priority = PRIORITY_HIGH;
+
+
+/*************************
+ * Registry manipulation */
+
+static int read_registry(char const *name, unsigned long type, void *ptr, unsigned long size)
+{
+ unsigned long reg_type;
+
+ if (registry == INVALID_HANDLE_VALUE ||
+ RegQueryValueEx(registry, name, 0, &reg_type, ptr, &size) != ERROR_SUCCESS
+ || reg_type != type)
+
+ return -1;
+
+ return 0;
+}
+
+
+static int write_registry(char const *name, unsigned long type, void *ptr, unsigned long size)
+{
+ if (registry == INVALID_HANDLE_VALUE
+ || RegSetValueEx(registry, name, 0, type, ptr, size) != ERROR_SUCCESS)
+
+ return -1;
+
+ return 0;
+}
+
+
+# define LOAD_REG_INT(name, var, defaultv) (read_registry(name, REG_DWORD, \
+ &(var), sizeof(var)) == -1 ? ((var) = (defaultv)) : (var))
+
+# define LOAD_REG_STRING(name, var, defaultv) (read_registry(name, REG_SZ, \
+ (var), sizeof(var)) == -1 ? strcpy(var, (defaultv)) : (var))
+
+# define SAVE_REG_INT(name, var) (write_registry(name, REG_DWORD, &(var), sizeof(var)))
+# define SAVE_REG_STRING(name, var) (write_registry(name, REG_SZ, (var), sizeof(var)))
+
+
+/*********************
+ * Range check */
+
+#define CHECK_RANGE(x, a, b) x = ((x < (a)) ? (a) : ((x > (b)) ? (b) : x))
+
+
+/*********************
+ * Config Dialog Box */
+
+
+static INT_PTR CALLBACK config_dialog(HWND dialog, UINT message,
+ WPARAM wparam, LPARAM lparam)
+{
+ int which;
+ int temp;
+ static int old_slider1 = 0;
+ char str[64];
+
+ (void)lparam;
+
+ switch (message) {
+ case WM_INITDIALOG:
+
+ /* Ok, now we need to set up the dialog's controls
+ * to match the current config
+ */
+
+ /* Channels */
+ CheckDlgButton(dialog, IDC_STEREO, config_stereo == CHANNEL_STEREO ? BST_CHECKED : BST_UNCHECKED);
+
+ /* Bits per sample */
+ switch (config_bits_per_sample) {
+ case 8: which = IDC_8BPS; break;
+ case 16: which = IDC_16BPS; break;
+ default:
+ which = 0;
+ }
+ if (which)
+ CheckRadioButton(dialog, IDC_8BPS, IDC_16BPS, which);
+
+ /* Resampling method */
+ switch (config_resampling) {
+ case RESAMPLING_ALIASING: which = IDC_ALIASING; break;
+ case RESAMPLING_LINEAR: which = IDC_LINEAR; break;
+ case RESAMPLING_LINEAR2: which = IDC_LINEAR_LOW_PASS; break;
+ case RESAMPLING_QUADRATIC: which = IDC_QUADRATIC; break;
+ case RESAMPLING_CUBIC: which = IDC_CUBIC; break;
+ default:
+ which = 0;
+ }
+ if (which)
+ CheckRadioButton(dialog, IDC_ALIASING, IDC_CUBIC, which);
+
+ /* Frequency */
+ switch (config_frequency) {
+ case 11025: which = IDC_11KHZ; break;
+ case 22050: which = IDC_22KHZ; break;
+ case 44100: which = IDC_44KHZ; break;
+ case 48000: which = IDC_48KHZ; break;
+ default:
+ which = 0;
+ }
+ if (which)
+ CheckRadioButton(dialog, IDC_11KHZ, IDC_48KHZ, which);
+
+ /* Buffer size - 1 KB -> 32 KB slider */
+ old_slider1 = (int)(log(config_buffer_size) / log(2)) - 10;
+ SendDlgItemMessage(dialog, IDC_BUFFERSIZE, TBM_SETRANGE, FALSE, MAKELONG(0, 5));
+ SendDlgItemMessage(dialog, IDC_BUFFERSIZE, TBM_SETPOS, TRUE, old_slider1);
+ sprintf(str, "%i KS", config_buffer_size / 1024);
+ SetDlgItemText(dialog, IDC_BUFFERSIZE2, str);
+
+ /* Thread Priority */
+ SendDlgItemMessage(dialog, IDC_THREAD_PRI, TBM_SETRANGE, FALSE, MAKELONG(0, 2));
+ SendDlgItemMessage(dialog, IDC_THREAD_PRI, TBM_SETPOS, TRUE, config_thread_priority);
+
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wparam)) {
+
+ case IDC_DEFAULT:
+ /* Load default settings */
+
+ /* Channels */
+ CheckDlgButton(dialog, IDC_STEREO, BST_CHECKED);
+
+ /* Bits per sample */
+ CheckRadioButton(dialog, IDC_8BPS, IDC_16BPS, IDC_16BPS);
+
+ /* Resampling method */
+ CheckRadioButton(dialog, IDC_ALIASING, IDC_CUBIC, IDC_LINEAR_LOW_PASS);
+
+ /* Frequency */
+ CheckRadioButton(dialog, IDC_11KHZ, IDC_48KHZ, IDC_44KHZ);
+
+ /* Buffer size - 1 KB -> 32 KB slider */
+ SendDlgItemMessage(dialog, IDC_BUFFERSIZE, TBM_SETPOS, TRUE, 3);
+ sprintf(str, "%i KS", 8192 / 1024);
+ SetDlgItemText(dialog, IDC_BUFFERSIZE2, str);
+
+ /* Thread Priority */
+ SendDlgItemMessage(dialog, IDC_THREAD_PRI, TBM_SETPOS, TRUE, PRIORITY_HIGH);
+
+ return TRUE;
+
+ case IDC_NICEST:
+ /* Load nicest settings */
+
+ /* Channels */
+ CheckDlgButton(dialog, IDC_STEREO, BST_CHECKED);
+
+ /* Bits per sample */
+ CheckRadioButton(dialog, IDC_8BPS, IDC_16BPS, IDC_16BPS);
+
+ /* Resampling method */
+ CheckRadioButton(dialog, IDC_ALIASING, IDC_CUBIC, IDC_CUBIC);
+
+ /* Frequency */
+ CheckRadioButton(dialog, IDC_11KHZ, IDC_48KHZ, IDC_48KHZ);
+
+ /* Buffer size - 1 KB -> 32 KB slider */
+ SendDlgItemMessage(dialog, IDC_BUFFERSIZE, TBM_SETPOS, TRUE, 3);
+ sprintf(str, "%i KS", 8192 / 1024);
+ SetDlgItemText(dialog, IDC_BUFFERSIZE2, str);
+
+ /* Thread Priority */
+ SendDlgItemMessage(dialog, IDC_THREAD_PRI, TBM_SETPOS, TRUE, PRIORITY_HIGH);
+
+ return TRUE;
+
+ case IDC_FASTEST:
+ /* Load fastest settings */
+
+ /* Channels */
+ CheckDlgButton(dialog, IDC_STEREO, BST_UNCHECKED);
+
+ /* Bits per sample */
+ CheckRadioButton(dialog, IDC_8BPS, IDC_16BPS, IDC_8BPS);
+
+ /* Resampling method */
+ CheckRadioButton(dialog, IDC_ALIASING, IDC_CUBIC, IDC_ALIASING);
+
+ /* Frequency */
+ CheckRadioButton(dialog, IDC_11KHZ, IDC_48KHZ, IDC_11KHZ);
+
+ /* Buffer size - 1 KB -> 32 KB slider */
+ SendDlgItemMessage(dialog, IDC_BUFFERSIZE, TBM_SETPOS, TRUE, 3);
+ sprintf(str, "%i KS", 8192 / 1024);
+ SetDlgItemText(dialog, IDC_BUFFERSIZE2, str);
+
+ /* Thread Priority */
+ SendDlgItemMessage(dialog, IDC_THREAD_PRI, TBM_SETPOS, TRUE, PRIORITY_HIGH);
+
+ return TRUE;
+
+ case IDC_OK:
+ /* Read back configuration */
+ config_stereo = (IsDlgButtonChecked(dialog, IDC_STEREO) == BST_CHECKED) ? CHANNEL_STEREO : CHANNEL_MONO;
+ config_bits_per_sample = (IsDlgButtonChecked(dialog, IDC_8BPS) == BST_CHECKED) ? 8 : 16;
+ config_resampling = (IsDlgButtonChecked(dialog, IDC_ALIASING) == BST_CHECKED) ? RESAMPLING_ALIASING
+ : (IsDlgButtonChecked(dialog, IDC_LINEAR) == BST_CHECKED) ? RESAMPLING_LINEAR
+ : (IsDlgButtonChecked(dialog, IDC_LINEAR_LOW_PASS) == BST_CHECKED) ? RESAMPLING_LINEAR2
+ : (IsDlgButtonChecked(dialog, IDC_QUADRATIC) == BST_CHECKED) ? RESAMPLING_QUADRATIC
+ : RESAMPLING_CUBIC;
+ config_frequency = (IsDlgButtonChecked(dialog, IDC_11KHZ) == BST_CHECKED) ? 11025
+ : (IsDlgButtonChecked(dialog, IDC_22KHZ) == BST_CHECKED) ? 22050
+ : (IsDlgButtonChecked(dialog, IDC_44KHZ) == BST_CHECKED) ? 44100
+ : 48000;
+ config_buffer_size = 1 << (SendDlgItemMessage(dialog, IDC_BUFFERSIZE, TBM_GETPOS, 0, 0) + 10);
+
+ config_thread_priority = SendDlgItemMessage(dialog, IDC_THREAD_PRI, TBM_GETPOS, 0, 0);
+
+ case IDCANCEL:
+ case IDC_CANCEL:
+ case IDCLOSE:
+ EndDialog(dialog, wparam);
+ return TRUE;
+ }
+ break;
+ default:
+ temp = 1 << (SendDlgItemMessage(dialog, IDC_BUFFERSIZE, TBM_GETPOS, 0, 0) + 10);
+ if (temp != old_slider1) {
+ old_slider1 = temp;
+ sprintf(str, "%i KS", temp / 1024);
+ SetDlgItemText(dialog, IDC_BUFFERSIZE2, str);
+ return TRUE;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+
+
+/* DUH Config dialog */
+void config(HWND hwndParent) {
+
+ if (DialogBox(mod.hDllInstance, MAKEINTRESOURCE(IDD_CONFIG),
+ hwndParent, config_dialog) == IDC_OK) {
+
+ SAVE_REG_INT("Stereo", config_stereo);
+ SAVE_REG_INT("Frequency", config_frequency);
+ SAVE_REG_INT("Resampling", config_resampling);
+ SAVE_REG_INT("BitsPerSample", config_bits_per_sample);
+ SAVE_REG_INT("BufferSize", config_buffer_size);
+ SAVE_REG_INT("ThreadPriority", config_thread_priority);
+ }
+}
+
+
+void config_init(void) {
+
+ /* Load config from registry */
+
+ if (RegCreateKeyEx(HKEY_CURRENT_USER, REGISTRY_KEY, 0, "",
+ REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, 0,
+ &registry, 0) != ERROR_SUCCESS)
+
+ registry = INVALID_HANDLE_VALUE;
+
+ LOAD_REG_INT("Stereo", config_stereo, CHANNEL_STEREO);
+ LOAD_REG_INT("Frequency", config_frequency, 44100);
+ LOAD_REG_INT("Resampling", config_resampling, RESAMPLING_CUBIC);
+ LOAD_REG_INT("BitsPerSample", config_bits_per_sample, 16);
+ LOAD_REG_INT("BufferSize", config_buffer_size, 8192);
+ LOAD_REG_INT("ThreadPriority", config_thread_priority, PRIORITY_HIGH);
+
+ CHECK_RANGE(config_stereo, CHANNEL_MONO, CHANNEL_STEREO);
+ if ( config_frequency != 11025
+ && config_frequency != 22050
+ && config_frequency != 44100
+ && config_frequency != 48000)
+ config_frequency = 44100;
+ CHECK_RANGE(config_resampling, RESAMPLING_ALIASING, RESAMPLING_CUBIC);
+ if ( config_bits_per_sample != 8
+ && config_bits_per_sample != 16)
+ config_bits_per_sample = 16;
+ CHECK_RANGE(config_buffer_size, 1024, 32768);
+ CHECK_RANGE(config_thread_priority, PRIORITY_NORMAL, PRIORITY_HIGHEST);
+
+
+ return;
+}
+
+
+
+void config_quit(void) {
+
+ /* Close registry key */
+ if (registry != INVALID_HANDLE_VALUE) {
+ RegCloseKey(registry);
+ registry = INVALID_HANDLE_VALUE;
+ }
+
+ return;
+}
+
+
+
+/* About box, yay! */
+void about(HWND hwndParent) {
+ MessageBox(hwndParent, "DUH! Winamp Plugin\n Version " VERSION " (x86)\n",
+ "About:", MB_OK | MB_ICONINFORMATION);
+}
+
diff --git a/dumb/dumb-kode54/winamp/gui.h b/dumb/dumb-kode54/winamp/gui.h
new file mode 100644
index 00000000..7924b247
--- /dev/null
+++ b/dumb/dumb-kode54/winamp/gui.h
@@ -0,0 +1,26 @@
+#define CHANNEL_MONO 1
+#define CHANNEL_STEREO 2
+
+#define RESAMPLING_ALIASING 0
+#define RESAMPLING_LINEAR 1
+#define RESAMPLING_LINEAR2 2
+#define RESAMPLING_QUADRATIC 3
+#define RESAMPLING_CUBIC 4
+
+#define PRIORITY_NORMAL 0
+#define PRIORITY_HIGH 1
+#define PRIORITY_HIGHEST 2
+
+
+extern void config(HWND hwndParent);
+extern void about(HWND hwndParent);
+
+extern void config_init(void);
+extern void config_quit(void);
+
+extern int config_bits_per_sample;
+extern int config_frequency;
+extern int config_stereo;
+extern int config_resampling;
+extern int config_buffer_size;
+extern int config_thread_priority;
diff --git a/dumb/dumb-kode54/winamp/in2.h b/dumb/dumb-kode54/winamp/in2.h
new file mode 100644
index 00000000..d156c34b
--- /dev/null
+++ b/dumb/dumb-kode54/winamp/in2.h
@@ -0,0 +1,123 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * in2.h - Winamp plug-in header file / / \ \
+ * | < / \_
+ * By Bob. | \/ /\ /
+ * \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+
+#include "out.h"
+
+// note: exported symbol is now winampGetInModule2.
+
+#define IN_VER 0x100
+
+typedef struct
+{
+ int version; // module type (IN_VER)
+ char *description; // description of module, with version string
+
+ HWND hMainWindow; // winamp's main window (filled in by winamp)
+ HINSTANCE hDllInstance; // DLL instance handle (Also filled in by winamp)
+
+ char *FileExtensions; // "mp3\0Layer 3 MPEG\0mp2\0Layer 2 MPEG\0mpg\0Layer 1 MPEG\0"
+ // May be altered from Config, so the user can select what they want
+
+ int is_seekable; // is this stream seekable?
+ int UsesOutputPlug; // does this plug-in use the output plug-ins? (musn't ever change, ever :)
+
+ void (*Config)(HWND hwndParent); // configuration dialog
+ void (*About)(HWND hwndParent); // about dialog
+
+ void (*Init)(); // called at program init
+ void (*Quit)(); // called at program quit
+
+ void (*GetFileInfo)(char *file, char *title, int *length_in_ms); // if file == NULL, current playing is used
+ int (*InfoBox)(char *file, HWND hwndParent);
+
+ int (*IsOurFile)(char *fn); // called before extension checks, to allow detection of mms://, etc
+ // playback stuff
+ int (*Play)(char *fn); // return zero on success, -1 on file-not-found, some other value on other (stopping winamp) error
+ void (*Pause)(); // pause stream
+ void (*UnPause)(); // unpause stream
+ int (*IsPaused)(); // ispaused? return 1 if paused, 0 if not
+ void (*Stop)(); // stop (unload) stream
+
+ // time stuff
+ int (*GetLength)(); // get length in ms
+ int (*GetOutputTime)(); // returns current output time in ms. (usually returns outMod->GetOutputTime()
+ void (*SetOutputTime)(int time_in_ms); // seeks to point in stream (in ms). Usually you signal yoru thread to seek, which seeks and calls outMod->Flush()..
+
+ // volume stuff
+ void (*SetVolume)(int volume); // from 0 to 255.. usually just call outMod->SetVolume
+ void (*SetPan)(int pan); // from -127 to 127.. usually just call outMod->SetPan
+
+ // in-window builtin vis stuff
+
+ void (*SAVSAInit)(int maxlatency_in_ms, int srate); // call once in Play(). maxlatency_in_ms should be the value returned from outMod->Open()
+ // call after opening audio device with max latency in ms and samplerate
+ void (*SAVSADeInit)(); // call in Stop()
+
+
+ // simple vis supplying mode
+ void (*SAAddPCMData)(void *PCMData, int nch, int bps, int timestamp);
+ // sets the spec data directly from PCM data
+ // quick and easy way to get vis working :)
+ // needs at least 576 samples :)
+
+ // advanced vis supplying mode, only use if you're cool. Use SAAddPCMData for most stuff.
+ int (*SAGetMode)(); // gets csa (the current type (4=ws,2=osc,1=spec))
+ // use when calling SAAdd()
+ void (*SAAdd)(void *data, int timestamp, int csa); // sets the spec data, filled in by winamp
+
+
+ // vis stuff (plug-in)
+ // simple vis supplying mode
+ void (*VSAAddPCMData)(void *PCMData, int nch, int bps, int timestamp); // sets the vis data directly from PCM data
+ // quick and easy way to get vis working :)
+ // needs at least 576 samples :)
+
+ // advanced vis supplying mode, only use if you're cool. Use VSAAddPCMData for most stuff.
+ int (*VSAGetMode)(int *specNch, int *waveNch); // use to figure out what to give to VSAAdd
+ void (*VSAAdd)(void *data, int timestamp); // filled in by winamp, called by plug-in
+
+
+ // call this in Play() to tell the vis plug-ins the current output params.
+ void (*VSASetInfo)(int nch, int srate);
+
+
+ // dsp plug-in processing:
+ // (filled in by winamp, called by input plug)
+
+ // returns 1 if active (which means that the number of samples returned by dsp_dosamples
+ // could be greater than went in.. Use it to estimate if you'll have enough room in the
+ // output buffer
+ int (*dsp_isactive)();
+
+ // returns number of samples to output. This can be as much as twice numsamples.
+ // be sure to allocate enough buffer for samples, then.
+ int (*dsp_dosamples)(short int *samples, int numsamples, int bps, int nch, int srate);
+
+
+ // eq stuff
+ void (*EQSet)(int on, char data[10], int preamp); // 0-64 each, 31 is +0, 0 is +12, 63 is -12. Do nothing to ignore.
+
+ // info setting (filled in by winamp)
+ void (*SetInfo)(int bitrate, int srate, int stereo, int synched); // if -1, changes ignored? :)
+
+ Out_Module *outMod; // filled in by winamp, optionally used :)
+} In_Module;
+
+
diff --git a/dumb/dumb-kode54/winamp/in_duh.c b/dumb/dumb-kode54/winamp/in_duh.c
new file mode 100644
index 00000000..e97c07af
--- /dev/null
+++ b/dumb/dumb-kode54/winamp/in_duh.c
@@ -0,0 +1,655 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * in_duh.c - Winamp plug-in for DUMB. / / \ \
+ * | < / \_
+ * By Bob. | \/ /\ /
+ * \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+
+//#define SOFTVOLUME
+
+#include "in_duh.h"
+#include "resource.h"
+#include "gui.h"
+
+
+typedef struct DUH_PLAYER
+{
+ int n_channels;
+ DUH_SIGRENDERER *dr;
+ float volume;
+}
+DUH_PLAYER;
+
+
+/* Winamp Output module; we write to this to tell Winamp what to play */
+In_Module mod;
+
+/* Currently playing file */
+DUH *duh;
+DUH_PLAYER *duh_player;
+int init_duh = TRUE;
+char *duh_filename = NULL;
+#ifdef SOFTVOLUME
+int thevolume = 255;
+#endif
+
+
+
+/******************
+ * Configuration */
+static int bits_per_sample;
+static int frequency;
+static int stereo;
+static int resampling;
+static int buffer_size;
+static int thread_priority;
+
+
+
+/****************
+ * Winamp Stuff */
+
+HANDLE input_file = INVALID_HANDLE_VALUE; /* input file handle */
+
+int killDecodeThread = 0; /* the kill switch for the decode thread */
+HANDLE thread_handle = INVALID_HANDLE_VALUE; /* the handle to the decode thread */
+
+DWORD WINAPI __stdcall DecodeThread(void *b); /* the decode thread procedure */
+
+/* Avoid CRT. Evil. Big. Bloated. */
+BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
+{
+ (void)hInst;
+ (void)ul_reason_for_call;
+ (void)lpReserved;
+ return TRUE;
+}
+
+
+/* Post this to the main window at end of file (after playback as stopped) */
+#define WM_WA_MPEG_EOF (WM_USER+2)
+
+
+
+/* Stuff for interfacing with Winamp */
+int decode_pos_ms;
+int paused;
+int seek_needed; /* if != -1, it is the point that the decode thread should seek to, in ms. */
+//char *sample_buffer = NULL;
+
+//int buffer_pos = 0;
+
+
+
+/* Init DUH */
+void init()
+{
+ config_init();
+ dumb_register_stdfiles();
+}
+
+
+
+/* De-Init */
+void quit()
+{
+ config_quit();
+
+ if (duh_player)
+ free(duh_player);
+ if (duh)
+ unload_duh(duh);
+
+ if (duh_filename)
+ free(duh_filename);
+
+ //if (sample_buffer)
+ //free(sample_buffer);
+
+ dumb_exit();
+}
+
+
+
+/* WA SDK: used for detecting URL streams.. unused here. strncmp(fn,"http://",7) to detect HTTP streams, etc */
+/* I -think- we need to tell Winamp that the file should use our plug-in */
+int isourfile(char *fn) { (void)fn; return 0; }
+
+void stop_duh(DUH_PLAYER *dp)
+{
+ if (dp) {
+ duh_end_sigrenderer(dp->dr);
+ free(dp);
+ }
+}
+
+
+int play(char *fn)
+{
+ static int priority_table[] = {
+ THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST
+ };
+ int maxlatency;
+ unsigned long thread_id;
+
+ /* Get rid of an old DUH */
+ if (duh_player) {
+ stop_duh(duh_player);
+ duh_player = NULL;
+ }
+
+ if (duh)
+ unload_duh(duh);
+
+ /* Load file */
+ duh = load_duh(fn);
+ if (!duh) {
+ duh = dumb_load_it(fn);
+ if (!duh) {
+ duh = dumb_load_xm(fn);
+ if (!duh) {
+ duh = dumb_load_s3m(fn);
+ if (!duh) {
+ duh = dumb_load_mod(fn);
+ if (!duh)
+ return 1;
+ }
+ }
+ }
+ }
+
+ init_duh = TRUE;
+
+ /* Set up some things for Winamp */
+ paused = 0;
+ decode_pos_ms = 0;
+ seek_needed = -1;
+
+ bits_per_sample = config_bits_per_sample;
+ frequency = config_frequency;
+ stereo = config_stereo;
+ resampling = config_resampling;
+ buffer_size = config_buffer_size;
+ thread_priority = priority_table[config_thread_priority];
+
+ /* Create the sample buffer */
+ //if (sample_buffer)
+ //free(sample_buffer);
+
+ //sample_buffer = malloc(((bits_per_sample + 7) / 8) * stereo * buffer_size);
+
+ //if (!sample_buffer)
+ //return 1;
+
+ //buffer_pos = 0;
+
+ /* Note: I -really- don't know what this does. Winamp's SDK doesn't mention this function... */
+ maxlatency = mod.outMod->Open(frequency, stereo, bits_per_sample, -1, -1);
+ if (maxlatency < 0) { /* error opening device */
+ unload_duh(duh);
+ duh = NULL;
+ return 1;
+ }
+
+ /* Store the file name */
+ if (duh_filename)
+ free(duh_filename);
+ duh_filename = strdup(fn);
+
+ /* Note2: Dunno what does too; damn those Winamp docs */
+ /* dividing by 1000 for the first parameter of setinfo makes it */
+ /* display 'H'... for hundred.. i.e. 14H Kbps. */
+ mod.SetInfo((frequency * bits_per_sample * stereo) / 1000,
+ frequency / 1000, stereo, 1);
+
+ /* Ditto */
+ /* initialize vis stuff */
+ mod.SAVSAInit(maxlatency, frequency);
+ mod.VSASetInfo(frequency, stereo);
+
+ /* Ditthree */
+#ifdef SOFTVOLUME
+ mod.outMod->SetVolume(255);
+#else
+ mod.outMod->SetVolume(-666); /* set the output plug-ins default volume */
+#endif
+
+ /* Ok, now we set up the decoding thread */
+ killDecodeThread = 0;
+ thread_handle = (HANDLE) CreateThread(NULL,0,DecodeThread,&killDecodeThread,0,&thread_id);
+ SetThreadPriority(thread_handle, thread_priority);
+
+ return 0;
+}
+
+
+
+/* Standard Winamp stuff */
+void pause() { paused = 1; mod.outMod->Pause(1); }
+void unpause() { paused = 0; mod.outMod->Pause(0); }
+int ispaused() { return paused; }
+
+
+
+/* Stop playing the file */
+void stop()
+{
+ if (thread_handle != INVALID_HANDLE_VALUE)
+ {
+ killDecodeThread=1;
+ if (WaitForSingleObject(thread_handle,INFINITE) == WAIT_TIMEOUT)
+ {
+ MessageBox(mod.hMainWindow,"Error asking thread to die!\n","Error killing decode thread",0);
+ TerminateThread(thread_handle,0);
+ }
+ CloseHandle(thread_handle);
+ thread_handle = INVALID_HANDLE_VALUE;
+ }
+ if (duh_player) {
+ stop_duh(duh_player);
+ duh_player = NULL;
+ }
+
+ if (duh) {
+ unload_duh(duh);
+ duh = NULL;
+ }
+
+ /* Should I unload the file? It takes time to reload... */
+
+ mod.outMod->Close();
+
+ mod.SAVSADeInit();
+}
+
+
+
+int getlength()
+{
+ return (int) ((LONG_LONG)duh_get_length(duh) * 1000 >> 16);
+}
+
+
+
+int getoutputtime()
+{
+ return decode_pos_ms + (mod.outMod->GetOutputTime() - mod.outMod->GetWrittenTime());
+}
+
+
+
+void setoutputtime(int time_in_ms)
+{
+ seek_needed = time_in_ms;
+}
+
+
+
+void setvolume(int volume)
+{
+#ifdef SOFTVOLUME
+ thevolume = volume;
+ if (duh_player) duh_player->volume = volume / 255.0f;
+#else
+ mod.outMod->SetVolume(volume);
+#endif
+}
+
+
+
+void setpan(int pan) { mod.outMod->SetPan(pan); }
+
+
+
+int infoDlg(char *fn, HWND hwnd)
+{
+ (void)fn;
+ (void)hwnd;
+ // TODO: implement info dialog.
+ return 0;
+}
+
+
+
+static const char *fn_basename(const char *filename)
+{
+ for (;;) {
+ const char *p = strpbrk(filename, "/\\");
+ if (!p) return filename;
+ filename = p + 1;
+ }
+}
+
+
+
+void getfileinfo(char *filename, char *title, int *length_in_ms)
+{
+ if (!filename || !*filename) { /* currently playing file */
+
+ if (length_in_ms)
+ *length_in_ms = getlength();
+
+ if (title) {
+ const char *mod_title = duh_get_tag(duh, "TITLE");
+ if (mod_title && mod_title[0])
+ sprintf(title, "%s - %s", fn_basename(filename), mod_title);
+ else
+ strcpy(title, fn_basename(filename));
+ }
+ }
+ else { /* some other file */
+#if 1 // needs fixing better than this! more to add to DUMB's API?
+ if (length_in_ms || title) {
+ DUH *duh = load_duh(filename);
+ if (!duh) {
+ duh = dumb_load_it(filename);
+ if (!duh) {
+ duh = dumb_load_xm(filename);
+ if (!duh) {
+ duh = dumb_load_s3m(filename);
+ if (!duh) {
+ duh = dumb_load_mod(filename);
+ if (!duh)
+ return;
+ }
+ }
+ }
+ }
+
+ if (length_in_ms)
+ *length_in_ms = (int)((LONG_LONG)duh_get_length(duh) * 1000 >> 16);
+
+ if (title) {
+ const char *mod_title = duh_get_tag(duh, "TITLE");
+ if (mod_title && mod_title[0])
+ sprintf(title, "%s - %s", fn_basename(duh_filename), mod_title);
+ else
+ strcpy(title, fn_basename(duh_filename));
+ }
+
+ unload_duh(duh);
+ }
+#elif 0
+ /* This code only works (worked?) for DUH files. */
+ DUMBFILE *d = dumbfile_open(filename);
+
+ if (!d)
+ return;
+
+ if (dumbfile_mgetl(d) != DUH_SIGNATURE)
+ return;
+
+ *length_in_ms = (unsigned int)((LONG_LONG)dumbfile_igetl(d) * 1000 >> 16);
+
+ if (title)
+ strcpy(title, filename);
+
+ dumbfile_close(d);
+#else
+ *length_in_ms = 1000 * 60 * 10;
+
+ if (title)
+ strcpy(title, filename);
+#endif
+ }
+}
+
+
+
+void eq_set(int on, char data[10], int preamp)
+{
+ (void)on;
+ (void)data;
+ (void)preamp;
+ /* No equalizer support, sorry */
+}
+
+
+
+DUH_PLAYER *start_duh(DUH *duh, int n_channels, long pos, float volume)
+{
+ DUH_PLAYER *dp;
+
+ // This restriction is imposed by Allegro. <-- um...?
+ ASSERT(n_channels > 0);
+ ASSERT(n_channels <= 2);
+
+ if (!duh)
+ return NULL;
+
+ dp = malloc(sizeof(*dp));
+ if (!dp)
+ return NULL;
+
+ dp->n_channels = n_channels;
+
+ dp->dr = duh_start_sigrenderer(duh, 0, n_channels, pos);
+
+ if (!dp->dr) {
+ free(dp);
+ return NULL;
+ }
+
+ {
+ DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(dp->dr);
+ dumb_it_set_loop_callback(itsr, &dumb_it_callback_terminate, NULL);
+ dumb_it_set_xm_speed_zero_callback(itsr, &dumb_it_callback_terminate, NULL);
+ }
+
+ dp->volume = volume;
+
+ return dp;
+}
+
+
+
+
+void pause_duh(DUH_PLAYER *dp)
+{
+ (void)dp;
+ pause();
+}
+
+
+
+void resume_duh(DUH_PLAYER *dp)
+{
+ (void)dp;
+ unpause();
+}
+
+
+
+void set_duh_volume(DUH_PLAYER *dp, float volume)
+{
+ (void)dp;
+ setvolume((int)(volume * 255.0f));
+}
+
+
+
+
+/* Generate 576 samples of data from the DUH_PLAYER */
+int get_576_samples(DUH_PLAYER *dp, char *buf)
+{
+#if 1
+ if (!dp) return 0;
+
+ long n = duh_render(dp->dr, bits_per_sample, bits_per_sample == 8 ? 1 : 0,
+ dp->volume, 65536.0f / frequency, 576, buf);
+
+ return n * (bits_per_sample >> 3) * stereo;
+#else
+ long n;
+ int bps = ((bits_per_sample + 7) / 8) * stereo;
+
+ if (!dp)
+ return 0;
+
+ if (buffer_pos == 0 || buffer_pos + 576 >= buffer_size) {
+
+ if (buffer_pos) {
+ memmove(sample_buffer, sample_buffer + bps * buffer_pos, (buffer_size - buffer_pos) * bps);
+ buffer_pos = buffer_size - buffer_pos;
+ }
+
+ n = duh_render(dp->dr, bits_per_sample, bits_per_sample == 8 ? 1 : 0,
+ dp->volume, 65536.0f / frequency, buffer_size - buffer_pos,
+ sample_buffer + buffer_pos * bps);
+
+ if (n > 576) n = 576;
+
+ n *= bps;
+ buffer_pos = 0;
+ }
+
+ memcpy(buf, sample_buffer + buffer_pos * bps, 576 * bps);
+
+ buffer_pos += 576;
+
+ return 576 * bps;
+#endif
+}
+
+
+DWORD WINAPI __stdcall DecodeThread(void *b)
+{
+ static char buf[576 * 4];
+ int done = 0;
+ int length = 0;
+
+ if (init_duh) {
+ dumb_resampling_quality = resampling;
+#ifdef SOFTVOLUME
+ duh_player = start_duh(duh, stereo, 0, thevolume / 255.0f);
+#else
+ duh_player = start_duh(duh, stereo, 0, 1.0f);
+#endif
+ init_duh = FALSE;
+ }
+ length = getlength();
+
+
+ while (! *((int *)b) )
+ {
+ if (seek_needed != -1) {
+
+ decode_pos_ms = seek_needed-(seek_needed%1000);
+ seek_needed = -1;
+ done = 0;
+
+ mod.outMod->Flush(decode_pos_ms);
+
+ /* Position the playback pointer */
+ stop_duh(duh_player);
+ duh_player = start_duh(duh, stereo, (unsigned int)(decode_pos_ms * 65536.0 / 1000.0), 1.0);
+ }
+ if (done) {
+ mod.outMod->CanWrite();
+
+ if (!mod.outMod->IsPlaying()) {
+ PostMessage(mod.hMainWindow,WM_WA_MPEG_EOF,0,0);
+ return 0;
+ }
+ Sleep(10);
+ }
+ else if (mod.outMod->CanWrite() >= ((576 * stereo * ((bits_per_sample + 7) / 8)))) {
+
+ int l = get_576_samples(duh_player, buf);
+
+ if (!l || decode_pos_ms >= length) {
+ done = 1;
+ }
+ else {
+ /* Vis plug-ins interface */
+ mod.SAAddPCMData((char *)buf, stereo, bits_per_sample, decode_pos_ms);
+ mod.VSAAddPCMData((char *)buf, stereo, bits_per_sample, decode_pos_ms);
+
+ /* Add PCM to output buffer */
+ decode_pos_ms += (576 * 1000) / frequency;
+
+ if (mod.dsp_isactive())
+ l = mod.dsp_dosamples((short *)buf, l / stereo / ((bits_per_sample + 7) / 8), bits_per_sample,
+ stereo, frequency) * (stereo * ((bits_per_sample + 7) / 8));
+
+ mod.outMod->Write(buf, l);
+ }
+ }
+ else /* Nothing to do this pass */
+ Sleep(config_frequency / 1000);
+ }
+ return 0;
+}
+
+
+
+In_Module mod =
+{
+ IN_VER,
+ "DUH! Player v" VERSION
+#ifdef __alpha
+ " (AXP)"
+#else
+ " (x86)"
+#endif
+ ,
+ 0, /* hMainWindow */
+ 0, /* hDllInstance */
+ "DUH\0Dynamic Universal Harmony File (*.DUH)\0"
+ "IT\0Impulse Tracker Module (*.IT)\0"
+ "XM\0Fast Tracker 2 Module (*.XM)\0"
+ "S3M\0Scream Tracker 3 Module (*.S3M)\0"
+ "MOD\0Amiga Module (*.MOD)\0"
+ ,
+ 1, /* is_seekable */
+ 1, /* uses output */
+ config,
+ about,
+ init,
+ quit,
+ getfileinfo,
+ infoDlg,
+ isourfile,
+ play,
+ pause,
+ unpause,
+ ispaused,
+ stop,
+
+ getlength,
+ getoutputtime,
+ setoutputtime,
+
+ setvolume,
+ setpan,
+
+ 0,0,0,0,0,0,0,0,0, /* vis stuff */
+
+
+ 0,0, /* dsp */
+
+ eq_set,
+
+ NULL, /* setinfo */
+
+ 0 /* out_mod */
+
+};
+
+__declspec( dllexport ) In_Module * winampGetInModule2()
+{
+ return &mod;
+}
diff --git a/dumb/dumb-kode54/winamp/in_duh.h b/dumb/dumb-kode54/winamp/in_duh.h
new file mode 100644
index 00000000..38dfe182
--- /dev/null
+++ b/dumb/dumb-kode54/winamp/in_duh.h
@@ -0,0 +1,40 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * in_duh.h - Winamp plug-in header file. / / \ \
+ * | < / \_
+ * By Bob. | \/ /\ /
+ * \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <windows.h>
+//#include <mmreg.h>
+//#include <msacm.h>
+#include <math.h>
+
+#include "in2.h"
+
+#include "../include/dumb.h"
+
+
+/******************
+ * Plug in config */
+
+#define VERSION "0.1"
+
+
+#define STREAM_SIZE 576
+#define STREAM_FREQ 44100
+
+
+extern In_Module mod;
diff --git a/dumb/dumb-kode54/winamp/minalleg.c b/dumb/dumb-kode54/winamp/minalleg.c
new file mode 100644
index 00000000..af645339
--- /dev/null
+++ b/dumb/dumb-kode54/winamp/minalleg.c
@@ -0,0 +1,2179 @@
+/* Mini Allegro - File and compression routines */
+/* Ripped from Allegro WIP */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <io.h>
+#include "minalleg.h"
+
+
+#ifndef _USE_LFN
+#define _USE_LFN 0
+#endif
+
+
+#if (!defined S_IRUSR) && (!defined SCAN_DEPEND)
+ #define S_IRUSR S_IREAD
+ #define S_IWUSR S_IWRITE
+#endif
+
+
+
+#define N 4096 /* 4k buffers for LZ compression */
+#define F 18 /* upper limit for LZ match length */
+#define THRESHOLD 2 /* LZ encode string into pos and length
+ if match size is greater than this */
+
+
+typedef struct PACK_DATA /* stuff for doing LZ compression */
+{
+ int state; /* where have we got to in the pack? */
+ int i, c, len, r, s;
+ int last_match_length, code_buf_ptr;
+ unsigned char mask;
+ char code_buf[17];
+ int match_position;
+ int match_length;
+ int lson[N+1]; /* left children, */
+ int rson[N+257]; /* right children, */
+ int dad[N+1]; /* and parents, = binary search trees */
+ unsigned char text_buf[N+F-1]; /* ring buffer, with F-1 extra bytes
+ for string comparison */
+} PACK_DATA;
+
+
+typedef struct UNPACK_DATA /* for reading LZ files */
+{
+ int state; /* where have we got to? */
+ int i, j, k, r, c;
+ int flags;
+ unsigned char text_buf[N+F-1]; /* ring buffer, with F-1 extra bytes
+ for string comparison */
+} UNPACK_DATA;
+
+
+static int refill_buffer(PACKFILE *f);
+static int flush_buffer(PACKFILE *f, int last);
+static void pack_inittree(PACK_DATA *dat);
+static void pack_insertnode(int r, PACK_DATA *dat);
+static void pack_deletenode(int p, PACK_DATA *dat);
+static int pack_write(PACKFILE *file, PACK_DATA *dat, int size, unsigned char *buf, int last);
+static int pack_read(PACKFILE *file, UNPACK_DATA *dat, int s, unsigned char *buf);
+
+static char the_password[256] = "";
+
+int _packfile_filesize = 0;
+int _packfile_datasize = 0;
+
+int _packfile_type = 0;
+
+
+#define FA_DAT_FLAGS (FA_RDONLY | FA_ARCH)
+
+
+int pack_getc(PACKFILE *f)
+{
+ f->buf_size--;
+ if (f->buf_size > 0)
+ return *(f->buf_pos++);
+ else
+ return _sort_out_getc(f);
+}
+
+
+int pack_putc(int c, PACKFILE *f)
+{
+ f->buf_size++;
+ if (f->buf_size >= F_BUF_SIZE)
+ return _sort_out_putc(c, f);
+ else
+ return (*(f->buf_pos++) = c);
+}
+
+
+/* fix_filename_case:
+ * Converts filename to upper case.
+ */
+char *fix_filename_case(char *filename)
+{
+ return filename;
+}
+
+
+
+/* fix_filename_slashes:
+ * Converts '/' or '\' to system specific path separator.
+ */
+char *fix_filename_slashes(char *filename)
+{
+ int pos, c;
+
+ for (pos=0; c = filename[pos]; pos++) {
+ if ((c == '/') || (c == '\\'))
+ filename[pos] = OTHER_PATH_SEPARATOR;
+ }
+
+ return filename;
+}
+
+
+
+/* fix_filename_path:
+ * Canonicalizes path.
+ */
+char *fix_filename_path(char *dest, char *path, int size)
+{
+ int saved_errno = errno;
+ char buf[512], buf2[512];
+ char *p;
+ int pos = 0;
+ int drive = -1;
+ int c1, i;
+
+ memset(buf, 0, sizeof(buf));
+
+ #if (DEVICE_SEPARATOR != 0) && (DEVICE_SEPARATOR != '\0')
+
+ /* check whether we have a drive letter */
+ c1 = tolower(path[0]);
+ if ((c1 >= 'a') && (c1 <= 'z')) {
+ int c2 = path[1];
+ if (c2 == DEVICE_SEPARATOR) {
+ drive = c1 - 'a';
+ path += 2;
+ }
+ }
+
+ /* if not, use the current drive */
+ if (drive < 0)
+ drive = _al_getdrive();
+
+ buf[pos] = drive + 'a'; pos++;
+ buf[pos] = DEVICE_SEPARATOR; pos++;
+ #endif
+
+ /* if the path is relative, make it absolute */
+ if ((path[0] != '/') && (path[0] != OTHER_PATH_SEPARATOR) && (path[0] != '#')) {
+ _al_getdcwd(drive, buf2, sizeof(buf2) - 1);
+ put_backslash(buf2);
+
+ p = buf2;
+ if ((tolower(p[0]) >= 'a') && (tolower(p[0]) <= 'z') && (p[1] == DEVICE_SEPARATOR))
+ p += 2;
+
+ memcpy(buf + pos, p, sizeof(buf) - pos);
+ pos = strlen(buf);
+ }
+
+ /* add our path, and clean it up a bit */
+ memcpy(buf + pos, path, sizeof(buf) - pos);
+
+ fix_filename_case(buf);
+ fix_filename_slashes(buf);
+
+ /* remove duplicate slashes */
+ buf2[0] = OTHER_PATH_SEPARATOR;
+ buf2[1] = OTHER_PATH_SEPARATOR;
+ buf2[2] = 0;
+ pos = 2;
+
+ while ((p = strstr(buf, buf2)) != NULL)
+ memmove(p, p + 1, strlen(p));
+
+ /* remove /./ patterns */
+ buf2[0] = OTHER_PATH_SEPARATOR;
+ buf2[1] = '.';
+ buf2[2] = OTHER_PATH_SEPARATOR;
+ buf2[3] = 0;
+ pos = 3;
+
+ while ((p = strstr(buf, buf2)) != NULL) {
+ memmove(p, p + 1, strlen(p));
+ memmove(p, p + 1, strlen(p));
+ }
+
+ /* collapse /../ patterns */
+ buf2[0] = OTHER_PATH_SEPARATOR;
+ buf2[1] = '.';
+ buf2[2] = '.';
+ buf2[3] = OTHER_PATH_SEPARATOR;
+ buf2[4] = 0;
+ pos = 4;
+
+ while ((p = strstr(buf, buf2)) != NULL) {
+ for (i = 0; buf + i < p; i++)
+ ;
+
+ while (--i > 0) {
+ c1 = buf[i];
+
+ if (c1 == OTHER_PATH_SEPARATOR)
+ break;
+
+ if (c1 == DEVICE_SEPARATOR) {
+ i++;
+ break;
+ }
+ }
+
+ if (i < 0)
+ i = 0;
+
+ p += strlen(buf2);
+ memmove(buf+i+1, p, strlen(p) + 1);
+ }
+
+ /* all done! */
+ memcpy(dest, buf, MIN(size, (int)strlen(buf)));
+
+ errno = saved_errno;
+
+ return dest;
+}
+
+
+
+/* replace_filename:
+ * Replaces filename in path with different one.
+ * It does not append '/' to the path.
+ */
+char *replace_filename(char *dest, char *path, char *filename, int size)
+{
+ char tmp[512];
+ int pos, c;
+
+ pos = strlen(path);
+
+ while (pos>0) {
+ c = path[pos - 1];
+ if ((c == '/') || (c == OTHER_PATH_SEPARATOR) || (c == DEVICE_SEPARATOR) || (c == '#'))
+ break;
+ pos--;
+ }
+ memset(tmp, 0, sizeof(tmp));
+ memcpy(tmp, path, MIN(sizeof(tmp), pos));
+ memcpy(tmp + MIN(sizeof(tmp), pos), filename, MIN(strlen(filename), sizeof(tmp) - pos));
+
+ memcpy(dest, tmp, MIN((int)strlen(tmp), size));
+
+ return dest;
+}
+
+
+
+/* replace_extension:
+ * Replaces extension in filename with different one.
+ * It appends '.' if it is not present in the filename.
+ */
+char *replace_extension(char *dest, char *filename, char *ext, int size)
+{
+ char tmp[512];
+ int pos, end, c;
+
+ pos = end = strlen(filename);
+
+ while (pos>0) {
+ c = filename[pos - 1];
+ if ((c == '.') || (c == '/') || (c == OTHER_PATH_SEPARATOR) || (c == DEVICE_SEPARATOR) || (c == '#'))
+ break;
+ pos--;
+ }
+
+ if (filename[pos - 1] == '.')
+ end = pos - 1;
+
+ memcpy(tmp, filename, MIN((int)sizeof(tmp), strlen(filename)));
+ if (strlen(tmp) < sizeof(tmp)-1) {
+ tmp[strlen(tmp)+1] = 0;
+ tmp[strlen(tmp)] = '.';
+ }
+ memcpy(tmp + strlen(tmp), ext, MIN(sizeof(tmp) - (int)strlen(tmp), (int)strlen(ext)));
+ memcpy(dest, tmp, MIN(size, sizeof(tmp)));
+
+ return dest;
+}
+
+
+
+/* append_filename:
+ * Append filename to path, adding separator if necessary.
+ */
+char *append_filename(char *dest, char *path, char *filename, int size)
+{
+ char tmp[512];
+ int pos, c;
+
+ memcpy(tmp, path, MIN(sizeof(tmp) - 1, strlen(path)));
+ tmp[511] = 0;
+ pos = strlen(tmp);
+
+ if ((pos > 0) && (tmp[pos] < ((int)sizeof(tmp) - 2))) {
+ c = tmp[pos - 1];
+
+ if ((c != '/') && (c != OTHER_PATH_SEPARATOR) && (c != DEVICE_SEPARATOR) && (c != '#')) {
+ tmp[pos] = OTHER_PATH_SEPARATOR;
+ pos++;
+ tmp[pos] = 0;
+ }
+ }
+
+ memcpy(tmp + strlen(tmp), filename, MIN(sizeof(tmp) - (int)strlen(tmp), (int)strlen(filename)));
+ memcpy(dest, tmp, MIN(sizeof(tmp), (int)strlen(tmp)));
+
+ return dest;
+}
+
+
+
+/* get_filename:
+ * When passed a completely specified file path, this returns a pointer
+ * to the filename portion. Both '\' and '/' are recognized as directory
+ * separators.
+ */
+char *get_filename(char *path)
+{
+ int pos, c;
+
+ pos = strlen(path);
+
+ while (pos>0) {
+ c = path[pos - 1];
+ if ((c == '/') || (c == OTHER_PATH_SEPARATOR) || (c == DEVICE_SEPARATOR) || (c == '#'))
+ break;
+ pos--;
+ }
+
+ return (char *)path + pos;
+}
+
+
+
+/* get_extension:
+ * When passed a complete filename (with or without path information)
+ * this returns a pointer to the file extension.
+ */
+char *get_extension(char *filename)
+{
+ int pos, c;
+
+ pos = strlen(filename);
+
+ while (pos>0) {
+ c = filename[pos - 1];
+ if ((c == '.') || (c == '/') || (c == OTHER_PATH_SEPARATOR) || (c == DEVICE_SEPARATOR) || (c == '#'))
+ break;
+ pos--;
+ }
+
+ if ((pos>0) && (filename[pos-1] == '.'))
+ return (char *)filename + pos;
+
+ return (char *)filename + strlen(filename);
+}
+
+
+
+/* put_backslash:
+ * If the last character of the filename is not a \, /, or #, this routine
+ * will concatenate a \ on to it.
+ */
+void put_backslash(char *filename)
+{
+ int c;
+
+ if (*filename) {
+ c = filename[strlen(filename)-1];
+
+ if ((c == '/') || (c == OTHER_PATH_SEPARATOR) || (c == DEVICE_SEPARATOR) || (c == '#'))
+ return;
+ }
+
+ filename += strlen(filename);
+ filename[0] = OTHER_PATH_SEPARATOR;
+ filename[1] = 0;
+}
+
+
+
+/* file_exists:
+ * Checks whether a file matching the given name and attributes exists,
+ * returning non zero if it does. The file attribute may contain any of
+ * the FA_* constants from dir.h. If aret is not null, it will be set
+ * to the attributes of the matching file. If an error occurs the system
+ * error code will be stored in errno.
+ */
+int file_exists(char *filename, int attrib, int *aret)
+{
+ int a;
+
+ if (!_al_file_isok(filename))
+ return 0;
+
+ if (!_al_file_exists(filename, attrib, &a))
+ return FALSE;
+
+ if (aret)
+ *aret = a;
+
+ return TRUE;
+}
+
+
+
+/* exists:
+ * Shortcut version of file_exists().
+ */
+int exists(char *filename)
+{
+ return file_exists(filename, FA_ARCH | FA_RDONLY, NULL);
+}
+
+
+
+/* file_size:
+ * Returns the size of a file, in bytes.
+ * If the file does not exist or an error occurs, it will return zero
+ * and store the system error code in errno.
+ */
+long file_size(char *filename)
+{
+
+ if (!_al_file_isok(filename))
+ return 0;
+
+ return _al_file_size(filename);
+}
+
+
+
+/* file_time:
+ * Returns a file time-stamp.
+ * If the file does not exist or an error occurs, it will return zero
+ * and store the system error code in errno.
+ */
+time_t file_time(char *filename)
+{
+ if (!_al_file_isok(filename))
+ return 0;
+
+ return _al_file_time(filename);
+}
+
+
+
+/* delete_file:
+ * Removes a file from the disk.
+ */
+int delete_file(char *filename)
+{
+ errno = 0;
+
+ if (!_al_file_isok(filename))
+ return errno;
+
+ unlink(filename);
+
+ return errno;
+}
+
+
+
+/* for_each_file:
+ * Finds all the files on the disk which match the given wildcard
+ * specification and file attributes, and executes callback() once for
+ * each. callback() will be passed three arguments, the first a string
+ * which contains the completed filename, the second being the attributes
+ * of the file, and the third an int which is simply a copy of param (you
+ * can use this for whatever you like). If an error occurs an error code
+ * will be stored in errno, and callback() can cause for_each_file() to
+ * abort by setting errno itself. Returns the number of successful calls
+ * made to callback(). The file attribute may contain any of the FA_*
+ * flags from dir.h.
+ */
+int for_each_file(char *name, int attrib, void (*callback)(char *filename, int attrib, int param), int param)
+{
+ char dta_name[512], buf[512];
+ void *dta;
+ int dta_attrib;
+ int c = 0;
+
+ if (!_al_file_isok(name))
+ return 0;
+
+ dta = _al_findfirst(name, attrib, dta_name, &dta_attrib);
+
+ if (!dta)
+ return 0;
+
+ do {
+ replace_filename(buf, name, dta_name, sizeof(buf));
+ (*callback)(buf, dta_attrib, param);
+ if (errno != 0)
+ break;
+ c++;
+ } while (_al_findnext(dta, dta_name, &dta_attrib) == 0);
+
+ _al_findclose(dta);
+
+ errno = 0;
+ return c;
+}
+
+
+
+/* packfile_password:
+ * Sets the password to be used by all future read/write operations.
+ */
+void packfile_password(char *password)
+{
+ int i = 0;
+ int c;
+
+ if (password) {
+ while ((c = *password) != 0) {
+ password++;
+ the_password[i++] = c;
+ if (i >= (int)sizeof(the_password)-1)
+ break;
+ }
+ }
+
+ the_password[i] = 0;
+}
+
+
+
+/* encrypt_id:
+ * Helper for encrypting magic numbers, using the current password.
+ */
+static long encrypt_id(long x, int new_format)
+{
+ long mask = 0;
+ int i, pos;
+
+ if (the_password[0]) {
+ for (i=0; the_password[i]; i++)
+ mask ^= ((long)the_password[i] << ((i&3) * 8));
+
+ for (i=0, pos=0; i<4; i++) {
+ mask ^= (long)the_password[pos++] << (24-i*8);
+ if (!the_password[pos])
+ pos = 0;
+ }
+
+ if (new_format)
+ mask ^= 42;
+ }
+
+ return x ^ mask;
+}
+
+
+
+/* clone_password:
+ * Sets up a local password string for use by this packfile.
+ */
+static int clone_password(PACKFILE *f)
+{
+ if (the_password[0]) {
+ if ((f->passdata = malloc(strlen(the_password)+1)) == NULL) {
+ errno = ENOMEM;
+ return FALSE;
+ }
+ strcpy(f->passdata, the_password);
+ }
+ else
+ f->passdata = NULL;
+
+ f->passpos = f->passdata;
+
+ return TRUE;
+}
+
+
+
+/* pack_fopen:
+ * Opens a file according to mode, which may contain any of the flags:
+ * 'r': open file for reading.
+ * 'w': open file for writing, overwriting any existing data.
+ * 'p': open file in 'packed' mode. Data will be compressed as it is
+ * written to the file, and automatically uncompressed during read
+ * operations. Files created in this mode will produce garbage if
+ * they are read without this flag being set.
+ * '!': open file for writing in normal, unpacked mode, but add the value
+ * F_NOPACK_MAGIC to the start of the file, so that it can be opened
+ * in packed mode and Allegro will automatically detect that the
+ * data does not need to be decompressed.
+ *
+ * Instead of these flags, one of the constants F_READ, F_WRITE,
+ * F_READ_PACKED, F_WRITE_PACKED or F_WRITE_NOPACK may be used as the second
+ * argument to fopen().
+ *
+ * On success, fopen() returns a pointer to a file structure, and on error
+ * it returns NULL and stores an error code in errno. An attempt to read a
+ * normal file in packed mode will cause errno to be set to EDOM.
+ */
+PACKFILE *pack_fopen(const char *filename, char *mode)
+{
+ PACKFILE *f, *f2;
+ long header = FALSE;
+ int c;
+
+ _packfile_type = 0;
+
+ if (!_al_file_isok(filename))
+ return NULL;
+
+ errno = 0;
+
+ if ((f = malloc(sizeof(PACKFILE))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ f->buf_pos = f->buf;
+ f->flags = 0;
+ f->buf_size = 0;
+ f->filename = NULL;
+ f->passdata = NULL;
+ f->passpos = NULL;
+
+ while ((c = *(mode++)) != 0) {
+ switch (c) {
+ case 'r': case 'R': f->flags &= ~PACKFILE_FLAG_WRITE; break;
+ case 'w': case 'W': f->flags |= PACKFILE_FLAG_WRITE; break;
+ case 'p': case 'P': f->flags |= PACKFILE_FLAG_PACK; break;
+ case '!': f->flags &= ~PACKFILE_FLAG_PACK; header = TRUE; break;
+ }
+ }
+
+ if (f->flags & PACKFILE_FLAG_WRITE) {
+ if (f->flags & PACKFILE_FLAG_PACK) {
+ /* write a packed file */
+ PACK_DATA *dat = malloc(sizeof(PACK_DATA));
+
+ if (!dat) {
+ errno = ENOMEM;
+ free(f);
+ return NULL;
+ }
+
+ if ((f->parent = pack_fopen(filename, F_WRITE)) == NULL) {
+ free(dat);
+ free(f);
+ return NULL;
+ }
+
+ pack_mputl(encrypt_id(F_PACK_MAGIC, TRUE), f->parent);
+
+ f->todo = 4;
+
+ for (c=0; c < N - F; c++)
+ dat->text_buf[c] = 0;
+
+ dat->state = 0;
+
+ f->pack_data = dat;
+ }
+ else {
+ /* write a 'real' file */
+ f->parent = NULL;
+ f->pack_data = NULL;
+
+ if (!clone_password(f)) {
+ free(f);
+ return NULL;
+ }
+#ifndef ALLEGRO_MPW
+ f->hndl = open(filename, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+#else
+ f->hndl = _al_open(uconvert_toascii(filename, NULL), O_WRONLY | O_BINARY | O_CREAT | O_TRUNC);
+#endif
+ if (f->hndl < 0) {
+ if (f->passdata)
+ free(f->passdata);
+ free(f);
+ return NULL;
+ }
+
+ errno = 0;
+ f->todo = 0;
+
+ if (header)
+ pack_mputl(encrypt_id(F_NOPACK_MAGIC, TRUE), f);
+ }
+ }
+ else {
+ if (f->flags & PACKFILE_FLAG_PACK) {
+ /* read a packed file */
+ UNPACK_DATA *dat = malloc(sizeof(UNPACK_DATA));
+
+ if (!dat) {
+ errno = ENOMEM;
+ free(f);
+ return NULL;
+ }
+
+ if ((f->parent = pack_fopen(filename, F_READ)) == NULL) {
+ free(dat);
+ free(f);
+ return NULL;
+ }
+
+ header = pack_mgetl(f->parent);
+
+ if ((f->parent->passpos) &&
+ ((header == encrypt_id(F_PACK_MAGIC, FALSE)) ||
+ (header == encrypt_id(F_NOPACK_MAGIC, FALSE)))) {
+
+ /* backward compatibility mode */
+ pack_fclose(f->parent);
+
+ if (!clone_password(f)) {
+ free(dat);
+ free(f);
+ return NULL;
+ }
+
+ if ((f->parent = pack_fopen(filename, F_READ)) == NULL) {
+ free(dat);
+ free(f);
+ return NULL;
+ }
+
+ f->parent->flags |= PACKFILE_FLAG_OLD_CRYPT;
+ f->flags |= PACKFILE_FLAG_OLD_CRYPT;
+
+ pack_mgetl(f->parent);
+
+ if (header == encrypt_id(F_PACK_MAGIC, FALSE))
+ header = encrypt_id(F_PACK_MAGIC, TRUE);
+ else
+ header = encrypt_id(F_NOPACK_MAGIC, TRUE);
+ }
+
+ if (header == encrypt_id(F_PACK_MAGIC, TRUE)) {
+ for (c=0; c < N - F; c++)
+ dat->text_buf[c] = 0;
+ dat->state = 0;
+ f->todo = LONG_MAX;
+ f->pack_data = (char *)dat;
+ }
+ else if (header == encrypt_id(F_NOPACK_MAGIC, TRUE)) {
+ f2 = f->parent;
+ free(dat);
+ free(f);
+ return f2;
+ }
+ else {
+ pack_fclose(f->parent);
+ free(dat);
+ free(f);
+ if (errno == 0)
+ errno = EDOM;
+ return NULL;
+ }
+ }
+ else {
+ /* read a 'real' file */
+ f->parent = NULL;
+ f->pack_data = NULL;
+
+ f->todo = _al_file_size(filename);
+
+ if (errno) {
+ free(f);
+ return NULL;
+ }
+
+ if (!clone_password(f)) {
+ free(f);
+ return NULL;
+ }
+
+#ifndef ALLEGRO_MPW
+ f->hndl = open(filename, O_RDONLY | O_BINARY, S_IRUSR | S_IWUSR);
+#else
+ f->hndl = _al_open(uconvert_toascii(filename, NULL), O_RDONLY | O_BINARY);
+#endif
+
+ if (f->hndl < 0) {
+ if (f->passdata)
+ free(f->passdata);
+ free(f);
+ return NULL;
+ }
+ }
+ }
+
+ return f;
+}
+
+
+
+/* pack_fclose:
+ * Closes a file after it has been read or written.
+ * Returns zero on success. On error it returns an error code which is
+ * also stored in errno. This function can fail only when writing to
+ * files: if the file was opened in read mode it will always succeed.
+ */
+int pack_fclose(PACKFILE *f)
+{
+ if (f) {
+ if (f->flags & PACKFILE_FLAG_WRITE) {
+ if (f->flags & PACKFILE_FLAG_CHUNK)
+ return pack_fclose(pack_fclose_chunk(f));
+
+ flush_buffer(f, TRUE);
+ }
+
+ if (f->passdata)
+ free(f->passdata);
+
+ if (f->pack_data)
+ free(f->pack_data);
+
+ if (f->parent)
+ pack_fclose(f->parent);
+ else
+ close(f->hndl);
+
+ free(f);
+ return errno;
+ }
+
+ return 0;
+}
+
+
+
+/* pack_fopen_chunk:
+ * Opens a sub-chunk of the specified file, for reading or writing depending
+ * on the type of the file. The returned file pointer describes the sub
+ * chunk, and replaces the original file, which will no longer be valid.
+ * When writing to a chunk file, data is sent to the original file, but
+ * is prefixed with two length counts (32 bit, big-endian). For uncompressed
+ * chunks these will both be set to the length of the data in the chunk.
+ * For compressed chunks, created by setting the pack flag, the first will
+ * contain the raw size of the chunk, and the second will be the negative
+ * size of the uncompressed data. When reading chunks, the pack flag is
+ * ignored, and the compression type is detected from the sign of the
+ * second size value. The file structure used to read chunks checks the
+ * chunk size, and will return EOF if you try to read past the end of
+ * the chunk. If you don't read all of the chunk data, when you call
+ * pack_fclose_chunk(), the parent file will advance past the unused data.
+ * When you have finished reading or writing a chunk, you should call
+ * pack_fclose_chunk() to return to your original file.
+ */
+PACKFILE *pack_fopen_chunk(PACKFILE *f, int pack)
+{
+ PACKFILE *chunk;
+ char *name;
+ int c;
+
+ if (f->flags & PACKFILE_FLAG_WRITE) {
+
+ /* write a sub-chunk */
+ name = tmpnam(NULL);
+ chunk = pack_fopen(name, (pack ? F_WRITE_PACKED : F_WRITE_NOPACK));
+
+ if (chunk) {
+ chunk->filename = strdup(name);
+
+ if (pack)
+ chunk->parent->parent = f;
+ else
+ chunk->parent = f;
+
+ chunk->flags |= PACKFILE_FLAG_CHUNK;
+ }
+ }
+ else {
+ /* read a sub-chunk */
+ _packfile_filesize = pack_mgetl(f);
+ _packfile_datasize = pack_mgetl(f);
+
+ if ((chunk = malloc(sizeof(PACKFILE))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ chunk->buf_pos = chunk->buf;
+ chunk->flags = PACKFILE_FLAG_CHUNK;
+ chunk->buf_size = 0;
+ chunk->filename = NULL;
+ chunk->passdata = NULL;
+ chunk->passpos = NULL;
+ chunk->parent = f;
+
+ if (f->flags & PACKFILE_FLAG_OLD_CRYPT) {
+ /* backward compatibility mode */
+ if (f->passdata) {
+ if ((chunk->passdata = malloc(strlen(f->passdata)+1)) == NULL) {
+ errno = ENOMEM;
+ free(chunk);
+ return NULL;
+ }
+ strcpy(chunk->passdata, f->passdata);
+ chunk->passpos = chunk->passdata + (long)f->passpos - (long)f->passdata;
+ f->passpos = f->passdata;
+ }
+ chunk->flags |= PACKFILE_FLAG_OLD_CRYPT;
+ }
+
+ if (_packfile_datasize < 0) {
+ /* read a packed chunk */
+ UNPACK_DATA *dat = malloc(sizeof(UNPACK_DATA));
+
+ if (!dat) {
+ errno = ENOMEM;
+ if (chunk->passdata) free(chunk->passdata);
+ free(chunk);
+ return NULL;
+ }
+
+ for (c=0; c < N - F; c++)
+ dat->text_buf[c] = 0;
+
+ dat->state = 0;
+ _packfile_datasize = -_packfile_datasize;
+ chunk->todo = _packfile_datasize;
+ chunk->pack_data = (char *)dat;
+ chunk->flags |= PACKFILE_FLAG_PACK;
+ }
+ else {
+ /* read an uncompressed chunk */
+ chunk->todo = _packfile_datasize;
+ chunk->pack_data = NULL;
+ }
+ }
+
+ return chunk;
+}
+
+
+
+/* pack_fclose_chunk:
+ * Call after reading or writing a sub-chunk. This closes the chunk file,
+ * and returns a pointer to the original file structure (the one you
+ * passed to pack_fopen_chunk()), to allow you to read or write data
+ * after the chunk.
+ */
+PACKFILE *pack_fclose_chunk(PACKFILE *f)
+{
+ PACKFILE *parent = f->parent;
+ PACKFILE *tmp;
+ char *name = f->filename;
+ int header;
+
+ if (f->flags & PACKFILE_FLAG_WRITE) {
+ /* finish writing a chunk */
+ _packfile_datasize = f->todo + f->buf_size - 4;
+
+ if (f->flags & PACKFILE_FLAG_PACK) {
+ parent = parent->parent;
+ f->parent->parent = NULL;
+ }
+ else
+ f->parent = NULL;
+
+ f->flags &= ~PACKFILE_FLAG_CHUNK;
+ pack_fclose(f);
+
+ tmp = pack_fopen(name, F_READ);
+ _packfile_filesize = tmp->todo - 4;
+
+ header = pack_mgetl(tmp);
+
+ pack_mputl(_packfile_filesize, parent);
+
+ if (header == encrypt_id(F_PACK_MAGIC, TRUE))
+ pack_mputl(-_packfile_datasize, parent);
+ else
+ pack_mputl(_packfile_datasize, parent);
+
+ while (!pack_feof(tmp))
+ pack_putc(pack_getc(tmp), parent);
+
+ pack_fclose(tmp);
+
+ delete_file(name);
+ free(name);
+ }
+ else {
+ /* finish reading a chunk */
+ while (f->todo > 0)
+ pack_getc(f);
+
+ if ((f->passpos) && (f->flags & PACKFILE_FLAG_OLD_CRYPT))
+ parent->passpos = parent->passdata + (long)f->passpos - (long)f->passdata;
+
+ if (f->passdata)
+ free(f->passdata);
+
+ if (f->pack_data)
+ free(f->pack_data);
+
+ free(f);
+ }
+
+ return parent;
+}
+
+
+
+/* pack_fseek:
+ * Like the stdio fseek() function, but only supports forward seeks
+ * relative to the current file position.
+ */
+int pack_fseek(PACKFILE *f, int offset)
+{
+ int i;
+
+ if (f->flags & PACKFILE_FLAG_WRITE)
+ return -1;
+
+ errno = 0;
+
+ /* skip forward through the buffer */
+ if (f->buf_size > 0) {
+ i = MIN(offset, f->buf_size);
+ f->buf_size -= i;
+ f->buf_pos += i;
+ offset -= i;
+ if ((f->buf_size <= 0) && (f->todo <= 0))
+ f->flags |= PACKFILE_FLAG_EOF;
+ }
+
+ /* need to seek some more? */
+ if (offset > 0) {
+ i = MIN(offset, f->todo);
+
+ if ((f->flags & PACKFILE_FLAG_PACK) || (f->passpos)) {
+ /* for compressed files, we just have to read through the data */
+ while (i > 0) {
+ pack_getc(f);
+ i--;
+ }
+ }
+ else {
+ if (f->parent) {
+ /* pass the seek request on to the parent file */
+ pack_fseek(f->parent, i);
+ }
+ else {
+ /* do a real seek */
+ lseek(f->hndl, i, SEEK_CUR);
+ }
+ f->todo -= i;
+ if (f->todo <= 0)
+ f->flags |= PACKFILE_FLAG_EOF;
+ }
+ }
+
+ return errno;
+}
+
+
+
+/* pack_igetw:
+ * Reads a 16 bit word from a file, using intel byte ordering.
+ */
+int pack_igetw(PACKFILE *f)
+{
+ int b1, b2;
+
+ if ((b1 = pack_getc(f)) != EOF)
+ if ((b2 = pack_getc(f)) != EOF)
+ return ((b2 << 8) | b1);
+
+ return EOF;
+}
+
+
+
+/* pack_igetl:
+ * Reads a 32 bit long from a file, using intel byte ordering.
+ */
+long pack_igetl(PACKFILE *f)
+{
+ int b1, b2, b3, b4;
+
+ if ((b1 = pack_getc(f)) != EOF)
+ if ((b2 = pack_getc(f)) != EOF)
+ if ((b3 = pack_getc(f)) != EOF)
+ if ((b4 = pack_getc(f)) != EOF)
+ return (((long)b4 << 24) | ((long)b3 << 16) |
+ ((long)b2 << 8) | (long)b1);
+
+ return EOF;
+}
+
+
+
+/* pack_iputw:
+ * Writes a 16 bit int to a file, using intel byte ordering.
+ */
+int pack_iputw(int w, PACKFILE *f)
+{
+ int b1, b2;
+
+ b1 = (w & 0xFF00) >> 8;
+ b2 = w & 0x00FF;
+
+ if (pack_putc(b2,f)==b2)
+ if (pack_putc(b1,f)==b1)
+ return w;
+
+ return EOF;
+}
+
+
+
+/* pack_iputw:
+ * Writes a 32 bit long to a file, using intel byte ordering.
+ */
+long pack_iputl(long l, PACKFILE *f)
+{
+ int b1, b2, b3, b4;
+
+ b1 = (int)((l & 0xFF000000L) >> 24);
+ b2 = (int)((l & 0x00FF0000L) >> 16);
+ b3 = (int)((l & 0x0000FF00L) >> 8);
+ b4 = (int)l & 0x00FF;
+
+ if (pack_putc(b4,f)==b4)
+ if (pack_putc(b3,f)==b3)
+ if (pack_putc(b2,f)==b2)
+ if (pack_putc(b1,f)==b1)
+ return l;
+
+ return EOF;
+}
+
+
+
+/* pack_mgetw:
+ * Reads a 16 bit int from a file, using motorola byte-ordering.
+ */
+int pack_mgetw(PACKFILE *f)
+{
+ int b1, b2;
+
+ if ((b1 = pack_getc(f)) != EOF)
+ if ((b2 = pack_getc(f)) != EOF)
+ return ((b1 << 8) | b2);
+
+ return EOF;
+}
+
+
+
+/* pack_mgetl:
+ * Reads a 32 bit long from a file, using motorola byte-ordering.
+ */
+long pack_mgetl(PACKFILE *f)
+{
+ int b1, b2, b3, b4;
+
+ if ((b1 = pack_getc(f)) != EOF)
+ if ((b2 = pack_getc(f)) != EOF)
+ if ((b3 = pack_getc(f)) != EOF)
+ if ((b4 = pack_getc(f)) != EOF)
+ return (((long)b1 << 24) | ((long)b2 << 16) |
+ ((long)b3 << 8) | (long)b4);
+
+ return EOF;
+}
+
+
+
+/* pack_mputw:
+ * Writes a 16 bit int to a file, using motorola byte-ordering.
+ */
+int pack_mputw(int w, PACKFILE *f)
+{
+ int b1, b2;
+
+ b1 = (w & 0xFF00) >> 8;
+ b2 = w & 0x00FF;
+
+ if (pack_putc(b1,f)==b1)
+ if (pack_putc(b2,f)==b2)
+ return w;
+
+ return EOF;
+}
+
+
+
+/* pack_mputl:
+ * Writes a 32 bit long to a file, using motorola byte-ordering.
+ */
+long pack_mputl(long l, PACKFILE *f)
+{
+ int b1, b2, b3, b4;
+
+ b1 = (int)((l & 0xFF000000L) >> 24);
+ b2 = (int)((l & 0x00FF0000L) >> 16);
+ b3 = (int)((l & 0x0000FF00L) >> 8);
+ b4 = (int)l & 0x00FF;
+
+ if (pack_putc(b1,f)==b1)
+ if (pack_putc(b2,f)==b2)
+ if (pack_putc(b3,f)==b3)
+ if (pack_putc(b4,f)==b4)
+ return l;
+
+ return EOF;
+}
+
+
+
+/* pack_fread:
+ * Reads n bytes from f and stores them at memory location p. Returns the
+ * number of items read, which will be less than n if EOF is reached or an
+ * error occurs. Error codes are stored in errno.
+ */
+long pack_fread(void *p, long n, PACKFILE *f)
+{
+ unsigned char *cp = (unsigned char *)p;
+ long c;
+ int i;
+
+ for (c=0; c<n; c++) {
+ if (--(f->buf_size) > 0)
+ *(cp++) = *(f->buf_pos++);
+ else {
+ i = _sort_out_getc(f);
+ if (i == EOF)
+ return c;
+ else
+ *(cp++) = i;
+ }
+ }
+
+ return n;
+}
+
+
+
+/* pack_fwrite:
+ * Writes n bytes to the file f from memory location p. Returns the number
+ * of items written, which will be less than n if an error occurs. Error
+ * codes are stored in errno.
+ */
+long pack_fwrite(void *p, long n, PACKFILE *f)
+{
+ unsigned char *cp = (unsigned char *)p;
+ long c;
+
+ for (c=0; c<n; c++) {
+ if (++(f->buf_size) >= F_BUF_SIZE) {
+ if (_sort_out_putc(*cp,f) != *cp)
+ return c;
+ cp++;
+ }
+ else
+ *(f->buf_pos++)=*(cp++);
+ }
+
+ return n;
+}
+
+
+
+/* pack_ungetc:
+ * Puts a character back in the file's input buffer. Added by gfoot for
+ * use in the fgets function; maybe it should be in the API. It only works
+ * for characters just fetched by pack_getc.
+ */
+static void pack_ungetc (int ch, PACKFILE *f)
+{
+ *(--f->buf_pos) = (unsigned char) ch;
+ f->buf_size++;
+ f->flags &= ~PACKFILE_FLAG_EOF;
+}
+
+
+
+/* pack_fgets:
+ * Reads a line from a text file, storing it at location p. Stops when a
+ * linefeed is encountered, or max characters have been read. Returns a
+ * pointer to where it stored the text, or NULL on error. The end of line
+ * is handled by detecting optional '\r' characters optionally followed
+ * by '\n' characters. This supports CR-LF (DOS/Windows), LF (Unix), and
+ * CR (Mac) formats.
+ */
+char *pack_fgets(char *p, int max, PACKFILE *f)
+{
+ char *pmax;
+ int c;
+
+ errno = 0;
+
+ pmax = p+max - 1;
+
+ if (pack_feof(f)) {
+ if (1 < max) *p = 0;
+ return NULL;
+ }
+
+ while ((c = pack_getc (f)) != EOF) {
+
+ if (c == '\r' || c == '\n') {
+ /* Technically we should check there's space in the buffer, and if so,
+ * add a \n. But pack_fgets has never done this. */
+ if (c == '\r') {
+ /* eat the following \n, if any */
+ if ((c = pack_getc (f)) != '\n') pack_ungetc (c, f);
+ }
+ break;
+ }
+
+ /* is there room in the buffer? */
+ if (1 > pmax - p) {
+ pack_ungetc (c, f);
+ c = '\0';
+ break;
+ }
+
+ /* write the character */
+ *p = c;
+ p++;
+ }
+
+ /* terminate the string */
+ *p = 0;
+
+ if (c == '\0' || errno)
+ return NULL;
+
+ return p;
+}
+
+
+
+/* pack_fputs:
+ * Writes a string to a text file, returning zero on success, -1 on error.
+ */
+int pack_fputs(char *p, PACKFILE *f)
+{
+ char *s;
+
+ errno = 0;
+
+ s = p;
+
+ while (*s) {
+ if (*s == '\n')
+ pack_putc('\r', f);
+
+ pack_putc(*s, f);
+ s++;
+ }
+
+ if (errno)
+ return -1;
+ else
+ return 0;
+}
+
+
+
+/* _sort_out_getc:
+ * Helper function for the pack_getc() macro.
+ */
+int _sort_out_getc(PACKFILE *f)
+{
+ if (f->buf_size == 0) {
+ if (f->todo <= 0)
+ f->flags |= PACKFILE_FLAG_EOF;
+ return *(f->buf_pos++);
+ }
+ return refill_buffer(f);
+}
+
+
+
+/* refill_buffer:
+ * Refills the read buffer. The file must have been opened in read mode,
+ * and the buffer must be empty.
+ */
+static int refill_buffer(PACKFILE *f)
+{
+ int i, sz, done, offset;
+
+ if ((f->flags & PACKFILE_FLAG_EOF) || (f->todo <= 0)) {
+ f->flags |= PACKFILE_FLAG_EOF;
+ return EOF;
+ }
+
+ if (f->parent) {
+ if (f->flags & PACKFILE_FLAG_PACK) {
+ f->buf_size = pack_read(f->parent, (UNPACK_DATA *)f->pack_data, MIN(F_BUF_SIZE, f->todo), f->buf);
+ }
+ else {
+ f->buf_size = pack_fread(f->buf, MIN(F_BUF_SIZE, f->todo), f->parent);
+ }
+ if (f->parent->flags & PACKFILE_FLAG_EOF)
+ f->todo = 0;
+ if (f->parent->flags & PACKFILE_FLAG_ERROR)
+ goto err;
+ }
+ else {
+ f->buf_size = MIN(F_BUF_SIZE, f->todo);
+
+ offset = lseek(f->hndl, 0, SEEK_CUR);
+ done = 0;
+
+ errno = 0;
+ sz = read(f->hndl, f->buf, f->buf_size);
+
+ while (sz+done < f->buf_size) {
+ if ((sz < 0) && ((errno != EINTR) && (errno != EAGAIN)))
+ goto err;
+
+ if (sz > 0)
+ done += sz;
+
+ lseek(f->hndl, offset+done, SEEK_SET);
+ errno = 0;
+ sz = read(f->hndl, f->buf+done, f->buf_size-done);
+ }
+ errno = 0;
+
+ if (errno == EINTR)
+ errno = 0;
+
+ if ((f->passpos) && (!(f->flags & PACKFILE_FLAG_OLD_CRYPT))) {
+ for (i=0; i<f->buf_size; i++) {
+ f->buf[i] ^= *(f->passpos++);
+ if (!*f->passpos)
+ f->passpos = f->passdata;
+ }
+ }
+ }
+
+ f->todo -= f->buf_size;
+ f->buf_pos = f->buf;
+ f->buf_size--;
+ if (f->buf_size <= 0)
+ if (f->todo <= 0)
+ f->flags |= PACKFILE_FLAG_EOF;
+
+ return *(f->buf_pos++);
+
+ err:
+ errno = EFAULT;
+ f->flags |= PACKFILE_FLAG_ERROR;
+ return EOF;
+}
+
+
+
+/* _sort_out_putc:
+ * Helper function for the pack_putc() macro.
+ */
+int _sort_out_putc(int c, PACKFILE *f)
+{
+ f->buf_size--;
+
+ if (flush_buffer(f, FALSE))
+ return EOF;
+
+ f->buf_size++;
+ return (*(f->buf_pos++)=c);
+}
+
+
+
+/* flush_buffer:
+ * flushes a file buffer to the disk. The file must be open in write mode.
+ */
+static int flush_buffer(PACKFILE *f, int last)
+{
+ int i, sz, done, offset;
+
+ if (f->buf_size > 0) {
+ if (f->flags & PACKFILE_FLAG_PACK) {
+ if (pack_write(f->parent, (PACK_DATA *)f->pack_data, f->buf_size, f->buf, last))
+ goto err;
+ }
+ else {
+ if ((f->passpos) && (!(f->flags & PACKFILE_FLAG_OLD_CRYPT))) {
+ for (i=0; i<f->buf_size; i++) {
+ f->buf[i] ^= *(f->passpos++);
+ if (!*f->passpos)
+ f->passpos = f->passdata;
+ }
+ }
+
+ offset = lseek(f->hndl, 0, SEEK_CUR);
+ done = 0;
+
+ errno = 0;
+ sz = write(f->hndl, f->buf, f->buf_size);
+
+ while (sz+done < f->buf_size) {
+ if ((sz < 0) && ((errno != EINTR) && (errno != EAGAIN)))
+ goto err;
+
+ if (sz > 0)
+ done += sz;
+
+ lseek(f->hndl, offset+done, SEEK_SET);
+ errno = 0;
+ sz = write(f->hndl, f->buf+done, f->buf_size-done);
+ }
+ errno = 0;
+ }
+ f->todo += f->buf_size;
+ }
+ f->buf_pos = f->buf;
+ f->buf_size = 0;
+ return 0;
+
+ err:
+ errno = EFAULT;
+ f->flags |= PACKFILE_FLAG_ERROR;
+ return EOF;
+}
+
+
+
+/***************************************************
+ ************ LZSS compression routines ************
+ ***************************************************
+
+ This compression algorithm is based on the ideas of Lempel and Ziv,
+ with the modifications suggested by Storer and Szymanski. The algorithm
+ is based on the use of a ring buffer, which initially contains zeros.
+ We read several characters from the file into the buffer, and then
+ search the buffer for the longest string that matches the characters
+ just read, and output the length and position of the match in the buffer.
+
+ With a buffer size of 4096 bytes, the position can be encoded in 12
+ bits. If we represent the match length in four bits, the <position,
+ length> pair is two bytes long. If the longest match is no more than
+ two characters, then we send just one character without encoding, and
+ restart the process with the next letter. We must send one extra bit
+ each time to tell the decoder whether we are sending a <position,
+ length> pair or an unencoded character, and these flags are stored as
+ an eight bit mask every eight items.
+
+ This implementation uses binary trees to speed up the search for the
+ longest match.
+
+ Original code by Haruhiko Okumura, 4/6/1989.
+ 12-2-404 Green Heights, 580 Nagasawa, Yokosuka 239, Japan.
+
+ Modified for use in the Allegro filesystem by Shawn Hargreaves.
+
+ Use, distribute, and modify this code freely.
+*/
+
+
+
+/* pack_inittree:
+ * For i = 0 to N-1, rson[i] and lson[i] will be the right and left
+ * children of node i. These nodes need not be initialized. Also, dad[i]
+ * is the parent of node i. These are initialized to N, which stands for
+ * 'not used.' For i = 0 to 255, rson[N+i+1] is the root of the tree for
+ * strings that begin with character i. These are initialized to N. Note
+ * there are 256 trees.
+ */
+static void pack_inittree(PACK_DATA *dat)
+{
+ int i;
+
+ for (i=N+1; i<=N+256; i++)
+ dat->rson[i] = N;
+
+ for (i=0; i<N; i++)
+ dat->dad[i] = N;
+}
+
+
+
+/* pack_insertnode:
+ * Inserts a string of length F, text_buf[r..r+F-1], into one of the trees
+ * (text_buf[r]'th tree) and returns the longest-match position and length
+ * via match_position and match_length. If match_length = F, then removes
+ * the old node in favor of the new one, because the old one will be
+ * deleted sooner. Note r plays double role, as tree node and position in
+ * the buffer.
+ */
+static void pack_insertnode(int r, PACK_DATA *dat)
+{
+ int i, p, cmp;
+ unsigned char *key;
+ unsigned char *text_buf = dat->text_buf;
+
+ cmp = 1;
+ key = &text_buf[r];
+ p = N + 1 + key[0];
+ dat->rson[r] = dat->lson[r] = N;
+ dat->match_length = 0;
+
+ for (;;) {
+
+ if (cmp >= 0) {
+ if (dat->rson[p] != N)
+ p = dat->rson[p];
+ else {
+ dat->rson[p] = r;
+ dat->dad[r] = p;
+ return;
+ }
+ }
+ else {
+ if (dat->lson[p] != N)
+ p = dat->lson[p];
+ else {
+ dat->lson[p] = r;
+ dat->dad[r] = p;
+ return;
+ }
+ }
+
+ for (i = 1; i < F; i++)
+ if ((cmp = key[i] - text_buf[p + i]) != 0)
+ break;
+
+ if (i > dat->match_length) {
+ dat->match_position = p;
+ if ((dat->match_length = i) >= F)
+ break;
+ }
+ }
+
+ dat->dad[r] = dat->dad[p];
+ dat->lson[r] = dat->lson[p];
+ dat->rson[r] = dat->rson[p];
+ dat->dad[dat->lson[p]] = r;
+ dat->dad[dat->rson[p]] = r;
+ if (dat->rson[dat->dad[p]] == p)
+ dat->rson[dat->dad[p]] = r;
+ else
+ dat->lson[dat->dad[p]] = r;
+ dat->dad[p] = N; /* remove p */
+}
+
+
+
+/* pack_deletenode:
+ * Removes a node from a tree.
+ */
+static void pack_deletenode(int p, PACK_DATA *dat)
+{
+ int q;
+
+ if (dat->dad[p] == N)
+ return; /* not in tree */
+
+ if (dat->rson[p] == N)
+ q = dat->lson[p];
+ else
+ if (dat->lson[p] == N)
+ q = dat->rson[p];
+ else {
+ q = dat->lson[p];
+ if (dat->rson[q] != N) {
+ do {
+ q = dat->rson[q];
+ } while (dat->rson[q] != N);
+ dat->rson[dat->dad[q]] = dat->lson[q];
+ dat->dad[dat->lson[q]] = dat->dad[q];
+ dat->lson[q] = dat->lson[p];
+ dat->dad[dat->lson[p]] = q;
+ }
+ dat->rson[q] = dat->rson[p];
+ dat->dad[dat->rson[p]] = q;
+ }
+
+ dat->dad[q] = dat->dad[p];
+ if (dat->rson[dat->dad[p]] == p)
+ dat->rson[dat->dad[p]] = q;
+ else
+ dat->lson[dat->dad[p]] = q;
+
+ dat->dad[p] = N;
+}
+
+
+
+/* pack_write:
+ * Called by flush_buffer(). Packs size bytes from buf, using the pack
+ * information contained in dat. Returns 0 on success.
+ */
+static int pack_write(PACKFILE *file, PACK_DATA *dat, int size, unsigned char *buf, int last)
+{
+ int i = dat->i;
+ int c = dat->c;
+ int len = dat->len;
+ int r = dat->r;
+ int s = dat->s;
+ int last_match_length = dat->last_match_length;
+ int code_buf_ptr = dat->code_buf_ptr;
+ unsigned char mask = dat->mask;
+ int ret = 0;
+
+ if (dat->state==2)
+ goto pos2;
+ else
+ if (dat->state==1)
+ goto pos1;
+
+ dat->code_buf[0] = 0;
+ /* code_buf[1..16] saves eight units of code, and code_buf[0] works
+ as eight flags, "1" representing that the unit is an unencoded
+ letter (1 byte), "0" a position-and-length pair (2 bytes).
+ Thus, eight units require at most 16 bytes of code. */
+
+ code_buf_ptr = mask = 1;
+
+ s = 0;
+ r = N - F;
+ pack_inittree(dat);
+
+ for (len=0; (len < F) && (size > 0); len++) {
+ dat->text_buf[r+len] = *(buf++);
+ if (--size == 0) {
+ if (!last) {
+ dat->state = 1;
+ goto getout;
+ }
+ }
+ pos1:
+ ;
+ }
+
+ if (len == 0)
+ goto getout;
+
+ for (i=1; i <= F; i++)
+ pack_insertnode(r-i,dat);
+ /* Insert the F strings, each of which begins with one or
+ more 'space' characters. Note the order in which these
+ strings are inserted. This way, degenerate trees will be
+ less likely to occur. */
+
+ pack_insertnode(r,dat);
+ /* Finally, insert the whole string just read. match_length
+ and match_position are set. */
+
+ do {
+ if (dat->match_length > len)
+ dat->match_length = len; /* match_length may be long near the end */
+
+ if (dat->match_length <= THRESHOLD) {
+ dat->match_length = 1; /* not long enough match: send one byte */
+ dat->code_buf[0] |= mask; /* 'send one byte' flag */
+ dat->code_buf[code_buf_ptr++] = dat->text_buf[r]; /* send uncoded */
+ }
+ else {
+ /* send position and length pair. Note match_length > THRESHOLD */
+ dat->code_buf[code_buf_ptr++] = (unsigned char) dat->match_position;
+ dat->code_buf[code_buf_ptr++] = (unsigned char)
+ (((dat->match_position >> 4) & 0xF0) |
+ (dat->match_length - (THRESHOLD + 1)));
+ }
+
+ if ((mask <<= 1) == 0) { /* shift mask left one bit */
+ if ((file->passpos) && (file->flags & PACKFILE_FLAG_OLD_CRYPT)) {
+ dat->code_buf[0] ^= *file->passpos;
+ file->passpos++;
+ if (!*file->passpos)
+ file->passpos = file->passdata;
+ };
+
+ for (i=0; i<code_buf_ptr; i++) /* send at most 8 units of */
+ pack_putc(dat->code_buf[i], file); /* code together */
+
+ if (pack_ferror(file)) {
+ ret = EOF;
+ goto getout;
+ }
+ dat->code_buf[0] = 0;
+ code_buf_ptr = mask = 1;
+ }
+
+ last_match_length = dat->match_length;
+
+ for (i=0; (i < last_match_length) && (size > 0); i++) {
+ c = *(buf++);
+ if (--size == 0) {
+ if (!last) {
+ dat->state = 2;
+ goto getout;
+ }
+ }
+ pos2:
+ pack_deletenode(s,dat); /* delete old strings and */
+ dat->text_buf[s] = c; /* read new bytes */
+ if (s < F-1)
+ dat->text_buf[s+N] = c; /* if the position is near the end of
+ buffer, extend the buffer to make
+ string comparison easier */
+ s = (s+1) & (N-1);
+ r = (r+1) & (N-1); /* since this is a ring buffer,
+ increment the position modulo N */
+
+ pack_insertnode(r,dat); /* register the string in
+ text_buf[r..r+F-1] */
+ }
+
+ while (i++ < last_match_length) { /* after the end of text, */
+ pack_deletenode(s,dat); /* no need to read, but */
+ s = (s+1) & (N-1); /* buffer may not be empty */
+ r = (r+1) & (N-1);
+ if (--len)
+ pack_insertnode(r,dat);
+ }
+
+ } while (len > 0); /* until length of string to be processed is zero */
+
+ if (code_buf_ptr > 1) { /* send remaining code */
+ if ((file->passpos) && (file->flags & PACKFILE_FLAG_OLD_CRYPT)) {
+ dat->code_buf[0] ^= *file->passpos;
+ file->passpos++;
+ if (!*file->passpos)
+ file->passpos = file->passdata;
+ };
+
+ for (i=0; i<code_buf_ptr; i++) {
+ pack_putc(dat->code_buf[i], file);
+ if (pack_ferror(file)) {
+ ret = EOF;
+ goto getout;
+ }
+ }
+ }
+
+ dat->state = 0;
+
+ getout:
+
+ dat->i = i;
+ dat->c = c;
+ dat->len = len;
+ dat->r = r;
+ dat->s = s;
+ dat->last_match_length = last_match_length;
+ dat->code_buf_ptr = code_buf_ptr;
+ dat->mask = mask;
+
+ return ret;
+}
+
+
+
+/* pack_read:
+ * Called by refill_buffer(). Unpacks from dat into buf, until either
+ * EOF is reached or s bytes have been extracted. Returns the number of
+ * bytes added to the buffer
+ */
+static int pack_read(PACKFILE *file, UNPACK_DATA *dat, int s, unsigned char *buf)
+{
+ int i = dat->i;
+ int j = dat->j;
+ int k = dat->k;
+ int r = dat->r;
+ int c = dat->c;
+ unsigned int flags = dat->flags;
+ int size = 0;
+
+ if (dat->state==2)
+ goto pos2;
+ else
+ if (dat->state==1)
+ goto pos1;
+
+ r = N-F;
+ flags = 0;
+
+ for (;;) {
+ if (((flags >>= 1) & 256) == 0) {
+ if ((c = pack_getc(file)) == EOF)
+ break;
+
+ if ((file->passpos) && (file->flags & PACKFILE_FLAG_OLD_CRYPT)) {
+ c ^= *file->passpos;
+ file->passpos++;
+ if (!*file->passpos)
+ file->passpos = file->passdata;
+ };
+
+ flags = c | 0xFF00; /* uses higher byte to count eight */
+ }
+
+ if (flags & 1) {
+ if ((c = pack_getc(file)) == EOF)
+ break;
+ dat->text_buf[r++] = c;
+ r &= (N - 1);
+ *(buf++) = c;
+ if (++size >= s) {
+ dat->state = 1;
+ goto getout;
+ }
+ pos1:
+ ;
+ }
+ else {
+ if ((i = pack_getc(file)) == EOF)
+ break;
+ if ((j = pack_getc(file)) == EOF)
+ break;
+ i |= ((j & 0xF0) << 4);
+ j = (j & 0x0F) + THRESHOLD;
+ for (k=0; k <= j; k++) {
+ c = dat->text_buf[(i + k) & (N - 1)];
+ dat->text_buf[r++] = c;
+ r &= (N - 1);
+ *(buf++) = c;
+ if (++size >= s) {
+ dat->state = 2;
+ goto getout;
+ }
+ pos2:
+ ;
+ }
+ }
+ }
+
+ dat->state = 0;
+
+ getout:
+
+ dat->i = i;
+ dat->j = j;
+ dat->k = k;
+ dat->r = r;
+ dat->c = c;
+ dat->flags = flags;
+
+ return size;
+}
+
+
+
+
+/* _al_file_isok:
+ * Helper function to check if it is safe to access a file on a floppy
+ * drive. This really only applies to the DOS library, so we don't bother
+ * with it.
+ */
+int _al_file_isok(const char *filename)
+{
+ return TRUE;
+}
+
+
+
+/* _al_file_exists:
+ * Checks whether the specified file exists.
+ */
+int _al_file_exists(const char *filename, int attrib, int *aret)
+{
+ struct _finddata_t info;
+ long handle;
+
+ errno = 0;
+
+ if ((handle = _findfirst(filename, &info)) < 0) {
+ return FALSE;
+ }
+
+ _findclose(handle);
+
+ if (aret)
+ *aret = info.attrib;
+
+ info.attrib &= (FA_HIDDEN | FA_SYSTEM | FA_LABEL | FA_DIREC);
+
+ if ((info.attrib & attrib) != info.attrib)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+
+/* _al_file_size:
+ * Measures the size of the specified file.
+ */
+long _al_file_size(const char *filename)
+{
+ struct _finddata_t info;
+ long handle;
+
+ errno = 0;
+
+ if ((handle = _findfirst(filename, &info)) < 0) {
+ return 0;
+ }
+
+ _findclose(handle);
+
+ if (info.attrib & (FA_SYSTEM | FA_LABEL | FA_DIREC))
+ return 0;
+
+ return info.size;
+}
+
+
+
+/* _al_file_time:
+ * Returns the timestamp of the specified file.
+ */
+time_t _al_file_time(const char *filename)
+{
+ struct _finddata_t info;
+ long handle;
+
+ errno = 0;
+
+ if ((handle = _findfirst(filename, &info)) < 0) {
+ return 0;
+ }
+
+ _findclose(handle);
+
+ if (info.attrib & (FA_SYSTEM | FA_LABEL | FA_DIREC))
+ return 0;
+
+ return info.time_write;
+}
+
+
+
+/* information structure for use by the directory scanning routines */
+typedef struct FFIND_INFO {
+ struct _finddata_t info;
+ long handle;
+ int attrib;
+} FFIND_INFO;
+
+
+
+/* _al_findfirst:
+ * Initiates a directory search.
+ */
+void *_al_findfirst(const char *name, int attrib, char *nameret, int *aret)
+{
+ FFIND_INFO *info;
+ int a;
+
+ info = malloc(sizeof(FFIND_INFO));
+
+ if (!info) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ info->attrib = attrib;
+
+ errno = 0;
+
+ if ((info->handle = _findfirst(name, &info->info)) < 0) {
+ free(info);
+ return NULL;
+ }
+
+ a = info->info.attrib & (FA_HIDDEN | FA_SYSTEM | FA_LABEL | FA_DIREC);
+
+ if ((a & attrib) != a) {
+ if (_al_findnext(info, nameret, aret) != 0) {
+ _findclose(info->handle);
+ free(info);
+ return NULL;
+ }
+ else
+ return info;
+ }
+
+ strcpy(nameret, info->info.name);
+
+ if (aret)
+ *aret = info->info.attrib;
+
+ return info;
+}
+
+
+
+/* _al_findnext:
+ * Retrieves the next file from a directory search.
+ */
+int _al_findnext(void *dta, char *nameret, int *aret)
+{
+ FFIND_INFO *info = (FFIND_INFO *) dta;
+ int a;
+
+ do {
+ if (_findnext(info->handle, &info->info) != 0) {
+ return -1;
+ }
+
+ a = info->info.attrib & (FA_HIDDEN | FA_SYSTEM | FA_LABEL | FA_DIREC);
+
+ } while ((a & info->attrib) != a);
+
+ strcpy(nameret, info->info.name);
+
+ if (aret)
+ *aret = info->info.attrib;
+
+ return 0;
+}
+
+
+
+/* _al_findclose:
+ * Cleans up after a directory search.
+ */
+void _al_findclose(void *dta)
+{
+ FFIND_INFO *info = (FFIND_INFO *) dta;
+
+ _findclose(info->handle);
+ free(info);
+}
+
+
+
+/* _al_getdrive:
+ * Returns the current drive number (0=A, 1=B, etc).
+ */
+int _al_getdrive()
+{
+ return _getdrive() - 1;
+}
+
+
+
+/* _al_getdcwd:
+ * Returns the current directory on the specified drive.
+ */
+void _al_getdcwd(int drive, char *buf, int size)
+{
+ char tmp[256];
+
+ if (_getdcwd(drive+1, tmp, sizeof(tmp)))
+ strcpy(buf, tmp);
+ else
+ buf[0] = 0;
+}
+
diff --git a/dumb/dumb-kode54/winamp/out.h b/dumb/dumb-kode54/winamp/out.h
new file mode 100644
index 00000000..4dd42241
--- /dev/null
+++ b/dumb/dumb-kode54/winamp/out.h
@@ -0,0 +1,71 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * out.h - Winamp plug-in header file. / / \ \
+ * | < / \_
+ * By Bob. | \/ /\ /
+ * \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#define OUT_VER 0x10
+
+typedef struct
+{
+ int version; // module version (OUT_VER)
+ char *description; // description of module, with version string
+ int id; // module id. each input module gets its own. non-nullsoft modules should
+ // be >= 65536.
+
+ HWND hMainWindow; // winamp's main window (filled in by winamp)
+ HINSTANCE hDllInstance; // DLL instance handle (filled in by winamp)
+
+ void (*Config)(HWND hwndParent); // configuration dialog
+ void (*About)(HWND hwndParent); // about dialog
+
+ void (*Init)(); // called when loaded
+ void (*Quit)(); // called when unloaded
+
+ int (*Open)(int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms);
+ // returns >=0 on success, <0 on failure
+ // NOTENOTENOTE: bufferlenms and prebufferms are ignored in most if not all output plug-ins.
+ // ... so don't expect the max latency returned to be what you asked for.
+ // returns max latency in ms (0 for diskwriters, etc)
+ // bufferlenms and prebufferms must be in ms. 0 to use defaults.
+ // prebufferms must be <= bufferlenms
+
+ void (*Close)(); // close the ol' output device.
+
+ int (*Write)(char *buf, int len);
+ // 0 on success. Len == bytes to write (<= 8192 always). buf is straight audio data.
+ // 1 returns not able to write (yet). Non-blocking, always.
+
+ int (*CanWrite)(); // returns number of bytes possible to write at a given time.
+ // Never will decrease unless you call Write (or Close, heh)
+
+ int (*IsPlaying)(); // non0 if output is still going or if data in buffers waiting to be
+ // written (i.e. closing while IsPlaying() returns 1 would truncate the song
+
+ int (*Pause)(int pause); // returns previous pause state
+
+ void (*SetVolume)(int volume); // volume is 0-255
+ void (*SetPan)(int pan); // pan is -128 to 128
+
+ void (*Flush)(int t); // flushes buffers and restarts output at time t (in ms)
+ // (used for seeking)
+
+ int (*GetOutputTime)(); // returns played time in MS
+ int (*GetWrittenTime)(); // returns time written in MS (used for synching up vis stuff)
+
+} Out_Module;
+
+
diff --git a/dumb/dumb-kode54/winamp/resource.h b/dumb/dumb-kode54/winamp/resource.h
new file mode 100644
index 00000000..eee566da
--- /dev/null
+++ b/dumb/dumb-kode54/winamp/resource.h
@@ -0,0 +1,36 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by config.rc
+//
+#define IDD_CONFIG 102
+#define IDC_8BPS 1000
+#define IDC_16BPS 1001
+#define IDC_STEREO 1002
+#define IDC_ALIASING 1003
+#define IDC_LINEAR 1004
+#define IDC_LINEAR_LOW_PASS 1005
+#define IDC_QUADRATIC 1006
+#define IDC_CUBIC 1007
+#define IDC_THREAD_PRI 1008
+#define IDC_11KHZ 1009
+#define IDC_22KHZ 1010
+#define IDC_44KHZ 1011
+#define IDC_48KHZ 1012
+#define IDC_OK 1013
+#define IDC_CANCEL 1014
+#define IDC_BUFFERSIZE 1015
+#define IDC_BUFFERSIZE2 1016
+#define IDC_DEFAULT 1017
+#define IDC_NICEST 1018
+#define IDC_FASTEST 1019
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1020
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/dumb/dumb-kode54/winamp/winamp.dsp b/dumb/dumb-kode54/winamp/winamp.dsp
new file mode 100644
index 00000000..25c82ab8
--- /dev/null
+++ b/dumb/dumb-kode54/winamp/winamp.dsp
@@ -0,0 +1,141 @@
+# Microsoft Developer Studio Project File - Name="winamp" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=winamp - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "winamp.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "winamp.mak" CFG="winamp - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "winamp - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "winamp - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "winamp - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ""
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WINAMP_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /G5 /MD /W3 /O2 /Ob2 /I "../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WINAMP_EXPORTS" /D "WINAMP_PLUGIN" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib dumb.lib /nologo /dll /machine:I386 /out:"in_duh.dll"
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PostBuild_Desc=UPX
+PostBuild_Cmds=g:\tools\upx\upx --best in_duh.dll
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "winamp - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ""
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WINAMP_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../include ." /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WINAMP_EXPORTS" /D "WINAMP_PLUGIN" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib dumb.lib /nologo /dll /profile /debug /machine:I386 /out:"in_duh.dll"
+# SUBTRACT LINK32 /nodefaultlib
+
+!ENDIF
+
+# Begin Target
+
+# Name "winamp - Win32 Release"
+# Name "winamp - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\gui.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\in_duh.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\include\dumb.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gui.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\IN2.H
+# End Source File
+# Begin Source File
+
+SOURCE=.\in_duh.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OUT.H
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\config.rc
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/dumb/dumb-kode54/winamp/winamp.dsw b/dumb/dumb-kode54/winamp/winamp.dsw
new file mode 100644
index 00000000..98a23156
--- /dev/null
+++ b/dumb/dumb-kode54/winamp/winamp.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "winamp"=.\winamp.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+